├── out └── production │ └── G-ClassManager │ ├── META-INF │ ├── G-ClassManager.kotlin_module │ └── MANIFEST.MF │ ├── lib │ ├── gson.jar │ ├── mysql.jar │ └── filters.jar │ └── top │ └── grapedge │ ├── ui │ ├── base │ │ ├── Range.class │ │ ├── GConfig.class │ │ ├── WrapEditorKit.class │ │ ├── WrapLabelView.class │ │ ├── util │ │ │ ├── ColorUtil.class │ │ │ ├── ImageUtil.class │ │ │ ├── AnimationUtil.class │ │ │ └── RendererUtil.class │ │ ├── WrapColumnFactory.class │ │ └── animation │ │ │ ├── base │ │ │ ├── Animator.class │ │ │ ├── Animatable.class │ │ │ ├── ColorAnimator.class │ │ │ ├── AbstractAnimatable.class │ │ │ └── AnimatableListener.class │ │ │ └── color │ │ │ ├── ColorRange.class │ │ │ └── ColorAnimatable.class │ ├── component │ │ ├── GFrame.class │ │ ├── GImage.class │ │ ├── GInput.class │ │ ├── GLabel.class │ │ ├── GPanel.class │ │ ├── GButton$1.class │ │ ├── GButton.class │ │ ├── GCheckBox.class │ │ ├── GImage$1.class │ │ ├── GLabel$1.class │ │ ├── GLabel$2.class │ │ ├── GLowPoly.class │ │ ├── GMenuItem.class │ │ ├── GTextPane.class │ │ ├── GWebView.class │ │ ├── GWindow.class │ │ ├── GChatBubble.class │ │ ├── GContainer.class │ │ ├── GPopupMenu.class │ │ ├── GRoundPane.class │ │ ├── GScrollPane.class │ │ ├── GShadowPane.class │ │ ├── WrapLayout.class │ │ ├── GGroupButton.class │ │ ├── GInputBorder.class │ │ ├── GMessageDialog.class │ │ ├── GPasswordInput.class │ │ ├── GScrollBarUI$1.class │ │ ├── GScrollBarUI.class │ │ ├── GLowPoly$TColor.class │ │ ├── GLowPoly$Vertex.class │ │ └── GLowPoly$Vector2D.class │ └── misc │ │ ├── GLimitFilter.class │ │ └── GNumberFilter.class │ └── gclass │ ├── base │ ├── Debug.class │ ├── GClass.class │ ├── Parser.class │ ├── net │ │ ├── GSocket.class │ │ └── Message.class │ ├── MarkDownParser.class │ └── MarkDownParser$ReplaceListener.class │ ├── client │ ├── ClientMain.class │ ├── base │ │ ├── GPost.class │ │ ├── GPost$1.class │ │ ├── GPost$2.class │ │ ├── GPost$3.class │ │ ├── GPost$4.class │ │ ├── GPost$5.class │ │ ├── GPost$6.class │ │ ├── GPost$7.class │ │ ├── GPost$8.class │ │ ├── GListener.class │ │ ├── MainStatus.class │ │ ├── GChatClient.class │ │ ├── GFileClient.class │ │ ├── GEditorListener.class │ │ └── GDownloadListener.class │ ├── ui │ │ └── ChatBar.class │ └── frame │ │ ├── MainFrame.class │ │ ├── LoginFrame$1.class │ │ ├── LoginFrame$2.class │ │ ├── LoginFrame.class │ │ ├── MainFrame$1.class │ │ ├── MainFrame$2.class │ │ ├── AddClassDialog.class │ │ ├── ClassInfoFrame.class │ │ ├── ContentEditor.class │ │ ├── MarkdownEditor.class │ │ ├── RegisterFrame.class │ │ ├── AddClassDialog$1.class │ │ ├── AddClassDialog$2.class │ │ ├── ClassInfoFrame$1.class │ │ ├── ClassInfoFrame$2.class │ │ ├── ClassInfoFrame$3.class │ │ ├── ContentEditor$1.class │ │ ├── ContentEditor$2.class │ │ ├── ContentEditor$3.class │ │ ├── CreateClassDialog.class │ │ ├── RegisterFrame$1.class │ │ ├── RegisterFrame$2.class │ │ ├── CreateClassDialog$1.class │ │ └── CreateClassDialog$2.class │ └── server │ ├── ServerMain.class │ ├── api │ ├── GFile.class │ ├── GGroup.class │ ├── GUser.class │ └── GMessage.class │ ├── json │ ├── Json.class │ ├── FileJson.class │ ├── UserJson.class │ ├── VoteJson.class │ ├── ClassJson.class │ ├── MemberJson.class │ ├── NoticeJson.class │ └── MessageJson.class │ ├── base │ ├── GServer.class │ ├── ClientHandler.class │ ├── GServerManager.class │ └── MessageHandler.class │ ├── server │ ├── GChatServer.class │ ├── GFileServer.class │ └── GInfoServer.class │ └── handler │ ├── chat │ ├── ChatClientHandler.class │ └── ChatMessageHandler.class │ ├── file │ ├── FileClientHandler.class │ └── FileMessageHandler.class │ └── info │ ├── InfoClientHandler.class │ └── InfoMessageHandler.class ├── images ├── chat.png ├── close.png ├── file.png ├── logo.png ├── minus.png ├── paint.png ├── pen.png ├── plus.png ├── tip.png ├── tools.png ├── avatar.jpg ├── notice.png ├── picture.png ├── splash.png ├── selected.png └── unselected.png ├── src ├── lib │ ├── gson.jar │ ├── mysql.jar │ └── filters.jar ├── META-INF │ └── MANIFEST.MF └── top │ └── grapedge │ ├── ui │ ├── base │ │ ├── animation │ │ │ ├── base │ │ │ │ ├── AnimatableListener.java │ │ │ │ ├── ColorAnimator.java │ │ │ │ ├── Animatable.java │ │ │ │ ├── Animator.java │ │ │ │ └── AbstractAnimatable.java │ │ │ └── color │ │ │ │ ├── ColorRange.java │ │ │ │ └── ColorAnimatable.java │ │ ├── util │ │ │ ├── AnimationUtil.java │ │ │ ├── ColorUtil.java │ │ │ ├── RendererUtil.java │ │ │ └── ImageUtil.java │ │ ├── GConfig.java │ │ ├── Range.java │ │ └── WrapEditorKit.java │ ├── component │ │ ├── GWindow.java │ │ ├── GContainer.java │ │ ├── GPopupMenu.java │ │ ├── GPanel.java │ │ ├── GMenuItem.java │ │ ├── GFrame.java │ │ ├── GScrollPane.java │ │ ├── GRoundPane.java │ │ ├── GWebView.java │ │ ├── GGroupButton.java │ │ ├── GCheckBox.java │ │ ├── GScrollBarUI.java │ │ ├── GMessageDialog.java │ │ ├── GTextPane.java │ │ ├── GShadowPane.java │ │ ├── GImage.java │ │ ├── GPasswordInput.java │ │ ├── GInput.java │ │ ├── GLabel.java │ │ ├── GChatBubble.java │ │ ├── GButton.java │ │ ├── WrapLayout.java │ │ └── GLowPoly.java │ └── misc │ │ ├── GNumberFilter.java │ │ └── GLimitFilter.java │ └── gclass │ ├── client │ ├── base │ │ ├── GListener.java │ │ ├── GEditorListener.java │ │ ├── GDownloadListener.java │ │ ├── MainStatus.java │ │ ├── GChatClient.java │ │ └── GFileClient.java │ ├── frame │ │ ├── MarkdownEditor.java │ │ ├── AddClassDialog.java │ │ └── CreateClassDialog.java │ ├── ClientMain.java │ └── ui │ │ └── ChatBar.java │ ├── server │ ├── base │ │ ├── MessageHandler.java │ │ ├── ClientHandler.java │ │ ├── GServer.java │ │ ├── Database.java │ │ └── GServerManager.java │ ├── json │ │ ├── Json.java │ │ ├── MemberJson.java │ │ ├── MessageJson.java │ │ ├── FileJson.java │ │ ├── UserJson.java │ │ ├── ClassJson.java │ │ ├── NoticeJson.java │ │ └── VoteJson.java │ ├── ServerMain.java │ ├── server │ │ ├── GFileServer.java │ │ ├── GInfoServer.java │ │ └── GChatServer.java │ ├── handler │ │ ├── chat │ │ │ ├── ChatMessageHandler.java │ │ │ └── ChatClientHandler.java │ │ ├── file │ │ │ ├── FileClientHandler.java │ │ │ └── FileMessageHandler.java │ │ └── info │ │ │ ├── InfoClientHandler.java │ │ │ └── InfoMessageHandler.java │ └── api │ │ ├── GFile.java │ │ ├── GUser.java │ │ └── GMessage.java │ └── base │ ├── Debug.java │ ├── net │ ├── Message.java │ └── GSocket.java │ ├── Parser.java │ ├── GClass.java │ └── MarkDownParser.java ├── README.md └── G-ClassManager.iml /out/production/G-ClassManager/META-INF/G-ClassManager.kotlin_module: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /images/chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/chat.png -------------------------------------------------------------------------------- /images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/close.png -------------------------------------------------------------------------------- /images/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/file.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/logo.png -------------------------------------------------------------------------------- /images/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/minus.png -------------------------------------------------------------------------------- /images/paint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/paint.png -------------------------------------------------------------------------------- /images/pen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/pen.png -------------------------------------------------------------------------------- /images/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/plus.png -------------------------------------------------------------------------------- /images/tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/tip.png -------------------------------------------------------------------------------- /images/tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/tools.png -------------------------------------------------------------------------------- /src/lib/gson.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/src/lib/gson.jar -------------------------------------------------------------------------------- /images/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/avatar.jpg -------------------------------------------------------------------------------- /images/notice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/notice.png -------------------------------------------------------------------------------- /images/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/picture.png -------------------------------------------------------------------------------- /images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/splash.png -------------------------------------------------------------------------------- /src/lib/mysql.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/src/lib/mysql.jar -------------------------------------------------------------------------------- /images/selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/selected.png -------------------------------------------------------------------------------- /images/unselected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/images/unselected.png -------------------------------------------------------------------------------- /src/lib/filters.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/src/lib/filters.jar -------------------------------------------------------------------------------- /src/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: top.grapedge.gclass.Main 3 | SplashScreen-Image: splash.png 4 | -------------------------------------------------------------------------------- /out/production/G-ClassManager/lib/gson.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/lib/gson.jar -------------------------------------------------------------------------------- /out/production/G-ClassManager/lib/mysql.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/lib/mysql.jar -------------------------------------------------------------------------------- /out/production/G-ClassManager/lib/filters.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/lib/filters.jar -------------------------------------------------------------------------------- /out/production/G-ClassManager/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: top.grapedge.gclass.Main 3 | SplashScreen-Image: splash.png 4 | -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/Range.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/Range.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/base/Debug.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/base/Debug.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/GConfig.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/GConfig.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/base/GClass.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/base/GClass.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/base/Parser.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/base/Parser.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GFrame.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GFrame.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GImage.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GImage.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GInput.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GInput.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GLabel.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GLabel.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GPanel.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GPanel.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/WrapEditorKit.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/WrapEditorKit.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/WrapLabelView.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/WrapLabelView.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/util/ColorUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/util/ColorUtil.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/util/ImageUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/util/ImageUtil.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GButton$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GButton$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GButton.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GButton.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GCheckBox.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GCheckBox.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GImage$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GImage$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GLabel$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GLabel$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GLabel$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GLabel$2.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GLowPoly.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GLowPoly.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GMenuItem.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GMenuItem.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GTextPane.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GTextPane.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GWebView.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GWebView.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GWindow.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GWindow.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/misc/GLimitFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/misc/GLimitFilter.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/misc/GNumberFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/misc/GNumberFilter.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/base/net/GSocket.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/base/net/GSocket.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/base/net/Message.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/base/net/Message.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/ClientMain.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/ClientMain.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/ui/ChatBar.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/ui/ChatBar.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/ServerMain.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/ServerMain.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/api/GFile.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/api/GFile.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/api/GGroup.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/api/GGroup.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/api/GUser.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/api/GUser.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/json/Json.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/json/Json.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GChatBubble.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GChatBubble.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GContainer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GContainer.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GPopupMenu.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GPopupMenu.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GRoundPane.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GRoundPane.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GScrollPane.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GScrollPane.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GShadowPane.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GShadowPane.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/WrapLayout.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/WrapLayout.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/base/MarkDownParser.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/base/MarkDownParser.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$2.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$3.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$3.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$4.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$4.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$5.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$5.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$6.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$6.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$7.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$7.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$8.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GPost$8.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/api/GMessage.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/api/GMessage.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/base/GServer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/base/GServer.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/json/FileJson.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/json/FileJson.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/json/UserJson.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/json/UserJson.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/json/VoteJson.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/json/VoteJson.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/WrapColumnFactory.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/WrapColumnFactory.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/util/AnimationUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/util/AnimationUtil.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/util/RendererUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/util/RendererUtil.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GGroupButton.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GGroupButton.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GInputBorder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GInputBorder.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GMessageDialog.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GMessageDialog.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GPasswordInput.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GPasswordInput.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GScrollBarUI$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GScrollBarUI$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GScrollBarUI.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GScrollBarUI.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GListener.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/MainStatus.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/MainStatus.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/MainFrame.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/MainFrame.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/json/ClassJson.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/json/ClassJson.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/json/MemberJson.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/json/MemberJson.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/json/NoticeJson.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/json/NoticeJson.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GLowPoly$TColor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GLowPoly$TColor.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GLowPoly$Vertex.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GLowPoly$Vertex.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GChatClient.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GChatClient.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GFileClient.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GFileClient.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/LoginFrame$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/LoginFrame$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/LoginFrame$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/LoginFrame$2.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/LoginFrame.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/LoginFrame.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/MainFrame$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/MainFrame$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/MainFrame$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/MainFrame$2.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/base/ClientHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/base/ClientHandler.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/json/MessageJson.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/json/MessageJson.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/server/GChatServer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/server/GChatServer.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/server/GFileServer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/server/GFileServer.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/server/GInfoServer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/server/GInfoServer.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/animation/base/Animator.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/animation/base/Animator.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/component/GLowPoly$Vector2D.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/component/GLowPoly$Vector2D.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GEditorListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GEditorListener.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/AddClassDialog.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/AddClassDialog.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/ClassInfoFrame.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/ClassInfoFrame.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/ContentEditor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/ContentEditor.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/MarkdownEditor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/MarkdownEditor.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/RegisterFrame.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/RegisterFrame.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/base/GServerManager.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/base/GServerManager.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/base/MessageHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/base/MessageHandler.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/animation/base/Animatable.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/animation/base/Animatable.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/animation/color/ColorRange.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/animation/color/ColorRange.class -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/animation/base/AnimatableListener.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.animation.base; 2 | 3 | public interface AnimatableListener { 4 | void stateChanged(Animatable animator); 5 | } 6 | -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/base/GDownloadListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/base/GDownloadListener.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/AddClassDialog$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/AddClassDialog$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/AddClassDialog$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/AddClassDialog$2.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/ClassInfoFrame$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/ClassInfoFrame$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/ClassInfoFrame$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/ClassInfoFrame$2.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/ClassInfoFrame$3.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/ClassInfoFrame$3.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/ContentEditor$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/ContentEditor$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/ContentEditor$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/ContentEditor$2.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/ContentEditor$3.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/ContentEditor$3.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/CreateClassDialog.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/CreateClassDialog.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/RegisterFrame$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/RegisterFrame$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/RegisterFrame$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/RegisterFrame$2.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/animation/base/ColorAnimator.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/animation/base/ColorAnimator.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/CreateClassDialog$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/CreateClassDialog$1.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/client/frame/CreateClassDialog$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/client/frame/CreateClassDialog$2.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/animation/color/ColorAnimatable.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/animation/color/ColorAnimatable.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/base/MarkDownParser$ReplaceListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/base/MarkDownParser$ReplaceListener.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/animation/base/AbstractAnimatable.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/animation/base/AbstractAnimatable.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/ui/base/animation/base/AnimatableListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/ui/base/animation/base/AnimatableListener.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/handler/chat/ChatClientHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/handler/chat/ChatClientHandler.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/handler/file/FileClientHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/handler/file/FileClientHandler.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/handler/info/InfoClientHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/handler/info/InfoClientHandler.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/handler/chat/ChatMessageHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/handler/chat/ChatMessageHandler.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/handler/file/FileMessageHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/handler/file/FileMessageHandler.class -------------------------------------------------------------------------------- /out/production/G-ClassManager/top/grapedge/gclass/server/handler/info/InfoMessageHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdemJensen/G-ClassManager/HEAD/out/production/G-ClassManager/top/grapedge/gclass/server/handler/info/InfoMessageHandler.class -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/base/GListener.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.base; 2 | 3 | import top.grapedge.gclass.server.json.MessageJson; 4 | 5 | public interface GListener { 6 | void onReceiveMessage(MessageJson message); 7 | } 8 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GWindow.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import javax.swing.*; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: GWindow 8 | * @author: Grapes 9 | * @create: 2019-03-05 18:24 10 | **/ 11 | public class GWindow extends JWindow { 12 | } 13 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/base/GEditorListener.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.base; 2 | 3 | /** 4 | * @program: G-ClassManager 5 | * @description: 6 | * @author: Grapes 7 | * @create: 2019-03-20 19:27 8 | **/ 9 | public interface GEditorListener { 10 | void onEditorConfirm(String text); 11 | } 12 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/util/AnimationUtil.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.util; 2 | 3 | /** 4 | * @program: G-ClassManager 5 | * @description: 6 | * @author: Grapes 7 | * @create: 2019-03-07 13:50 8 | **/ 9 | public class AnimationUtil { 10 | public static void addAnimator() { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/animation/base/ColorAnimator.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.animation.base; 2 | 3 | import java.awt.*; 4 | 5 | public interface ColorAnimator { 6 | Color normalColor = new Color(0x515A5A); 7 | Color hoverColor = new Color(0x616A6B); 8 | Color pressColor = new Color(0x424949); 9 | } 10 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/base/GDownloadListener.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.base; 2 | 3 | /** 4 | * @program: G-ClassManager 5 | * @description: 6 | * @author: Grapes 7 | * @create: 2019-03-18 08:41 8 | **/ 9 | public interface GDownloadListener { 10 | void onReceive(long cur, long length); 11 | } 12 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/frame/MarkdownEditor.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.frame; 2 | 3 | import top.grapedge.ui.component.GTextPane; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: Markdown编辑器 8 | * @author: Grapes 9 | * @create: 2019-03-19 21:43 10 | **/ 11 | public class MarkdownEditor extends GTextPane { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/animation/base/Animatable.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.animation.base; 2 | 3 | import top.grapedge.ui.base.Range; 4 | 5 | import java.time.Duration; 6 | 7 | public interface Animatable { 8 | Range getRange(); 9 | T getValue(); 10 | boolean update(); 11 | void setDuration(Duration duration); 12 | Duration getDuration(); 13 | } 14 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/base/MessageHandler.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.base; 2 | 3 | import top.grapedge.gclass.base.net.Message; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 消息处理器 8 | * @author: Grapes 9 | * @create: 2019-03-14 12:40 10 | **/ 11 | public abstract class MessageHandler { 12 | public abstract Message handleMessage(Message message); 13 | } 14 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/json/Json.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.json; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 8 | * @author: Grapes 9 | * @create: 2019-03-14 14:54 10 | **/ 11 | public class Json { 12 | @Override 13 | public String toString() { 14 | return Parser.toJson(this); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/GConfig.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base; 2 | 3 | import java.awt.*; 4 | 5 | /** 6 | * @program: UIFrame 7 | * @description: UI config 8 | * @author: Grapes 9 | * @create: 2019-03-04 21:46 10 | **/ 11 | public class GConfig { 12 | public static Font font = new Font("微软雅黑", Font.PLAIN, 13); 13 | 14 | public static Font createFont(int size) { 15 | return new Font("微软雅黑", Font.PLAIN, size); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GContainer.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | /** 7 | * @program: G-ClassManager 8 | * @description: 零边距容器 9 | * @author: Grapes 10 | * @create: 2019-03-06 09:11 11 | **/ 12 | public class GContainer extends GPanel { 13 | public GContainer() { 14 | super(); 15 | BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS); 16 | //boxLayout. 17 | setLayout(boxLayout); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/json/MemberJson.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.json; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 8 | * @author: Grapes 9 | * @create: 2019-03-18 10:19 10 | **/ 11 | public class MemberJson extends Json{ 12 | public String userId; 13 | public String classId; 14 | public int type; 15 | 16 | public static MemberJson parse(String json) { 17 | return Parser.fromJson(json, MemberJson.class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于局域网的班级管理系统 2 | 这是SDU软件学院2018级的Java课设。 3 | 本质上是一个mini版QQ,所以我在做的时候界面参考了TIM。 4 | 5 | ## 功能 6 | - 低多边形控件 7 | - 登录功能 8 | - 注册功能 9 | - 文件传输功能 10 | - 1对1聊天、班级聊天(群聊) 11 | - 公告功能 12 | - 投票功能(老师的要求是随机传递,我跟老师讨论了一下改成了实时,投票为同意和不同意两个选项) 13 | - 简单的基于Markdown的编辑器 14 | 15 | ## 截图 16 | 17 | ![登录界面](https://s2.ax1x.com/2019/03/21/A3ncfH.gif) 18 | ![聊天界面](https://s2.ax1x.com/2019/03/21/A3nXXq.png) 19 | ![公告界面](https://s2.ax1x.com/2019/03/21/A3u9NF.png) 20 | ![文件传输](https://s2.ax1x.com/2019/03/21/A3uF39.png) 21 | 22 | ## BUG 23 | 目前有一些已知的小bug,我会在以后的更新中修复。 -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/json/MessageJson.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.json; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 8 | * @author: Grapes 9 | * @create: 2019-03-15 11:45 10 | **/ 11 | public class MessageJson extends Json { 12 | public String sender; 13 | public String receiver; 14 | public String text; 15 | public int msgid; 16 | public long time; 17 | public int type; 18 | 19 | public static MessageJson parse(String json) { 20 | return Parser.fromJson(json, MessageJson.class); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/json/FileJson.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.json; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 文件数据 8 | * @author: Grapes 9 | * @create: 2019-03-14 15:54 10 | **/ 11 | public class FileJson extends Json { 12 | public int fileId = -1; 13 | public String name = ""; 14 | public String path = ""; 15 | public String owner = "-1"; 16 | public long length = 0; 17 | public String classId; 18 | 19 | public static FileJson parse(String json) { 20 | return Parser.fromJson(json, FileJson.class); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/animation/color/ColorRange.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.animation.color; 2 | 3 | import top.grapedge.ui.base.Range; 4 | import top.grapedge.ui.base.util.ColorUtil; 5 | 6 | import java.awt.*; 7 | 8 | /** 9 | * @program: UIFrame 10 | * @description: 颜色过渡 11 | * @author: Grapes 12 | * @create: 2019-03-04 20:19 13 | **/ 14 | public class ColorRange extends Range { 15 | public ColorRange(Color from, Color to) { 16 | super(from, to); 17 | } 18 | 19 | @Override 20 | public Color valueAt(double progress) { 21 | return ColorUtil.blend(getTo(), getFrom(), progress); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GPopupMenu.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | import top.grapedge.ui.base.util.RendererUtil; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | 9 | /** 10 | * @program: G-ClassManager 11 | * @description: 12 | * @author: Grapes 13 | * @create: 2019-03-08 21:44 14 | **/ 15 | public class GPopupMenu extends JPopupMenu { 16 | public GPopupMenu() { 17 | setFont(GConfig.font); 18 | } 19 | 20 | @Override 21 | protected void paintComponent(Graphics g) { 22 | RendererUtil.applyQualityRenderingHints((Graphics2D)g); 23 | super.paintComponent(g); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/misc/GNumberFilter.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.misc; 2 | 3 | import javax.swing.text.AttributeSet; 4 | import javax.swing.text.BadLocationException; 5 | 6 | /** 7 | * @program: G-ClassManager 8 | * @description: 9 | * @author: Grapes 10 | * @create: 2019-03-07 16:16 11 | **/ 12 | public class GNumberFilter extends GLimitFilter { 13 | 14 | public GNumberFilter(int limit) { 15 | super(limit); 16 | } 17 | 18 | @Override 19 | public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { 20 | if (str.charAt(0) >= '0' && str.charAt(0) <= '9') { 21 | super.insertString(offs, str, a); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/json/UserJson.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.json; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 用户信息表 8 | * @author: Grapes 9 | * @create: 2019-03-14 13:42 10 | **/ 11 | public class UserJson extends Json{ 12 | public String userid; 13 | public String password; 14 | public String username; 15 | public int avatar; 16 | public int status; 17 | 18 | public static UserJson parse(String json) { 19 | return Parser.fromJson(json, UserJson.class); 20 | } 21 | 22 | public UserJson() {} 23 | public UserJson(String userid) { 24 | this.userid = userid; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/Range.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base; 2 | 3 | /** 4 | * @program: UIFrame 5 | * @description: Range 6 | * @author: Grapes 7 | * @create: 2019-03-04 19:45 8 | **/ 9 | public abstract class Range { 10 | private T from; 11 | private T to; 12 | 13 | public Range(T from, T to) { 14 | this.from = from; 15 | this.to = to; 16 | } 17 | 18 | public T getFrom() { 19 | return from; 20 | } 21 | 22 | public T getTo() { 23 | return to; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "From" + getFrom() + " To " + getTo(); 29 | } 30 | 31 | public abstract T valueAt(double progress); 32 | } 33 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/animation/color/ColorAnimatable.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.animation.color; 2 | 3 | import top.grapedge.ui.base.Range; 4 | import top.grapedge.ui.base.animation.base.AbstractAnimatable; 5 | import top.grapedge.ui.base.animation.base.AnimatableListener; 6 | 7 | import java.awt.*; 8 | import java.time.Duration; 9 | 10 | /** 11 | * @program: UIFrame 12 | * @description: 颜色动画类 13 | * @author: Grapes 14 | * @create: 2019-03-04 20:24 15 | **/ 16 | public class ColorAnimatable extends AbstractAnimatable { 17 | public ColorAnimatable(Range range, Duration duration, AnimatableListener listener) { 18 | super(range, listener); 19 | setDuration(duration); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/json/ClassJson.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.json; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 8 | * @author: Grapes 9 | * @create: 2019-03-14 23:11 10 | **/ 11 | public class ClassJson extends Json { 12 | public String classid; 13 | public int gradeIndex; 14 | public int classIndex; 15 | public int avatar = -1; 16 | public String intro; 17 | 18 | public static ClassJson parse(String json) { 19 | return Parser.fromJson(json, ClassJson.class); 20 | } 21 | 22 | public ClassJson() {} 23 | 24 | public ClassJson(String classid) { 25 | this.classid = classid; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GPanel.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | import java.awt.event.MouseAdapter; 8 | import java.awt.event.MouseEvent; 9 | 10 | /** 11 | * @program: G-ClassManager 12 | * @description: 13 | * @author: Grapes 14 | * @create: 2019-03-05 18:55 15 | **/ 16 | public class GPanel extends JPanel { 17 | 18 | public GPanel() { 19 | ((FlowLayout)getLayout()).setVgap(0); 20 | ((FlowLayout)getLayout()).setHgap(0); 21 | } 22 | 23 | public GPanel(LayoutManager layoutManager) { 24 | this(); 25 | setLayout(layoutManager); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/ServerMain.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server; 2 | 3 | import top.grapedge.gclass.base.Debug; 4 | import top.grapedge.gclass.server.base.Database; 5 | import top.grapedge.gclass.server.base.GServerManager; 6 | 7 | /** 8 | * @program: G-ClassManager 9 | * @description: 服务器主类 10 | * @author: Grapes 11 | * @create: 2019-03-14 13:20 12 | **/ 13 | public class ServerMain { 14 | public static void main(String[] args) { 15 | try { 16 | Debug.out("服务器已启动"); 17 | var database = new Database(); 18 | var serverManager = new GServerManager(); 19 | } catch (Exception e) { 20 | e.printStackTrace(); 21 | Debug.out("服务器出现错误"); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/json/NoticeJson.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.json; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 8 | * @author: Grapes 9 | * @create: 2019-03-20 19:47 10 | **/ 11 | public class NoticeJson extends Json { 12 | public int noticeId; 13 | public String sender; // 发送者 14 | public String classId; // 班级id 15 | public String text; // 公告内容 16 | public long time; // 公告时间 17 | 18 | public NoticeJson() {} 19 | 20 | public NoticeJson(int noticeId) { 21 | this.noticeId = noticeId; 22 | } 23 | 24 | public static NoticeJson parse(String json) { 25 | return Parser.fromJson(json, NoticeJson.class); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/json/VoteJson.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.json; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 8 | * @author: Grapes 9 | * @create: 2019-03-21 13:49 10 | **/ 11 | public class VoteJson extends Json { 12 | public int voteId; 13 | public int noticeId; 14 | public String classId; 15 | public int agree = 0; 16 | public int disagree = 0; 17 | public int status = 0; // 0 未开始,刚发布的状态, 1 开始了,审议状态, 2 审议结束,投票状态 3 投票结束,公示状态 18 | public long time = 0; 19 | public String member; 20 | 21 | public static VoteJson parse(String json) { 22 | return Parser.fromJson(json, VoteJson.class); 23 | } 24 | // (不进每个投票状态为24小时,为了方便演示这里调整为5分钟。 25 | } 26 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GMenuItem.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | import top.grapedge.ui.base.util.RendererUtil; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | 9 | /** 10 | * @program: G-ClassManager 11 | * @description: 12 | * @author: Grapes 13 | * @create: 2019-03-08 21:51 14 | **/ 15 | public class GMenuItem extends JMenuItem { 16 | public GMenuItem() { 17 | setFont(GConfig.font); 18 | setPreferredSize(new Dimension(100, 30)); 19 | } 20 | 21 | public GMenuItem(String text) { 22 | this(); 23 | setText(text); 24 | } 25 | 26 | @Override 27 | protected void paintComponent(Graphics g) { 28 | RendererUtil.applyQualityRenderingHints((Graphics2D)g); 29 | super.paintComponent(g); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/misc/GLimitFilter.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.misc; 2 | 3 | import javax.swing.text.AttributeSet; 4 | import javax.swing.text.BadLocationException; 5 | import javax.swing.text.PlainDocument; 6 | 7 | /** 8 | * @program: G-ClassManager 9 | * @description: 10 | * @author: Grapes 11 | * @create: 2019-03-07 19:11 12 | **/ 13 | public class GLimitFilter extends PlainDocument { 14 | private int limit; 15 | 16 | public int getLimit() { 17 | return limit; 18 | } 19 | 20 | public GLimitFilter(int limit) { 21 | super(); 22 | this.limit = limit; 23 | } 24 | 25 | @Override 26 | public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { 27 | if (getLength() < getLimit()) 28 | super.insertString(offs, str, a); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /G-ClassManager.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GFrame.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | /** 7 | * @program: G-ClassManager 8 | * @description: 9 | * @author: Grapes 10 | * @create: 2019-03-05 19:26 11 | **/ 12 | public class GFrame extends JFrame { 13 | public GFrame() { 14 | //setLocationRelativeTo(null); 15 | } 16 | 17 | public GFrame(String title) { 18 | super(title); 19 | } 20 | 21 | public GFrame(int width, int height) { 22 | setSize(width, height); 23 | } 24 | 25 | @Override 26 | public void setSize(Dimension d) { 27 | super.setSize(d); 28 | setLocationRelativeTo(null); 29 | } 30 | 31 | public void setSize(Dimension d, boolean center) { 32 | if (center) setSize(d); 33 | else super.setSize(d); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/base/Debug.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.base; 2 | 3 | /** 4 | * @program: G-ClassManager 5 | * @description: 调试信息 6 | * @author: Grapes 7 | * @create: 2019-03-11 13:01 8 | **/ 9 | public class Debug { 10 | public static void log(Object o) { 11 | System.out.println(o); 12 | } 13 | 14 | public static int outLength = 30; 15 | public static void out(Object o) { 16 | String msg = o.toString(); 17 | if (msg.length() <= outLength - 2) { 18 | var t = (outLength - msg.length()) / 2; 19 | for (int i = 0; i < t; i++) System.out.print("="); 20 | System.out.print(msg); 21 | for (int i = 0; i < t; i++) System.out.print("="); 22 | System.out.println(); 23 | } 24 | } 25 | 26 | public static void error(Object o) { 27 | System.out.print("Error: " + o); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/base/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.base; 2 | 3 | import top.grapedge.gclass.base.net.GSocket; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 处理客户端抽象类 8 | * @author: Grapes 9 | * @create: 2019-03-14 12:26 10 | **/ 11 | public abstract class ClientHandler { 12 | protected GServer server; 13 | protected GSocket client; 14 | protected MessageHandler messageHandler = null; 15 | 16 | public ClientHandler(GSocket socket, GServer server) { 17 | this.client = socket; 18 | this.server = server; 19 | } 20 | 21 | public void setMessageHandler(MessageHandler messageHandler) { 22 | this.messageHandler = messageHandler; 23 | } 24 | 25 | public void start() { 26 | update(); 27 | } 28 | 29 | /** 30 | * 更新处理 31 | */ 32 | public abstract void update(); 33 | } 34 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GScrollPane.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.util.RendererUtil; 4 | 5 | import javax.swing.*; 6 | import java.awt.*; 7 | 8 | /** 9 | * @program: G-ClassManager 10 | * @description: 11 | * @author: Grapes 12 | * @create: 2019-03-07 23:50 13 | **/ 14 | public class GScrollPane extends JScrollPane { 15 | public GScrollPane(JComponent component) { 16 | super(component); 17 | initView(); 18 | } 19 | 20 | public GScrollPane() { super(); initView();} 21 | 22 | private void initView() { 23 | setOpaque(false); 24 | getHorizontalScrollBar().setUI(new GScrollBarUI()); 25 | getVerticalScrollBar().setUI(new GScrollBarUI()); 26 | } 27 | 28 | @Override 29 | protected void paintComponent(Graphics g) { 30 | RendererUtil.applyQualityRenderingHints((Graphics2D)g); 31 | super.paintComponent(g); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GRoundPane.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.util.ColorUtil; 4 | import top.grapedge.ui.base.util.RendererUtil; 5 | 6 | import java.awt.*; 7 | import java.awt.geom.RoundRectangle2D; 8 | 9 | /** 10 | * @program: G-ClassManager 11 | * @description: 12 | * @author: Grapes 13 | * @create: 2019-03-06 20:55 14 | **/ 15 | public class GRoundPane extends GPanel { 16 | 17 | private int arc; 18 | 19 | public GRoundPane(int arc) { 20 | this.arc = arc; 21 | setOpaque(false); 22 | setBackground(ColorUtil.TRANSPARENT); 23 | } 24 | 25 | @Override 26 | protected void paintComponent(Graphics g) { 27 | super.paintComponent(g); 28 | Graphics2D g2d = (Graphics2D)g; 29 | RendererUtil.applyQualityRenderingHints(g2d); 30 | g2d.clip(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), arc, arc)); 31 | g2d.fillRect(0, 0, getWidth(), getHeight()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/base/GServer.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.base; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * @program: G-ClassManager 10 | * @description: 服务端基类 11 | * @author: Grapes 12 | * @create: 2019-03-14 12:21 13 | **/ 14 | public abstract class GServer { 15 | protected List clients; 16 | protected ServerSocket serverSocket; 17 | 18 | public GServer(int port) throws IOException { 19 | clients = new ArrayList<>(); 20 | serverSocket = new ServerSocket(port); 21 | } 22 | 23 | /** 24 | * 服务更新 25 | */ 26 | public abstract void update(); 27 | 28 | /** 29 | * 从客户端列表中移除 30 | * @param des 移除的客户端 31 | */ 32 | public void remove(Object des) { 33 | clients.remove(des); 34 | } 35 | 36 | public List getClients() { 37 | return clients; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/server/GFileServer.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.server; 2 | 3 | import top.grapedge.gclass.base.net.GSocket; 4 | import top.grapedge.gclass.server.base.GServer; 5 | import top.grapedge.gclass.server.handler.file.FileClientHandler; 6 | import top.grapedge.gclass.server.handler.file.FileMessageHandler; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * @program: G-ClassManager 12 | * @description: 文件传输服务 13 | * @author: Grapes 14 | * @create: 2019-03-14 15:47 15 | **/ 16 | public class GFileServer extends GServer { 17 | public GFileServer(int port) throws IOException { 18 | super(port); 19 | } 20 | 21 | @Override 22 | public void update() { 23 | while (true) { 24 | try { 25 | var handler = new FileClientHandler(new GSocket(serverSocket.accept()), this, new FileMessageHandler()); 26 | handler.start(); 27 | clients.add(handler); 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/server/GInfoServer.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.server; 2 | 3 | import top.grapedge.gclass.base.net.GSocket; 4 | import top.grapedge.gclass.server.base.GServer; 5 | import top.grapedge.gclass.server.handler.info.InfoClientHandler; 6 | import top.grapedge.gclass.server.handler.info.InfoMessageHandler; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * @program: G-ClassManager 12 | * @description: 用于处理实时聊天消息 13 | * @author: Grapes 14 | * @create: 2019-03-14 12:21 15 | **/ 16 | public class GInfoServer extends GServer { 17 | 18 | public GInfoServer(int port) throws IOException { 19 | super(port); 20 | } 21 | 22 | // 用于更新 23 | @Override 24 | public void update() { 25 | while (true) { 26 | try { 27 | var handler = new InfoClientHandler(new GSocket(serverSocket.accept()), this, new InfoMessageHandler()); 28 | handler.start(); 29 | clients.add(handler); 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/base/net/Message.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.base.net; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 基本通信定义 8 | * @author: Grapes 9 | * @create: 2019-03-11 15:48 10 | **/ 11 | public class Message { 12 | /** 13 | * code:通信代码,用于标识消息类型或者执行结果 14 | */ 15 | public int code; 16 | /** 17 | * token:标识状态的附加值,如果有需要,用于验证身份 18 | */ 19 | public int token; 20 | /** 21 | * props:此条消息附带的结果或者参数 22 | * 格式为JSON 23 | */ 24 | public String props; 25 | 26 | public Message() { 27 | this(0, -1, ""); 28 | } 29 | 30 | public Message(int code, String props) { 31 | this.code = code; 32 | this.props = props; 33 | } 34 | 35 | public Message(int code, int token, String props) { 36 | this(code, props); 37 | this.token = token; 38 | } 39 | 40 | // 转换为Json格式 41 | @Override 42 | public String toString() { 43 | return Parser.toJson(this); 44 | } 45 | 46 | public static Message parse(String json) { 47 | return Parser.fromJson(json, Message.class); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/animation/base/Animator.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.animation.base; 2 | 3 | import top.grapedge.gclass.base.Debug; 4 | 5 | import javax.swing.*; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public enum Animator { 10 | INSTANCE(60); 11 | 12 | private Timer timer; 13 | 14 | private final List animations = new ArrayList<>(); 15 | 16 | Animator(int fps) { 17 | if (fps <= 0) fps = 60; 18 | timer = new Timer(1000 / fps, e-> { 19 | try { 20 | animations.removeIf(Animatable::update); 21 | if (animations.isEmpty()) { 22 | timer.stop(); 23 | } 24 | } catch (Exception e1) { 25 | Debug.log("动画引擎初始化失败,正在重试。"); 26 | timer.restart(); 27 | } 28 | }); 29 | } 30 | 31 | public void add(Animatable anim) { 32 | animations.add(anim); 33 | timer.restart(); 34 | } 35 | 36 | public void remove(Animatable anim) { 37 | animations.remove(anim); 38 | if (animations.isEmpty()) { 39 | timer.stop(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GWebView.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.gclass.base.GClass; 4 | import top.grapedge.ui.base.GConfig; 5 | 6 | import javax.swing.*; 7 | import javax.swing.border.EmptyBorder; 8 | import javax.swing.event.HyperlinkEvent; 9 | import java.awt.*; 10 | 11 | /** 12 | * @program: G-ClassManager 13 | * @description: 14 | * @author: Grapes 15 | * @create: 2019-03-21 08:56 16 | **/ 17 | public class GWebView extends JEditorPane { 18 | public GWebView() { 19 | setContentType("text/html"); 20 | setEditorKit(GClass.getHTMLEditorKit()); 21 | setEditable(false); 22 | setBorder(new EmptyBorder(10, 10, 0, 0)); 23 | putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); 24 | setFont(GConfig.createFont(13)); 25 | addHyperlinkListener(e -> { 26 | if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { 27 | try { 28 | Desktop.getDesktop().browse(e.getURL().toURI()); 29 | } catch (Exception e1) { 30 | e1.printStackTrace(); 31 | } 32 | } 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/handler/chat/ChatMessageHandler.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.handler.chat; 2 | 3 | import top.grapedge.gclass.base.net.Message; 4 | import top.grapedge.gclass.server.base.GServerManager; 5 | import top.grapedge.gclass.server.base.MessageHandler; 6 | import top.grapedge.gclass.server.json.MessageJson; 7 | 8 | /** 9 | * @program: G-ClassManager 10 | * @description: 11 | * @author: Grapes 12 | * @create: 2019-03-17 23:02 13 | **/ 14 | public class ChatMessageHandler extends MessageHandler { 15 | private ChatClientHandler clientHandler; 16 | 17 | public void setClientHandler(ChatClientHandler clientHandler) { 18 | this.clientHandler = clientHandler; 19 | } 20 | 21 | @Override 22 | public Message handleMessage(Message message) { 23 | switch (message.code) { 24 | case 0: 25 | var json = MessageJson.parse(message.props); 26 | clientHandler.sendMessageToServer(json); // 向相应的用户发送提示信息 27 | break; 28 | case 2: 29 | // 注册事件,用于注册用户信息 30 | clientHandler.setUserId(GServerManager.getUserId(message.token)); 31 | break; 32 | } 33 | return null; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/handler/file/FileClientHandler.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.handler.file; 2 | 3 | import top.grapedge.gclass.base.net.GSocket; 4 | import top.grapedge.gclass.base.net.Message; 5 | import top.grapedge.gclass.server.base.ClientHandler; 6 | import top.grapedge.gclass.server.base.GServer; 7 | 8 | /** 9 | * @program: G-ClassManager 10 | * @description: 11 | * @author: Grapes 12 | * @create: 2019-03-14 15:49 13 | **/ 14 | public class FileClientHandler extends ClientHandler { 15 | public FileClientHandler(GSocket socket, GServer server, FileMessageHandler handler) { 16 | super(socket, server); 17 | this.messageHandler = handler; 18 | } 19 | 20 | @Override 21 | public void update() { 22 | new Thread(()-> { 23 | try { 24 | var message = Message.parse(client.readUTF()); 25 | ((FileMessageHandler)messageHandler).setSocket(client); 26 | var result = messageHandler.handleMessage(message); 27 | if (result != null) client.writeUTF(result.toString()); 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | server.remove(this); 31 | } 32 | }).start(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GGroupButton.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.util.RendererUtil; 4 | 5 | import javax.swing.*; 6 | import java.awt.*; 7 | 8 | /** 9 | * @program: G-ClassManager 10 | * @description: 11 | * @author: Grapes 12 | * @create: 2019-03-12 23:13 13 | **/ 14 | public class GGroupButton extends GButton { 15 | private boolean showCount = true; 16 | private int countSize = 14; 17 | private int margin = 12; 18 | 19 | public void setShowCount(boolean showCount) { 20 | this.showCount = showCount; 21 | getParent().repaint(); 22 | } 23 | 24 | public boolean isShowCount() { 25 | return showCount; 26 | } 27 | 28 | public GGroupButton(String text) { 29 | super(text); 30 | } 31 | @Override 32 | protected void paintComponent(Graphics g) { 33 | Graphics2D g2d = (Graphics2D)g; 34 | RendererUtil.applyQualityRenderingHints(g2d, true); 35 | super.paintComponent(g2d); 36 | if (!showCount) 37 | return; 38 | ImageIcon icon = new ImageIcon("images/tip.png"); 39 | icon.setImage(icon.getImage().getScaledInstance(countSize, countSize, Image.SCALE_DEFAULT)); 40 | g.drawImage(icon.getImage(), getWidth() - countSize - margin, getHeight() / 2 - countSize / 2, null); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/base/MainStatus.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.base; 2 | 3 | /** 4 | * @program: G-ClassManager 5 | * @description: 全局状态类 6 | * @author: Grapes 7 | * @create: 2019-03-18 08:17 8 | **/ 9 | public class MainStatus { 10 | private String userId = "-1000"; 11 | private String targetId = "-1000"; 12 | private boolean isClass = false; 13 | 14 | private boolean isAdmin = false; 15 | 16 | public boolean isAdmin() { 17 | return isAdmin; 18 | } 19 | 20 | public void setAdmin(boolean admin) { 21 | isAdmin = admin; 22 | } 23 | 24 | private int token = -10000; 25 | 26 | public void setToken(int token) { 27 | this.token = token; 28 | } 29 | 30 | public int getToken() { 31 | return token; 32 | } 33 | 34 | public void setUserId(String userId) { 35 | this.userId = userId; 36 | } 37 | 38 | public String getUserId() { 39 | return userId; 40 | } 41 | 42 | public void setTargetId(String targetId) { 43 | this.targetId = targetId; 44 | } 45 | 46 | public String getTargetId() { 47 | return targetId; 48 | } 49 | 50 | public boolean isClass() { 51 | return isClass; 52 | } 53 | 54 | public void setClass(boolean aClass) { 55 | isClass = aClass; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/util/ColorUtil.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.util; 2 | 3 | import java.awt.*; 4 | 5 | /** 6 | * @program: UIFrame 7 | * @description: 颜色混合类 8 | * @author: Grapes 9 | * @create: 2019-03-04 20:20 10 | **/ 11 | public class ColorUtil { 12 | // 透明色 13 | public static final Color TRANSPARENT = new Color(0, 0, 0, 0); 14 | public static final Color BACKGROUND = new Color(0xFDFEFE); 15 | // 一个十分简单的颜色过渡功能 16 | public static Color blend(Color from, Color to, double ratio) { 17 | float r = (float) ratio; 18 | float ir = (float) 1.0 - r; 19 | 20 | float red = from.getRed() * r + to.getRed() * ir; 21 | float green = from.getGreen() * r + to.getGreen() * ir; 22 | float blue = from.getBlue() * r + to.getBlue() * ir; 23 | float alpha = from.getAlpha() * r + to.getAlpha() * ir; 24 | 25 | red = Math.min(255f, Math.max(0f, red)); 26 | green = Math.min(255f, Math.max(0f, green)); 27 | blue = Math.min(255f, Math.max(0f, blue)); 28 | alpha = Math.min(255f, Math.max(0f, alpha)); 29 | 30 | Color color = null; 31 | try { 32 | color = new Color((int) red, (int) green, (int) blue, (int) alpha); 33 | } catch (IllegalArgumentException exp) { 34 | exp.printStackTrace(); 35 | } 36 | return color; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GCheckBox.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | import top.grapedge.ui.base.util.ColorUtil; 5 | import top.grapedge.ui.base.util.RendererUtil; 6 | 7 | import javax.swing.*; 8 | import javax.swing.border.EmptyBorder; 9 | import java.awt.*; 10 | 11 | /** 12 | * @program: G-ClassManager 13 | * @description: 14 | * @author: Grapes 15 | * @create: 2019-03-07 12:12 16 | **/ 17 | public class GCheckBox extends JCheckBox { 18 | public GCheckBox() { 19 | } 20 | 21 | public GCheckBox(String text) { 22 | super(text); 23 | setFont(GConfig.font); 24 | setBackground(Color.WHITE); 25 | setOpaque(false); 26 | setFocusPainted(false); 27 | setBorderPaintedFlat(true); 28 | ImageIcon unselected = new ImageIcon("images/unselected.png"); 29 | unselected.setImage(unselected.getImage().getScaledInstance(13, 13, Image.SCALE_DEFAULT )); 30 | ImageIcon selected = new ImageIcon("images/selected.png"); 31 | selected.setImage(selected.getImage().getScaledInstance(13, 13, Image.SCALE_DEFAULT)); 32 | setIcon(unselected); 33 | setSelectedIcon(selected); 34 | } 35 | 36 | @Override 37 | protected void paintComponent(Graphics g) { 38 | Graphics2D g2d = (Graphics2D)g; 39 | RendererUtil.applyQualityRenderingHints(g2d); 40 | super.paintComponent(g2d); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/base/Database.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.base; 2 | 3 | import java.sql.*; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 数据库处理 8 | * @author: Grapes 9 | * @create: 2019-03-14 13:27 10 | **/ 11 | public class Database { 12 | 13 | private static Database instance; 14 | private final String DATABASE = "jdbc:mysql://localhost:3306/g-class-manager?serverTimezone=GMT%2B8"; 15 | private final String USERNAME = "root"; 16 | private final String PASSWORD = "8835230."; 17 | 18 | public static Database getInstance() { 19 | return instance; 20 | } 21 | 22 | private Connection conn; 23 | private Statement stat; 24 | public Database() throws SQLException { 25 | instance = this; 26 | conn = DriverManager.getConnection(DATABASE, USERNAME, PASSWORD); 27 | stat = conn.createStatement(); 28 | } 29 | 30 | public Connection getConn() { 31 | return conn; 32 | } 33 | 34 | public Statement getStat() { 35 | return stat; 36 | } 37 | 38 | public int update(String sql) throws SQLException { 39 | return stat.executeUpdate(sql); 40 | } 41 | 42 | public ResultSet query(String sql) throws SQLException { 43 | return stat.executeQuery(sql); 44 | } 45 | 46 | public PreparedStatement preparedStatement(String sql) throws SQLException { 47 | return conn.prepareStatement(sql); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/base/Parser.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.base; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonSyntaxException; 5 | import com.google.gson.internal.Primitives; 6 | 7 | import java.io.File; 8 | import java.lang.reflect.Type; 9 | import java.math.BigInteger; 10 | import java.nio.charset.StandardCharsets; 11 | import java.security.MessageDigest; 12 | 13 | /** 14 | * @program: G-ClassManager 15 | * @description: 进行各种字符串转换、加密 16 | * @author: Grapes 17 | * @create: 2019-03-14 11:45 18 | **/ 19 | public class Parser { 20 | public static T fromJson(String json, Class classOfT) throws JsonSyntaxException { 21 | return Primitives.wrap(classOfT).cast(new Gson().fromJson(json, classOfT)); 22 | } 23 | 24 | public static T fromJson(String json, Type typeOf) { 25 | return new Gson().fromJson(json, typeOf); 26 | } 27 | 28 | public static String toJson(Object src) { 29 | return new Gson().toJson(src); 30 | } 31 | 32 | public static String md5(String string) { 33 | try { 34 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 35 | md5.update(StandardCharsets.UTF_8.encode(string)); 36 | return String.format("%032x", new BigInteger(1, md5.digest())); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | return null; 40 | } 41 | } 42 | 43 | public static String md5(File file) { 44 | // TODO 45 | return ""; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/handler/info/InfoClientHandler.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.handler.info; 2 | 3 | import top.grapedge.gclass.base.Debug; 4 | import top.grapedge.gclass.base.net.GSocket; 5 | import top.grapedge.gclass.base.net.Message; 6 | import top.grapedge.gclass.server.base.ClientHandler; 7 | import top.grapedge.gclass.server.base.GServer; 8 | import top.grapedge.gclass.server.base.MessageHandler; 9 | 10 | /** 11 | * @program: G-ClassManager 12 | * @description: 用于处理信息 13 | * @author: Grapes 14 | * @create: 2019-03-14 12:33 15 | **/ 16 | public class InfoClientHandler extends ClientHandler { 17 | 18 | public InfoClientHandler(GSocket socket, GServer server, MessageHandler infoHandler) { 19 | super(socket, server); 20 | this.messageHandler = infoHandler; 21 | } 22 | 23 | @Override 24 | public void update() { 25 | new Thread(()-> { 26 | try { 27 | while (true) { 28 | // 获得消息 29 | var request = Message.parse(client.readUTF()); 30 | // 获取执行结果 31 | var result = messageHandler.handleMessage(request); 32 | // 将结果写入 33 | if (result != null) client.writeUTF(result.toString()); 34 | else client.writeUTF(""); 35 | } 36 | } catch (Exception e) { 37 | //e.printStackTrace(); 38 | Debug.log(e.getMessage()); 39 | ((InfoMessageHandler)messageHandler).logout(); 40 | server.remove(this); 41 | } 42 | }).start(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GScrollBarUI.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.util.RendererUtil; 4 | 5 | import javax.swing.*; 6 | import javax.swing.plaf.metal.MetalScrollBarUI; 7 | import java.awt.*; 8 | 9 | /** 10 | * @program: G-ClassManager 11 | * @description: 12 | * @author: Grapes 13 | * @create: 2019-03-08 14:31 14 | **/ 15 | public class GScrollBarUI extends MetalScrollBarUI { 16 | 17 | private Color trackColor = new Color(0xF9F9F9); 18 | private Color thumbColor = new Color(65, 65, 65, 50); 19 | 20 | private JButton b = new JButton() { 21 | @Override 22 | public Dimension getPreferredSize() { 23 | return new Dimension(0, 0); 24 | } 25 | }; 26 | 27 | GScrollBarUI() { 28 | 29 | } 30 | 31 | 32 | @Override 33 | protected void paintThumb(Graphics g, JComponent c, Rectangle r) { 34 | g.setColor(thumbColor); 35 | RendererUtil.applyQualityRenderingHints((Graphics2D)g); 36 | g.fillRoundRect(r.x, r.y, r.width, r.height, 8, 8); 37 | //g.fillRect(r.x, r.y, r.width, r.height); 38 | } 39 | 40 | @Override 41 | protected void paintTrack(Graphics g, JComponent c, Rectangle r) { 42 | g.setColor(trackColor); 43 | RendererUtil.applyQualityRenderingHints((Graphics2D)g); 44 | g.fillRect(r.x, r.y, r.width, r.height); 45 | } 46 | 47 | @Override 48 | protected JButton createDecreaseButton(int orientation) { 49 | return b; 50 | } 51 | 52 | @Override 53 | protected JButton createIncreaseButton(int orientation) { 54 | return b; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/util/RendererUtil.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.util; 2 | 3 | import java.awt.*; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 渲染 8 | * @author: Grapes 9 | * @create: 2019-03-05 18:41 10 | **/ 11 | public class RendererUtil { 12 | public static void applyQualityRenderingHints(Graphics2D g2d, boolean text) { 13 | if (text) g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); 14 | applyQualityRenderingHints(g2d); 15 | } 16 | public static void applyQualityRenderingHints(Graphics2D g2d) { 17 | g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); 18 | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 19 | g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); 20 | g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); 21 | // 22 | g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 23 | g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 24 | g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); 25 | g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 26 | } 27 | 28 | public static GraphicsConfiguration getGraphicsConfiguration() { 29 | return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/base/net/GSocket.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.base.net; 2 | 3 | import java.io.*; 4 | import java.net.Socket; 5 | 6 | /** 7 | * @program: G-ClassManager 8 | * @description: 对Socket进行封装 9 | * @author: Grapes 10 | * @create: 2019-03-10 16:17 11 | **/ 12 | public class GSocket { 13 | private Socket socket; 14 | private DataInputStream reader; 15 | private DataOutputStream writer; 16 | 17 | public GSocket(Socket socket) throws IOException { 18 | this.socket = socket; 19 | reader = new DataInputStream(socket.getInputStream()); 20 | writer = new DataOutputStream(socket.getOutputStream()); 21 | } 22 | public GSocket(String host, int port) throws IOException { 23 | this(new Socket(host, port)); 24 | } 25 | 26 | public String readUTF() throws IOException { 27 | return reader.readUTF(); 28 | } 29 | 30 | public void writeUTF(String line) throws IOException { 31 | writer.writeUTF(line); 32 | writer.flush(); 33 | } 34 | 35 | public void write(byte[] bytes) throws IOException { 36 | writer.write(bytes); 37 | writer.flush(); 38 | } 39 | 40 | public int read(byte[] bytes, int length) throws IOException { 41 | return reader.read(bytes, 0, length); 42 | } 43 | 44 | public void closeReader() throws IOException { 45 | socket.shutdownInput(); 46 | } 47 | 48 | public void closeWriter() throws IOException { 49 | socket.shutdownOutput(); 50 | } 51 | 52 | public void closeALL() throws IOException { 53 | reader.close(); 54 | writer.close(); 55 | socket.close(); 56 | } 57 | 58 | public void close() throws IOException { 59 | socket.close(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/base/GServerManager.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.base; 2 | 3 | import top.grapedge.gclass.base.Debug; 4 | import top.grapedge.gclass.base.GClass; 5 | import top.grapedge.gclass.server.server.GChatServer; 6 | import top.grapedge.gclass.server.server.GFileServer; 7 | import top.grapedge.gclass.server.server.GInfoServer; 8 | 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * @program: G-ClassManager 15 | * @description: 服务器管理器 16 | * @author: Grapes 17 | * @create: 2019-03-14 14:22 18 | **/ 19 | public class GServerManager { 20 | private static Map tokens = new HashMap<>(); 21 | 22 | public GServerManager() throws IOException { 23 | Debug.out("数据库已连接"); 24 | var chatServer = new GChatServer(GClass.CHAT_PORT); 25 | new Thread(chatServer::update).start(); 26 | Debug.out("聊天服务已启动"); 27 | var fileServer = new GFileServer(GClass.FILE_PORT); 28 | new Thread(fileServer::update).start(); 29 | Debug.out("文件服务已启动"); 30 | // 启动信息传输服务 31 | var infoServer = new GInfoServer(GClass.INFO_PORT); 32 | new Thread(infoServer::update).start(); 33 | Debug.out("信息服务已启动"); 34 | } 35 | 36 | public static boolean containsToken(int key) { 37 | return tokens.containsKey(key); 38 | } 39 | 40 | public static String getUserId(int key) { 41 | return tokens.get(key); 42 | } 43 | 44 | /** 45 | * 增加用户至token列表 46 | * @param token 标识 47 | * @param userid 用户id 48 | */ 49 | public static void addToken(int token, String userid) { 50 | tokens.put(token, userid); 51 | } 52 | 53 | public static void removeToken(String key) { 54 | tokens.remove(key); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/WrapEditorKit.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base; 2 | 3 | import javax.swing.text.*; 4 | 5 | /** 6 | * @program: G-ClassManager 7 | * @description: 8 | * @author: Grapes 9 | * @create: 2019-03-20 14:48 10 | **/ 11 | public class WrapEditorKit extends StyledEditorKit { 12 | ViewFactory defaultFactory = new WrapColumnFactory(); 13 | public ViewFactory getViewFactory() { 14 | return defaultFactory; 15 | } 16 | 17 | } 18 | class WrapColumnFactory implements ViewFactory { 19 | public View create(Element elem) { 20 | String kind = elem.getName(); 21 | if (kind != null) { 22 | switch (kind) { 23 | case AbstractDocument.ContentElementName: 24 | return new WrapLabelView(elem); 25 | case AbstractDocument.ParagraphElementName: 26 | return new ParagraphView(elem); 27 | case AbstractDocument.SectionElementName: 28 | return new BoxView(elem, View.Y_AXIS); 29 | case StyleConstants.ComponentElementName: 30 | return new ComponentView(elem); 31 | case StyleConstants.IconElementName: 32 | return new IconView(elem); 33 | } 34 | } 35 | return new LabelView(elem); 36 | } 37 | } 38 | 39 | class WrapLabelView extends LabelView { 40 | WrapLabelView(Element elem) { 41 | super(elem); 42 | } 43 | 44 | public float getMinimumSpan(int axis) { 45 | switch (axis) { 46 | case View.X_AXIS: 47 | return 0; 48 | case View.Y_AXIS: 49 | return super.getMinimumSpan(axis); 50 | default: 51 | throw new IllegalArgumentException("Invalid axis: " + axis); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GMessageDialog.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | import top.grapedge.ui.base.util.ColorUtil; 5 | 6 | import javax.swing.*; 7 | import javax.swing.border.EmptyBorder; 8 | import java.awt.*; 9 | 10 | /** 11 | * @program: G-ClassManager 12 | * @description: 13 | * @author: Grapes 14 | * @create: 2019-03-12 08:53 15 | **/ 16 | public class GMessageDialog extends JDialog { 17 | public GMessageDialog(Frame parent, String msg, String title) { 18 | super(parent, title, true); 19 | initView(msg); 20 | } 21 | 22 | public GMessageDialog(Dialog parent, String msg, String title) { 23 | super(parent, title, true); 24 | initView(msg); 25 | } 26 | 27 | private void initView(String msg) { 28 | setResizable(false); 29 | //setMinimumSize(new Dimension(200, 100)); 30 | GPanel panel = new GPanel(); 31 | panel.setBorder(new EmptyBorder(10, 10, 10, 10)); 32 | panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 33 | panel.setBackground(ColorUtil.BACKGROUND); 34 | GLabel label = new GLabel(msg); 35 | label.setFont(GConfig.createFont(15)); 36 | 37 | panel.add(label); 38 | 39 | GPanel button = new GPanel(); 40 | button.setBackground(ColorUtil.BACKGROUND); 41 | button.setBorder(new EmptyBorder(20, 0, 10, 0)); 42 | GButton confirm = new GButton("确定"); 43 | confirm.setRadius(7); 44 | confirm.setPreferredSize(new Dimension(80, 30)); 45 | confirm.addActionListener(e->dispose()); 46 | button.add(confirm); 47 | 48 | panel.add(button); 49 | 50 | setContentPane(panel); 51 | pack(); 52 | setLocationRelativeTo(null); 53 | setVisible(true); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/animation/base/AbstractAnimatable.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.animation.base; 2 | 3 | import top.grapedge.ui.base.Range; 4 | 5 | import java.time.Duration; 6 | import java.time.LocalDateTime; 7 | 8 | /** 9 | * @program: UIFrame 10 | * @description: 11 | * @author: Grapes 12 | * @create: 2019-03-04 20:03 13 | **/ 14 | public class AbstractAnimatable implements Animatable { 15 | 16 | private Range range; 17 | private LocalDateTime startTime; // 动画开始时间 18 | private Duration duration = Duration.ofSeconds(2); // 动画时长,默认为2s 19 | private T value; // 当前动画值 20 | 21 | private AnimatableListener listener; 22 | 23 | public AbstractAnimatable(Range range, AnimatableListener listener) { 24 | this.range = range; 25 | this.value = range.getFrom(); 26 | this.listener = listener; 27 | } 28 | 29 | @Override 30 | public Range getRange() { 31 | return range; 32 | } 33 | 34 | @Override 35 | public T getValue() { 36 | return value; 37 | } 38 | 39 | public double getProgress() { 40 | startTime = startTime == null ? LocalDateTime.now() : startTime; 41 | return Duration.between(startTime, LocalDateTime.now()).toMillis() / (double)getDuration().toMillis(); 42 | } 43 | 44 | @Override 45 | public boolean update() { 46 | double progress = getProgress(); 47 | //System.out.println(progress); 48 | progress = progress > 1.0 ? 1.0 : progress; 49 | value = getRange().valueAt(progress); 50 | listener.stateChanged(this); 51 | return progress >= 1.0; 52 | } 53 | 54 | @Override 55 | public void setDuration(Duration duration) { 56 | this.duration = duration; 57 | } 58 | 59 | @Override 60 | public Duration getDuration() { 61 | return duration; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/ClientMain.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client; 2 | 3 | import top.grapedge.gclass.client.base.GChatClient; 4 | import top.grapedge.gclass.client.base.GFileClient; 5 | import top.grapedge.gclass.client.base.GPost; 6 | import top.grapedge.gclass.client.frame.LoginFrame; 7 | import top.grapedge.ui.component.GImage; 8 | import top.grapedge.ui.component.GMessageDialog; 9 | import top.grapedge.ui.component.GWindow; 10 | 11 | import javax.swing.*; 12 | import java.awt.*; 13 | import java.io.IOException; 14 | 15 | public class ClientMain { 16 | 17 | public static void main(String[] args) { 18 | try { 19 | new ClientMain(); 20 | } catch (IOException e) { 21 | e.printStackTrace(); 22 | new GMessageDialog((Frame) null, "连接服务器失败", "哎鸭鸭"); 23 | System.exit(0); 24 | } 25 | EventQueue.invokeLater(LoginFrame::new); 26 | } 27 | 28 | private ClientMain() throws IOException { 29 | // 显示闪屏界面 30 | showSplash(); 31 | new GPost(); 32 | new GFileClient(); 33 | new GChatClient(); 34 | try { 35 | UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); 36 | } catch (Exception e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | 41 | 42 | private void showSplash() { 43 | int showMillis = 0; 44 | int width = 400, height = 250; 45 | GWindow splash = new GWindow(); 46 | GImage screen = new GImage(new ImageIcon("images/splash.png")); 47 | screen.setPreferredSize(new Dimension(width, height)); 48 | splash.getContentPane().add( 49 | screen 50 | ); 51 | splash.setSize(new Dimension(width, height)); 52 | splash.setLocationRelativeTo(null); 53 | splash.setVisible(true); 54 | try { 55 | Thread.sleep(showMillis); 56 | } catch (Exception e) { 57 | e.printStackTrace(); 58 | } 59 | 60 | splash.setVisible(false); 61 | splash.dispose(); 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/base/GChatClient.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.base; 2 | 3 | import top.grapedge.gclass.base.GClass; 4 | import top.grapedge.gclass.base.net.GSocket; 5 | import top.grapedge.gclass.base.net.Message; 6 | import top.grapedge.gclass.server.json.MessageJson; 7 | 8 | import java.io.IOException; 9 | import java.net.Socket; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * @program: G-ClassManager 15 | * @description: 16 | * @author: Grapes 17 | * @create: 2019-03-18 10:04 18 | **/ 19 | public class GChatClient { 20 | private static GChatClient instance; 21 | private GSocket socket; 22 | private int token = -12222; 23 | private List msgListener = new ArrayList<>(); 24 | public void addMsgListener(GListener listener) { 25 | msgListener.add(listener); 26 | } 27 | 28 | public static GChatClient getInstance() { 29 | return instance; 30 | } 31 | 32 | private Thread readThread = new Thread(()-> { 33 | try { 34 | while (true) { 35 | var msg = MessageJson.parse(Message.parse(socket.readUTF()).props); 36 | for (var i : msgListener) { 37 | i.onReceiveMessage(msg); 38 | } 39 | } 40 | } catch (Exception e) { 41 | e.printStackTrace(); 42 | } 43 | }); 44 | public GChatClient() throws IOException { 45 | instance = this; 46 | socket = new GSocket(new Socket(GClass.HOST, GClass.CHAT_PORT)); 47 | readThread.start(); 48 | } 49 | 50 | public void register(int token) { 51 | this.token = token; 52 | try { 53 | socket.writeUTF(new Message(2, token, "").toString()); 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | 59 | public void sendMessage(MessageJson messageJson) { 60 | try { 61 | socket.writeUTF(new Message(0, token, messageJson.toString()).toString()); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/handler/chat/ChatClientHandler.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.handler.chat; 2 | 3 | import top.grapedge.gclass.base.Debug; 4 | import top.grapedge.gclass.base.net.GSocket; 5 | import top.grapedge.gclass.base.net.Message; 6 | import top.grapedge.gclass.server.base.ClientHandler; 7 | import top.grapedge.gclass.server.base.GServer; 8 | import top.grapedge.gclass.server.json.MessageJson; 9 | import top.grapedge.gclass.server.server.GChatServer; 10 | 11 | import java.io.IOException; 12 | 13 | /** 14 | * @program: G-ClassManager 15 | * @description: 聊天服务端 16 | * @author: Grapes 17 | * @create: 2019-03-17 23:01 18 | **/ 19 | public class ChatClientHandler extends ClientHandler { 20 | private String userId = "-1000"; 21 | 22 | public String getUserId() { 23 | return userId; 24 | } 25 | 26 | public ChatClientHandler(GSocket socket, GServer server, ChatMessageHandler handler) { 27 | super(socket, server); 28 | this.messageHandler = handler; 29 | ((ChatMessageHandler) this.messageHandler).setClientHandler(this); 30 | } 31 | 32 | @Override 33 | public void update() { 34 | new Thread(()-> { 35 | try { 36 | while (true) { 37 | var message = Message.parse(client.readUTF()); 38 | messageHandler.handleMessage(message); 39 | } 40 | } catch (Exception e) { 41 | Debug.log(e.getMessage()); 42 | ((GChatServer)server).logout(userId); 43 | server.remove(this); 44 | } 45 | }).start(); 46 | } 47 | 48 | public void sendMessageToServer(MessageJson json) { 49 | ((GChatServer)server).sendMessage(json); 50 | } 51 | 52 | public void sendNewMessage(MessageJson message) { 53 | try { 54 | client.writeUTF(new Message(0, 0, message.toString()).toString()); 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | } 58 | } 59 | 60 | public void setUserId(String props) { 61 | userId = props; 62 | ((GChatServer)server).register(userId, this); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GTextPane.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | 5 | import javax.swing.*; 6 | import javax.swing.text.BadLocationException; 7 | import javax.swing.text.StyleConstants; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * @program: G-ClassManager 13 | * @description: 14 | * @author: Grapes 15 | * @create: 2019-03-08 16:22 16 | **/ 17 | public class GTextPane extends JTextPane { 18 | public GTextPane() { 19 | initView(); 20 | } 21 | 22 | private void initView() { 23 | setFont(GConfig.font); 24 | } 25 | 26 | private List images = new ArrayList<>(); 27 | private void parse() { 28 | images.clear(); 29 | var root = getStyledDocument().getRootElements()[0]; 30 | var n = root.getElementCount(); 31 | for (var i = 0; i < n; i++) { 32 | var e = root.getElement(i); 33 | for (var j = 0; j < e.getElementCount(); j++) { 34 | var icon = (ImageIcon) StyleConstants.getIcon(e.getElement(j).getAttributes()); 35 | if (icon != null) { 36 | images.add(icon); 37 | } 38 | } 39 | } 40 | } 41 | 42 | public String getMessage() { 43 | 44 | parse(); 45 | var msg = new StringBuilder(); 46 | var k = 0; 47 | for (var i = 0; i < getStyledDocument().getLength(); i++) { 48 | var ele = getStyledDocument().getCharacterElement(i); 49 | if (ele.getName().equals("icon")) { 50 | msg.append("<").append(images.get(k++)).append(">"); 51 | } else { 52 | try { 53 | var ch = getStyledDocument().getText(i, 1); 54 | // 转义 55 | if (ch.equals("<") || ch.equals(">") || ch.equals("\\")) { 56 | msg.append("\\"); 57 | } 58 | msg.append(ch); 59 | } catch (BadLocationException e1) { 60 | e1.printStackTrace(); 61 | } 62 | } 63 | } 64 | return msg.toString(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GShadowPane.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.util.ImageUtil; 4 | import top.grapedge.ui.base.util.RendererUtil; 5 | 6 | import javax.swing.*; 7 | import javax.swing.border.EmptyBorder; 8 | import java.awt.*; 9 | import java.awt.geom.RoundRectangle2D; 10 | import java.awt.image.BufferedImage; 11 | 12 | /** 13 | * @program: G-ClassManager 14 | * @description: 阴影面板 15 | * @author: Grapes 16 | * @create: 2019-03-06 08:38 17 | **/ 18 | public class GShadowPane extends JPanel { 19 | private BufferedImage shadow; 20 | 21 | private int arc; 22 | 23 | public GShadowPane(int px, int arc) { 24 | this.arc = arc; 25 | setOpaque(false); 26 | setBorder(new EmptyBorder(px, px, px, px)); 27 | FlowLayout layout = new FlowLayout(); 28 | layout.setVgap(0); 29 | layout.setHgap(0); 30 | setLayout(layout); 31 | } 32 | 33 | @Override 34 | public void invalidate() { 35 | shadow = null; 36 | super.invalidate(); 37 | } 38 | 39 | @Override 40 | protected void paintComponent(Graphics g) { 41 | super.paintComponent(g); 42 | Graphics2D g2d = (Graphics2D)g; 43 | RendererUtil.applyQualityRenderingHints(g2d); 44 | Insets insets = getInsets(); 45 | int x = insets.left; 46 | int y = insets.top; 47 | int width = getWidth() - (insets.left + insets.right); 48 | int height = getHeight() - (insets.top + insets.bottom); 49 | 50 | if (shadow == null && width > 0 && height > 0) { 51 | int shadowWidth = Math.min(Math.min(insets.left, insets.right), Math.min(insets.top, insets.bottom)); 52 | shadow = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 53 | Graphics2D _g2d = shadow.createGraphics(); 54 | _g2d.setColor(getBackground()); 55 | _g2d.fillRect(0, 0, width, height); 56 | _g2d.dispose(); 57 | shadow = ImageUtil.generateShadow(shadow, shadowWidth, Color.BLACK, 0.8f); 58 | } 59 | g2d.drawImage(shadow, 0, 0, this); 60 | g2d.setPaint(getBackground()); 61 | g2d.clip(new RoundRectangle2D.Double(x, y, width, height, arc, arc)); 62 | g2d.fillRect(x, y, width, height); 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/api/GFile.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.api; 2 | 3 | import top.grapedge.gclass.base.GClass; 4 | import top.grapedge.gclass.base.net.Message; 5 | import top.grapedge.gclass.server.base.Database; 6 | import top.grapedge.gclass.server.json.FileJson; 7 | 8 | import java.sql.ResultSet; 9 | import java.sql.Statement; 10 | 11 | /** 12 | * @program: G-ClassManager 13 | * @description: 14 | * @author: Grapes 15 | * @create: 2019-03-14 16:12 16 | **/ 17 | public class GFile { 18 | public static Message addFile(FileJson file) { 19 | try { 20 | var sql = "INSERT INTO file (name, path, owner) VALUES (?,?,?);"; 21 | var ps = Database.getInstance().getConn().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 22 | ps.setString(1, file.name); 23 | ps.setString(2, file.path); 24 | ps.setString(3, file.owner); 25 | ps.executeUpdate(); 26 | ResultSet rs = ps.getGeneratedKeys(); 27 | var result = new FileJson(); 28 | result.fileId = -1; 29 | if (rs.next()) { 30 | result.fileId = rs.getInt(1); 31 | } 32 | result.name = file.name; 33 | result.owner = file.owner; 34 | result.path = file.path; 35 | return new Message(0, 0, result.toString()); 36 | } catch (Exception e) { 37 | e.printStackTrace(); 38 | } 39 | return null; 40 | } 41 | 42 | public static Message getFile(FileJson fileJson) { 43 | try { 44 | var sql = "SELECT * FROM file WHERE fileid=?;"; 45 | var ps = Database.getInstance().getConn().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 46 | ps.setInt(1, fileJson.fileId); 47 | var rs = ps.executeQuery(); 48 | var result = new FileJson(); 49 | result.fileId = -1; 50 | result.path = GClass.getServerFileSavePath(); 51 | if (rs.next()) { 52 | result.fileId = rs.getInt("fileid"); 53 | result.owner = rs.getString("owner"); 54 | result.name = rs.getString("name"); 55 | result.path = rs.getString("path"); 56 | } 57 | return new Message(0, 0, result.toString()); 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } 61 | return null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GImage.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.util.ImageUtil; 4 | import top.grapedge.ui.base.util.RendererUtil; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.MouseAdapter; 10 | import java.awt.event.MouseEvent; 11 | 12 | /** 13 | * @program: G-ClassManager 14 | * @description: 显示图片控件 15 | * @author: Grapes 16 | * @create: 2019-03-05 18:18 17 | **/ 18 | public class GImage extends GLabel { 19 | private ImageIcon icon; 20 | 21 | private String command = "img"; 22 | 23 | public void setCommand(String command) { 24 | this.command = command; 25 | } 26 | 27 | public GImage(ImageIcon img) { 28 | this.icon = img; 29 | setIcon(img); 30 | setAlignmentX(Component.CENTER_ALIGNMENT); 31 | setAlignmentY(Component.CENTER_ALIGNMENT); 32 | addMouseListener(new MouseAdapter() { 33 | @Override 34 | public void mouseClicked(MouseEvent e) { 35 | actionListener.actionPerformed(new ActionEvent(GImage.this, ActionEvent.ACTION_PERFORMED, command)); 36 | } 37 | }); 38 | } 39 | 40 | public void setIcon(ImageIcon icon) { 41 | this.icon = icon; 42 | super.setIcon(icon); 43 | } 44 | 45 | public GImage(Image img) { 46 | this(new ImageIcon(img)); 47 | } 48 | 49 | @Override 50 | public void setBounds(int x, int y, int width, int height) { 51 | super.setBounds(x, y, width, height); 52 | setPreferredSize(new Dimension(width, height)); 53 | } 54 | 55 | @Override 56 | public void setSize(Dimension d) { 57 | setPreferredSize(d); 58 | } 59 | 60 | @Override 61 | public void setPreferredSize(Dimension preferredSize) { 62 | super.setPreferredSize(preferredSize); 63 | if (preferredSize.width > 0 && preferredSize.height > 0) icon.setImage(icon.getImage().getScaledInstance(preferredSize.width, preferredSize.height, Image.SCALE_DEFAULT )); 64 | } 65 | 66 | public void setRound() { 67 | this.icon = ImageUtil.getRoundImageIcon(this.icon.getImage()); 68 | setIcon(this.icon); 69 | } 70 | 71 | @Override 72 | protected void paintComponent(Graphics g) { 73 | Graphics2D g2d = (Graphics2D)g; 74 | RendererUtil.applyQualityRenderingHints(g2d); 75 | super.paintComponent(g2d); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GPasswordInput.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | import top.grapedge.ui.base.util.RendererUtil; 5 | 6 | import javax.swing.*; 7 | import javax.swing.text.PlainDocument; 8 | import java.awt.*; 9 | import java.awt.event.FocusEvent; 10 | import java.awt.event.FocusListener; 11 | 12 | /** 13 | * @program: G-ClassManager 14 | * @description: 15 | * @author: Grapes 16 | * @create: 2019-03-07 18:56 17 | **/ 18 | public class GPasswordInput extends JPasswordField implements FocusListener { 19 | private String hintText; 20 | private char echoChar; 21 | public GPasswordInput(int columns) { 22 | super(columns); 23 | echoChar = getEchoChar(); 24 | setFont(GConfig.createFont(14)); 25 | setBorder(new GInputBorder(new Color(192, 192, 192), 1 , true)); 26 | setBorder(BorderFactory.createCompoundBorder(getBorder(), BorderFactory.createEmptyBorder(5, 5, 5, 5))); 27 | addFocusListener(this); 28 | } 29 | 30 | public void setHintText(String hintText) { 31 | this.hintText = hintText; 32 | focusLost(null); 33 | } 34 | 35 | private Color foreground = Color.BLACK; 36 | private Color hintColor = Color.GRAY; 37 | @Override 38 | public void setForeground(Color fg) { 39 | this.foreground = fg; 40 | } 41 | 42 | public void setHintColor(Color hintColor) { 43 | this.hintColor = hintColor; 44 | } 45 | 46 | private boolean isHint = true; 47 | 48 | public boolean isHint() { 49 | return isHint; 50 | } 51 | 52 | private PlainDocument filter = new PlainDocument(); 53 | 54 | public void setFilter(PlainDocument filter) { 55 | this.filter = filter; 56 | } 57 | 58 | @Override 59 | public void focusGained(FocusEvent e) { 60 | if (isHint) { 61 | setEchoChar(echoChar); 62 | isHint = false; 63 | setDocument(filter); 64 | setText(""); 65 | super.setForeground(foreground); 66 | } 67 | } 68 | 69 | 70 | @Override 71 | public void focusLost(FocusEvent e) { 72 | if(getPassword().length <= 0) { 73 | setEchoChar('\0'); 74 | isHint = true; 75 | setDocument(new PlainDocument()); 76 | super.setForeground(hintColor); 77 | setText(hintText); 78 | } 79 | } 80 | 81 | @Override 82 | protected void paintComponent(Graphics g) { 83 | RendererUtil.applyQualityRenderingHints((Graphics2D)g); 84 | super.paintComponent(g); 85 | } 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/ui/ChatBar.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.ui; 2 | 3 | import top.grapedge.ui.base.util.ImageUtil; 4 | import top.grapedge.ui.component.GGroupButton; 5 | import top.grapedge.ui.component.GPanel; 6 | 7 | import javax.swing.*; 8 | import java.awt.*; 9 | import java.awt.event.ActionListener; 10 | 11 | /** 12 | * @program: G-ClassManager 13 | * @description: 用于创建组件 14 | * @author: Grapes 15 | * @create: 2019-03-12 21:14 16 | **/ 17 | public class ChatBar { 18 | private int type = 0; 19 | 20 | public void setType(int type) { 21 | this.type = type; 22 | } 23 | 24 | public int getType() { 25 | return type; 26 | } 27 | 28 | private String id; 29 | 30 | public String getId() { 31 | return id; 32 | } 33 | 34 | private GPanel myGroup; 35 | private GGroupButton myGroupButton; 36 | public void addActionListener(ActionListener listener) { 37 | myGroupButton.addActionListener(listener); 38 | } 39 | 40 | private String title; 41 | public String getTitle() { 42 | return title; 43 | } 44 | public ChatBar(String id, String title, ImageIcon avatar, int type, int asize, int gwidth, int gheight) { 45 | this.id = id; 46 | this.title = title; 47 | myGroup = new GPanel(); 48 | myGroup.setPreferredSize(new Dimension(gwidth, gheight)); 49 | myGroup.setMaximumSize(new Dimension(gwidth, gheight)); 50 | myGroupButton = new GGroupButton(title); 51 | myGroupButton.setHorizontalAlignment(SwingConstants.LEFT); 52 | myGroupButton.setNormalColor(new Color(0xFBFCFC)); 53 | myGroupButton.setHoverColor(new Color(0xFDFEFE)); 54 | myGroupButton.setPressColor(new Color(0xF4F6F7)); 55 | myGroupButton.setTextColor(new Color(0x424949)); 56 | this.type = type; 57 | if (avatar == null) avatar = ImageUtil.getRoundImageIcon(new ImageIcon("images/avatar.jpg").getImage()); 58 | avatar.setImage(avatar.getImage().getScaledInstance(asize, asize, Image.SCALE_DEFAULT)); 59 | avatar = ImageUtil.getRoundImageIcon(avatar.getImage()); 60 | myGroupButton.setIcon(avatar); 61 | myGroupButton.setPreferredSize(new Dimension(gwidth, gheight)); 62 | myGroup.add(myGroupButton); 63 | } 64 | 65 | public GPanel getPane() { 66 | return myGroup; 67 | } 68 | 69 | public GGroupButton getArea() { 70 | return myGroupButton; 71 | } 72 | 73 | public void showCount(boolean show) { 74 | myGroupButton.setShowCount(show); 75 | } 76 | public boolean isShowCount() { 77 | return myGroupButton.isShowCount(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/server/GChatServer.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.server; 2 | 3 | import top.grapedge.gclass.base.net.GSocket; 4 | import top.grapedge.gclass.server.api.GGroup; 5 | import top.grapedge.gclass.server.api.GMessage; 6 | import top.grapedge.gclass.server.base.GServer; 7 | import top.grapedge.gclass.server.handler.chat.ChatClientHandler; 8 | import top.grapedge.gclass.server.handler.chat.ChatMessageHandler; 9 | import top.grapedge.gclass.server.json.MemberJson; 10 | import top.grapedge.gclass.server.json.MessageJson; 11 | 12 | import java.io.IOException; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * @program: G-ClassManager 19 | * @description: 20 | * @author: Grapes 21 | * @create: 2019-03-17 23:24 22 | **/ 23 | public class GChatServer extends GServer { 24 | private Map chatMap = new HashMap<>(); 25 | private static GChatServer instance; 26 | 27 | public static GChatServer getInstance() { 28 | return instance; 29 | } 30 | 31 | public GChatServer(int port) throws IOException { 32 | super(port); 33 | instance = this; 34 | } 35 | 36 | @Override 37 | public void update() { 38 | while (true) { 39 | try { 40 | var handler = new ChatClientHandler(new GSocket(serverSocket.accept()), this, new ChatMessageHandler()); 41 | handler.start(); 42 | clients.add(handler); 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | } 46 | } 47 | } 48 | 49 | public void register(String userId, ChatClientHandler handler) { 50 | logout(userId); // 确保用户注销 51 | chatMap.put(userId, handler); 52 | } 53 | 54 | private void sendMessageToClient(String userId, MessageJson message) { 55 | // 如果用户在线 56 | if (chatMap.containsKey(userId)) { 57 | // 发送消息提醒 58 | var receiver = chatMap.get(userId); 59 | if (receiver == null) return; 60 | receiver.sendNewMessage(message); 61 | } else if (message.type == 0) { // 确保是普通消息,群组消息此时已经在数据库中. 62 | // 用户离线,将消息添加至数据库 63 | GMessage.addMessage(message); 64 | } 65 | } 66 | 67 | public void sendMessage(MessageJson message) { 68 | if (message.type == 0) { 69 | sendMessageToClient(message.sender, message); 70 | sendMessageToClient(message.receiver, message); 71 | } else { 72 | GMessage.addMessage(message); // 将群组消息添加到数据库 73 | List members = GGroup.getMembers(message.receiver); 74 | for (var i : members) { 75 | sendMessageToClient(i.userId, message); 76 | } 77 | } 78 | } 79 | 80 | public void logout(String userId) { 81 | chatMap.remove(userId); 82 | } 83 | } -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/handler/file/FileMessageHandler.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.handler.file; 2 | 3 | import top.grapedge.gclass.base.GClass; 4 | import top.grapedge.gclass.base.net.GSocket; 5 | import top.grapedge.gclass.base.net.Message; 6 | import top.grapedge.gclass.server.api.GFile; 7 | import top.grapedge.gclass.server.base.MessageHandler; 8 | import top.grapedge.gclass.server.json.FileJson; 9 | 10 | import java.io.File; 11 | import java.io.FileInputStream; 12 | import java.io.FileOutputStream; 13 | import java.util.Objects; 14 | 15 | /** 16 | * @program: G-ClassManager 17 | * @description: 18 | * @author: Grapes 19 | * @create: 2019-03-14 16:03 20 | **/ 21 | public class FileMessageHandler extends MessageHandler { 22 | private GSocket socket; 23 | 24 | public void setSocket(GSocket socket) { 25 | this.socket = socket; 26 | } 27 | 28 | @Override 29 | public Message handleMessage(Message message) { 30 | if (socket == null) { 31 | return new Message(-128, 0, "出现玄学错误"); 32 | } 33 | if (message.code == 0) { 34 | return saveFile(FileJson.parse(message.props)); 35 | } else if (message.code == 1) { 36 | return sendFile(FileJson.parse(message.props)); 37 | } 38 | return null; 39 | } 40 | 41 | public Message sendFile(FileJson info) { 42 | try { 43 | var gf = GFile.getFile(info); 44 | var file = FileJson.parse(gf.props); 45 | var fs = new File(file.path + "\\" + file.fileId); 46 | var fin = new FileInputStream(fs); 47 | file.length = fs.length(); 48 | gf.props = file.toString(); 49 | socket.writeUTF(gf.toString()); 50 | var bytes = new byte[1024]; 51 | var length = fin.read(bytes, 0, bytes.length); 52 | while (length != -1) { 53 | socket.write(bytes); 54 | length = fin.read(bytes, 0, length); 55 | } 56 | fin.close(); 57 | socket.closeWriter(); 58 | return null; 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | } 62 | return null; 63 | } 64 | 65 | public Message saveFile(FileJson info) { 66 | 67 | var path = GClass.getServerFileSavePath(); 68 | var file = new File(path); 69 | if (!file.exists()) { 70 | file.mkdirs(); 71 | } 72 | info.path = path; 73 | var upload = FileJson.parse(Objects.requireNonNull(GFile.addFile(info)).props); 74 | try { 75 | var fout = new FileOutputStream(new File(path + "\\" + upload.fileId)); 76 | var bytes = new byte[1024]; 77 | var length = socket.read(bytes, bytes.length); 78 | while (length != -1) { 79 | fout.write(bytes, 0, bytes.length); 80 | length = socket.read(bytes, bytes.length); 81 | } 82 | fout.close(); 83 | } catch (Exception e) { 84 | e.printStackTrace(); 85 | } 86 | 87 | return new Message(upload.fileId, 0,upload.toString()); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GInput.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | import top.grapedge.ui.base.util.RendererUtil; 5 | 6 | import javax.swing.*; 7 | import javax.swing.border.LineBorder; 8 | import javax.swing.text.PlainDocument; 9 | import java.awt.*; 10 | import java.awt.event.FocusEvent; 11 | import java.awt.event.FocusListener; 12 | 13 | /** 14 | * @program: G-ClassManager 15 | * @description: 输入框 16 | * @author: Grapes 17 | * @create: 2019-03-06 09:43 18 | **/ 19 | public class GInput extends JTextField implements FocusListener { 20 | private String hintText; 21 | public GInput(int columns) { 22 | super(columns); 23 | setFont(GConfig.createFont(15)); 24 | setBorder(new GInputBorder(new Color(192, 192, 192), 1 , true)); 25 | setBorder(BorderFactory.createCompoundBorder(getBorder(), BorderFactory.createEmptyBorder(5, 5, 5, 5))); 26 | addFocusListener(this); 27 | } 28 | 29 | public void setHintText(String hintText) { 30 | this.hintText = hintText; 31 | focusLost(null); 32 | } 33 | 34 | private Color foreground = Color.BLACK; 35 | private Color hintColor = Color.GRAY; 36 | @Override 37 | public void setForeground(Color fg) { 38 | this.foreground = fg; 39 | } 40 | 41 | public void setHintColor(Color hintColor) { 42 | this.hintColor = hintColor; 43 | } 44 | 45 | private boolean isHint = true; 46 | 47 | public boolean isHint() { 48 | return isHint; 49 | } 50 | 51 | private PlainDocument filter = new PlainDocument(); 52 | 53 | public void setFilter(PlainDocument filter) { 54 | this.filter = filter; 55 | } 56 | 57 | @Override 58 | public void focusGained(FocusEvent e) { 59 | if (isHint) { 60 | isHint = false; 61 | setDocument(filter); 62 | setText(""); 63 | super.setForeground(foreground); 64 | } 65 | } 66 | 67 | @Override 68 | protected void paintComponent(Graphics g) { 69 | RendererUtil.applyQualityRenderingHints((Graphics2D)g); 70 | super.paintComponent(g); 71 | } 72 | 73 | @Override 74 | public void focusLost(FocusEvent e) { 75 | if(getText().length() == 0) { 76 | isHint = true; 77 | setDocument(new PlainDocument()); 78 | super.setForeground(hintColor); 79 | setText(hintText); 80 | } 81 | 82 | } 83 | 84 | 85 | } 86 | 87 | class GInputBorder extends LineBorder { 88 | 89 | public GInputBorder(Color color, int thickness, boolean roundedCorners) { 90 | super(color, thickness, roundedCorners); 91 | } 92 | 93 | public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { 94 | 95 | RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, 96 | RenderingHints.VALUE_ANTIALIAS_ON); 97 | Color oldColor = g.getColor(); 98 | Graphics2D g2 = (Graphics2D)g; 99 | int i; 100 | g2.setRenderingHints(rh); 101 | g2.setColor(lineColor); 102 | for(i = 0; i < thickness; i++) { 103 | if(!roundedCorners) 104 | g2.drawRect(x+i, y+i, width-i-i-1, height-i-i-1); 105 | else 106 | g2.drawRoundRect(x+i, y+i, width-i-i-1, height-i-i-1, 5, 5); 107 | } 108 | g2.setColor(oldColor); 109 | } 110 | } -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GLabel.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | import top.grapedge.ui.base.animation.base.Animator; 5 | import top.grapedge.ui.base.animation.color.ColorAnimatable; 6 | import top.grapedge.ui.base.animation.color.ColorRange; 7 | import top.grapedge.ui.base.util.RendererUtil; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.ActionListener; 13 | import java.awt.event.MouseAdapter; 14 | import java.awt.event.MouseEvent; 15 | import java.time.Duration; 16 | 17 | /** 18 | * @program: G-ClassManager 19 | * @description: 标签控件 20 | * @author: Grapes 21 | * @create: 2019-03-05 18:22 22 | **/ 23 | public class GLabel extends JLabel { 24 | private Duration animationTime = Duration.ofMillis(200); 25 | private ColorAnimatable transitionAnimatable; 26 | 27 | private Color textColor = Color.WHITE; 28 | private Color normalColor = new Color(0x515A5A); 29 | private Color hoverColor = new Color(0x616A6B); 30 | private Color pressColor = new Color(0x424949); 31 | 32 | private boolean isPressed = false; 33 | private boolean isRollover = false; 34 | 35 | protected ActionListener actionListener = new ActionListener() { 36 | @Override 37 | public void actionPerformed(ActionEvent e) { 38 | 39 | } 40 | }; 41 | 42 | public void addActionListener(ActionListener actionListener) { 43 | this.actionListener = actionListener; 44 | } 45 | 46 | public GLabel() {} 47 | public GLabel(String text) { 48 | super(text); 49 | setFont(GConfig.font); 50 | 51 | addMouseListener(new MouseAdapter() { 52 | @Override 53 | public void mouseEntered(MouseEvent e) { 54 | isRollover = true; 55 | setTextColor(); 56 | } 57 | 58 | @Override 59 | public void mouseExited(MouseEvent e) { 60 | isRollover = false; 61 | setTextColor(); 62 | } 63 | 64 | @Override 65 | public void mousePressed(MouseEvent e) { 66 | isPressed = true; 67 | setTextColor(); 68 | } 69 | 70 | @Override 71 | public void mouseReleased(MouseEvent e) { 72 | isPressed = false; 73 | setTextColor(); 74 | } 75 | 76 | @Override 77 | public void mouseClicked(MouseEvent e) { 78 | actionListener.actionPerformed(new ActionEvent(GLabel.this, ActionEvent.ACTION_PERFORMED, GLabel.this.getText())); 79 | } 80 | }); 81 | 82 | } 83 | 84 | private void setTextColor() { 85 | Color targetColor; 86 | if (isPressed) { 87 | targetColor = pressColor; 88 | } else if (isRollover) { 89 | targetColor = hoverColor; 90 | } else { 91 | targetColor = normalColor; 92 | } 93 | setColorTransitionAnimatable(targetColor); 94 | } 95 | 96 | protected void setColorTransitionAnimatable(Color targetColor) { 97 | double progress = stopAnimation(); 98 | transitionAnimatable = new ColorAnimatable( 99 | new ColorRange(getForeground(), targetColor), 100 | preferredAnimationTime(progress), 101 | animator -> setForeground((Color) animator.getValue()) 102 | ); 103 | Animator.INSTANCE.add(transitionAnimatable); 104 | } 105 | 106 | protected double stopAnimation() { 107 | if (transitionAnimatable != null) { 108 | Animator.INSTANCE.remove(transitionAnimatable); 109 | return transitionAnimatable.getProgress(); 110 | } 111 | return 0.0; 112 | } 113 | 114 | protected Duration preferredAnimationTime(double currentProgress) { 115 | if (currentProgress > 0.0 && currentProgress < 1.0) { 116 | double remainingProgress = 1.0 - currentProgress; 117 | double runningTime = animationTime.toMillis() * remainingProgress; 118 | return Duration.ofMillis((long)runningTime); 119 | } 120 | 121 | return animationTime; 122 | } 123 | 124 | @Override 125 | protected void paintComponent(Graphics g) { 126 | RendererUtil.applyQualityRenderingHints((Graphics2D)g, false); 127 | super.paintComponent(g); 128 | } 129 | 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/base/GFileClient.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.base; 2 | 3 | import top.grapedge.gclass.base.Debug; 4 | import top.grapedge.gclass.base.GClass; 5 | import top.grapedge.gclass.base.net.GSocket; 6 | import top.grapedge.gclass.base.net.Message; 7 | import top.grapedge.gclass.server.json.FileJson; 8 | 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.FileOutputStream; 12 | import java.io.IOException; 13 | import java.net.Socket; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * @program: G-ClassManager 19 | * @description: 20 | * @author: Grapes 21 | * @create: 2019-03-14 16:33 22 | **/ 23 | public class GFileClient { 24 | private static GFileClient instance; 25 | 26 | public static GFileClient getInstance() { 27 | return instance; 28 | } 29 | 30 | public GFileClient() throws IOException { 31 | instance = this; 32 | } 33 | 34 | /** 35 | * 上传文件 36 | * @param fileJson 文件信息 37 | * @return 上传结果 38 | * @throws IOException 39 | */ 40 | public synchronized Message upload(FileJson fileJson) { 41 | try { 42 | var socket = new GSocket(new Socket(GClass.HOST, GClass.FILE_PORT)); 43 | var file = new File(fileJson.path); 44 | if (!file.exists()) { 45 | return null; 46 | } 47 | fileJson.name = file.getName(); 48 | fileJson.length = file.length(); 49 | if (fileJson.owner == null) fileJson.owner = "-1"; 50 | socket.writeUTF(new Message(0, 0, fileJson.toString()).toString()); 51 | 52 | var fin = new FileInputStream(file); 53 | var bytes = new byte[1024]; 54 | var length = fin.read(bytes, 0, bytes.length); 55 | while (length != -1) { 56 | socket.write(bytes); 57 | length = fin.read(bytes, 0, bytes.length); 58 | } 59 | socket.closeWriter(); 60 | var result = Message.parse(socket.readUTF()); 61 | socket.closeALL(); 62 | return result; 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | return null; 66 | } 67 | } 68 | 69 | /** 70 | * 下载文件 71 | * @param fileid 文件id 72 | * @param path 要存储到哪个文件夹 73 | * @return 74 | */ 75 | public synchronized Message download(int fileid, String path) { 76 | return download(fileid, path, false); 77 | } 78 | 79 | private List downloadListeners = new ArrayList<>(); 80 | 81 | public void addDownloadListener(GDownloadListener listener, boolean clearDownloadListener) { 82 | if (clearDownloadListener) downloadListeners.clear(); 83 | downloadListeners.add(listener); 84 | } 85 | 86 | public synchronized Message download(int fileid, String path, boolean nameById) { 87 | try { 88 | var fileJson = new FileJson(); 89 | fileJson.fileId = fileid; 90 | var socket = new GSocket(new Socket(GClass.HOST, GClass.FILE_PORT)); 91 | socket.writeUTF(new Message(1, 0, fileJson.toString()).toString()); 92 | var msg = Message.parse(socket.readUTF()); 93 | if (msg.code < 0) { 94 | socket.closeALL(); 95 | return null; 96 | } 97 | 98 | createDirs(path); 99 | 100 | var file = FileJson.parse(msg.props); 101 | path += "\\" + (nameById ? file.fileId : file.name); 102 | 103 | var fout = new FileOutputStream(new File(path)); 104 | var bytes = new byte[1024]; 105 | var length = socket.read(bytes, bytes.length); 106 | var curProgress = (long)length; 107 | while (length != -1) { 108 | fout.write(bytes, 0, bytes.length); 109 | length = socket.read(bytes, bytes.length); 110 | for (var i : downloadListeners) { 111 | i.onReceive(curProgress, file.length); 112 | } 113 | curProgress += length; 114 | } 115 | for (var i : downloadListeners) { 116 | i.onReceive(file.length, file.length); 117 | } 118 | fout.close(); 119 | socket.closeALL(); 120 | return new Message(0, 0, path); 121 | } catch (Exception e) { 122 | e.printStackTrace(); 123 | return null; 124 | } 125 | } 126 | 127 | public static boolean createDirs(String path) { 128 | return new File(path).mkdirs(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/base/GClass.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.base; 2 | 3 | import javax.swing.text.html.HTMLEditorKit; 4 | import java.util.Calendar; 5 | 6 | /** 7 | * @program: G-ClassManager 8 | * @description: 全局变量 9 | * @author: Grapes 10 | * @create: 2019-03-11 16:36 11 | **/ 12 | public class GClass { 13 | 14 | public static final String HOST = "127.0.0.1"; // 主机地址 15 | public static final int CHAT_PORT = 2333; // 实时聊天端口号 16 | public static final int FILE_PORT = 2334; // 文件传输端口号 17 | public static final int INFO_PORT = 2335; // 信息获取端口号 18 | 19 | // 消息定义,下面这些数字随便取的,不重复就行 20 | public static final int LOGIN = 0; // 登录 21 | public static final int REGISTER = 1; // 注册 22 | public static final int USER_INFO = 2; // 用户信息 23 | public static final int CLASS_INFO = 3; // 班级信息 24 | public static final int CHAT_MSG = 4; // 获取用户聊天内容 25 | public static final int FILE_INFO = 5; // 获取文件信息 26 | public static final int VOTE_INFO = 6; // 获取投票信息 27 | public static final int NOTICE_INFO = 7; // 获取公告信息 28 | public static final int VOTE = 8; // 进行投票操作 29 | public static final int MY_CLASS = 9; // 我的班级 30 | public static final int MY_CHAT = 10; // 我的班级 31 | public static final int MEMBERS = 11; // 获取成员列表 32 | public static final int CREATE_CLASS = 12; // 创建班级 33 | public static final int ADD_CLASS = 13; // 添加班级 34 | public static final int SET_ADMIN = 14; // 设置管理员 35 | public static final int ADD_NOTICE = 15; // 发布公告 36 | public static final int UPDATE_NOTICE = 16; // 修改公告内容,用于投票的那个确认文稿 37 | public static final int NOTICE_LIST = 17; // 获取公告信息 38 | public static final int ADD_VOTE = 18; // 发布投票 39 | public static final int VOTE_LIST = 19; // 获取投票列表 40 | public static final int SET_VOTE = 20; // 开始投票 41 | public static final int ADD_ADVICE = 21; // 添加建议 42 | public static final int ADVICE_LIST = 22; // 建议列表 43 | public static final int FILE_LIST = 23; // 文件列表 44 | public static final int ADD_FILE = 24; // 添加文件 45 | 46 | /** 47 | * 缓存文件夹路径 48 | * @return 49 | */ 50 | private static String getCachePath() { 51 | return System.getProperty("user.home") + "\\gclass"; 52 | } 53 | 54 | /** 55 | * 将文件图片缓存至此文件夹 56 | * @return 57 | */ 58 | public static String getFileCachePath() { 59 | return getCachePath() + "\\cache"; 60 | } 61 | 62 | public static String getFileCachePath(String fileId) { 63 | return getFileCachePath() + "\\" + fileId; 64 | } 65 | 66 | /** 67 | * 得到聊天记录保存文件夹 68 | * @return 69 | */ 70 | public static String getRecordPath() { 71 | return getCachePath() + "\\record"; 72 | } 73 | 74 | /** 75 | * 得到聊天记录文件路径 76 | * @param id 用户id或者班级id 77 | * @return 78 | */ 79 | public static String getRecordPath(String id) { 80 | return getRecordPath() + "\\" + id; 81 | } 82 | 83 | public static String getRecordPath(String userid, String sender) { 84 | return getRecordPath(userid) + "\\" + sender; 85 | } 86 | 87 | public static String getServerFileSavePath() { 88 | var cal = Calendar.getInstance(); 89 | var year = cal.get(Calendar.YEAR); 90 | var month = cal.get(Calendar.MONTH); 91 | var date = cal.get(Calendar.DATE); 92 | return "E:\\gclass\\" + year + "\\" + (month + 1) + "\\" + date; 93 | } 94 | 95 | public static String getFileReceivePath() { 96 | return getCachePath() + "\\files"; 97 | } 98 | 99 | public static String getImageReceivePath() { 100 | return getCachePath() + "\\images"; 101 | } 102 | 103 | public static HTMLEditorKit getHTMLEditorKit() { 104 | var kit = new HTMLEditorKit(); 105 | var style = kit.getStyleSheet(); 106 | style.addRule("blockquote {\n" + 107 | " background: #f9f9f9;\n" + 108 | " border-left: 10px solid #cccccc;\n" + 109 | " margin: 10px 10px;\n" + 110 | " padding: 8px 10px;\n" + 111 | "}"); 112 | style.addRule("a {color: #333333;}"); 113 | style.addRule("code {\n" + 114 | " overflow-wrap: break-word;\n" + 115 | " word-wrap: break-word;\n" + 116 | " padding: 2px 4px;\n" + 117 | " color: #555555;\n" + 118 | " background: #eeeeee;\n" + 119 | " border-radius: 3px;\n" + 120 | "}"); 121 | return kit; 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/handler/info/InfoMessageHandler.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.handler.info; 2 | 3 | import top.grapedge.gclass.base.GClass; 4 | import top.grapedge.gclass.base.Parser; 5 | import top.grapedge.gclass.base.net.Message; 6 | import top.grapedge.gclass.server.api.GGroup; 7 | import top.grapedge.gclass.server.api.GMessage; 8 | import top.grapedge.gclass.server.api.GUser; 9 | import top.grapedge.gclass.server.base.MessageHandler; 10 | import top.grapedge.gclass.server.json.*; 11 | 12 | /** 13 | * @program: G-ClassManager 14 | * @description: 处理信息获取 15 | * @author: Grapes 16 | * @create: 2019-03-14 12:51 17 | **/ 18 | public class InfoMessageHandler extends MessageHandler { 19 | 20 | private int token = Integer.MIN_VALUE; 21 | 22 | public void logout() { 23 | GUser.logout(token); 24 | } 25 | @Override 26 | public Message handleMessage(Message message) { 27 | Message result = null; 28 | switch (message.code) { 29 | // 登录 30 | case GClass.LOGIN: 31 | result = GUser.login(UserJson.parse(message.props)); 32 | token = result.token; 33 | break; 34 | // 注册 35 | case GClass.REGISTER: 36 | result = GUser.register(UserJson.parse(message.props)); 37 | break; 38 | // 获取用户信息 39 | case GClass.USER_INFO: 40 | var user = GUser.getUserInfo(UserJson.parse(message.props).userid); 41 | result = new Message(0, 0, user == null ? "" : user.toString()); 42 | break; 43 | // 获取班级信息 44 | case GClass.CLASS_INFO: 45 | result = GGroup.getClassInfo(ClassJson.parse(message.props).classid); 46 | break; 47 | // 获取用户聊天内容 48 | case GClass.CHAT_MSG: 49 | result = GMessage.getMessage(token, MessageJson.parse(message.props)); 50 | break; 51 | case GClass.MY_CLASS: 52 | result = GGroup.getMyClass(token); 53 | break; 54 | case GClass.MY_CHAT: 55 | result = GMessage.getNewChat(token); 56 | break; 57 | case GClass.MEMBERS: 58 | result = new Message(0, 0, Parser.toJson(GGroup.getMembers(ClassJson.parse(message.props).classid))); 59 | break; 60 | case GClass.ADD_CLASS: 61 | result = GGroup.addClass(message.token, ClassJson.parse(message.props)); 62 | break; 63 | case GClass.CREATE_CLASS: 64 | result = GGroup.createClass(message.token, ClassJson.parse(message.props)); 65 | break; 66 | case GClass.SET_ADMIN: 67 | var member = MemberJson.parse(message.props); 68 | result = GGroup.updateMember(token, member.userId, member.classId, member.type); 69 | break; 70 | case GClass.NOTICE_LIST: 71 | result = GGroup.getNoticeList(ClassJson.parse(message.props)); 72 | break; 73 | case GClass.ADD_NOTICE: 74 | result = GGroup.addNotice(NoticeJson.parse(message.props)); 75 | break; 76 | case GClass.UPDATE_NOTICE: 77 | result = GGroup.updateNotice(NoticeJson.parse(message.props)); 78 | break; 79 | case GClass.NOTICE_INFO: 80 | result = GGroup.getNotice(NoticeJson.parse(message.props)); 81 | break; 82 | case GClass.ADD_VOTE: 83 | result = GGroup.addVote(NoticeJson.parse(message.props)); 84 | break; 85 | case GClass.VOTE_LIST: 86 | result = GGroup.getVoteList(ClassJson.parse(message.props)); 87 | break; 88 | case GClass.ADVICE_LIST: 89 | result = GGroup.getAdviceList(VoteJson.parse(message.props)); 90 | break; 91 | case GClass.SET_VOTE: 92 | result = GGroup.setVoteStatus(VoteJson.parse(message.props)); 93 | break; 94 | case GClass.VOTE: 95 | result = GGroup.vote(VoteJson.parse(message.props)); 96 | break; 97 | case GClass.ADD_ADVICE: 98 | result = GGroup.addAdvice(NoticeJson.parse(message.props)); 99 | break; 100 | case GClass.FILE_LIST: 101 | result = GGroup.getFileList(ClassJson.parse(message.props)); 102 | break; 103 | case GClass.ADD_FILE: 104 | result = GGroup.addFile(FileJson.parse(message.props)); 105 | break; 106 | } 107 | return result; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/frame/AddClassDialog.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.frame; 2 | 3 | import top.grapedge.gclass.client.base.GPost; 4 | import top.grapedge.gclass.server.json.ClassJson; 5 | import top.grapedge.ui.base.GConfig; 6 | import top.grapedge.ui.base.util.ColorUtil; 7 | import top.grapedge.ui.component.*; 8 | import top.grapedge.ui.misc.GNumberFilter; 9 | 10 | import javax.swing.*; 11 | import java.awt.*; 12 | import java.awt.event.MouseAdapter; 13 | import java.awt.event.MouseEvent; 14 | import java.awt.event.MouseMotionAdapter; 15 | 16 | /** 17 | * @program: G-ClassManager 18 | * @description: 19 | * @author: Grapes 20 | * @create: 2019-03-12 19:16 21 | **/ 22 | public class AddClassDialog extends JDialog { 23 | // 窗体高度 24 | private int width = 540; 25 | // 窗体宽度 26 | private int height = 400; 27 | // 阴影大小 28 | private int shadowSize = 8; 29 | private MainFrame mainFrame; 30 | 31 | AddClassDialog(MainFrame parent, String title) { 32 | super(parent, title); 33 | this.mainFrame = parent; 34 | setModal(true); 35 | initView(); 36 | setUndecorated(true); 37 | setBackground(ColorUtil.TRANSPARENT); 38 | setSize(width, height); 39 | setLocationRelativeTo(null); 40 | setVisible(true); 41 | } 42 | 43 | private int startX, startY; 44 | 45 | private GInput classId; 46 | private void initView() { 47 | GShadowPane container = new GShadowPane(shadowSize, 10); 48 | container.setLayout(new BorderLayout()); 49 | setContentPane(container); 50 | 51 | GLowPoly poly = new GLowPoly(width, height / 3, 50, 50); 52 | poly.setLayout(new FlowLayout(FlowLayout.RIGHT)); 53 | add(poly, BorderLayout.NORTH); 54 | 55 | var close = new GButton(); 56 | ImageIcon closeIcon = new ImageIcon("images/close.png"); 57 | closeIcon.setImage(closeIcon.getImage().getScaledInstance(20, 20, Image.SCALE_DEFAULT)); 58 | close.setIcon(closeIcon); 59 | close.setRadius(7); 60 | close.setNormalColor(ColorUtil.TRANSPARENT); 61 | close.addActionListener(e-> { 62 | this.dispose(); 63 | }); 64 | 65 | poly.add(close); 66 | 67 | GPanel inputs = new GPanel(new FlowLayout(FlowLayout.CENTER)); 68 | inputs.setBackground(ColorUtil.BACKGROUND); 69 | inputs.setBorder(BorderFactory.createCompoundBorder(inputs.getBorder(), BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY))); 70 | inputs.setBorder(BorderFactory.createCompoundBorder(inputs.getBorder(), BorderFactory.createEmptyBorder(40, 0, 0, 0))); 71 | inputs.setPreferredSize(new Dimension(width, height / 3)); 72 | 73 | GLabel label = new GLabel("加入班级"); 74 | label.setFont(GConfig.createFont(18)); 75 | label.setPreferredSize(new Dimension(300, 30)); 76 | inputs.add(label); 77 | classId = new GInput(20); 78 | classId.setHintText("班级号"); 79 | classId.setFilter(new GNumberFilter(12)); 80 | classId.setPreferredSize(new Dimension(300, 45)); 81 | inputs.add(classId); 82 | 83 | GButton add = new GButton("添加"); 84 | add.addActionListener(e->{if (!classId.isHint()) { 85 | addEvent(classId.getText()); 86 | }}); 87 | add.setPreferredSize(new Dimension(300, 45)); 88 | inputs.add(add); 89 | 90 | add(inputs, BorderLayout.WEST); 91 | 92 | //////////////////////////////////////////// 93 | addDragMoveFunction(); 94 | } 95 | 96 | private void addEvent(String classId) { 97 | var json = new ClassJson(); 98 | json.classid = classId; 99 | var result = GPost.getInstance().addClass(json); 100 | if (result.code == 0) { 101 | new GMessageDialog(this, "加入成功", "圣骑士的斩杀"); 102 | mainFrame.onSendClassMessage(json.classid, "大家好,我是新加入的成员。"); 103 | dispose(); 104 | } else { 105 | new GMessageDialog(this, result.props, "法力值不足"); 106 | } 107 | } 108 | 109 | private void addDragMoveFunction() { 110 | addMouseListener(new MouseAdapter() { 111 | @Override 112 | public void mousePressed(MouseEvent e) { 113 | startX = e.getX(); 114 | startY = e.getY(); 115 | } 116 | 117 | }); 118 | 119 | addMouseMotionListener(new MouseMotionAdapter() { 120 | @Override 121 | public void mouseDragged(MouseEvent e) { 122 | int wx = getLocation().x; 123 | int wy = getLocation().y; 124 | setLocation(wx + e.getX() - startX, wy + e.getY() - startY); 125 | } 126 | }); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GChatBubble.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | import top.grapedge.ui.base.util.RendererUtil; 5 | 6 | import javax.swing.*; 7 | import javax.swing.border.EmptyBorder; 8 | import javax.swing.text.Style; 9 | import javax.swing.text.StyleConstants; 10 | import javax.swing.text.StyleContext; 11 | import javax.swing.text.StyledDocument; 12 | import java.awt.*; 13 | import java.awt.geom.GeneralPath; 14 | 15 | /** 16 | * @program: G-ClassManager 17 | * @description: 聊天气泡类 18 | * @author: Grapes 19 | * @create: 2019-03-16 11:43 20 | **/ 21 | public class GChatBubble extends GPanel { 22 | private GTextPane textPane; 23 | private boolean isLeft; 24 | 25 | public boolean isLeft() { 26 | return isLeft; 27 | } 28 | 29 | /** 30 | * 用于计算气泡应该有的实际高度。 31 | * @param width 期望的宽度 32 | * @param document 当前文档内容 33 | * @return 高度 34 | */ 35 | private int getContentHeight(int width, StyledDocument document) { 36 | var testPane = new GTextPane(); 37 | testPane.setSize(width, Short.MAX_VALUE); 38 | testPane.setStyledDocument(document); 39 | return testPane.getPreferredSize().height; 40 | } 41 | 42 | public GChatBubble(boolean isLeft) { 43 | //setLayout(new BorderLayout()); 44 | this.isLeft = isLeft; 45 | // 在绘制气泡时左偏移了10个像素,然后期望和边框距离6像素 46 | if (isLeft) { 47 | setBorder(new EmptyBorder(6, 16, 6, 6)); 48 | } else { 49 | setBorder(new EmptyBorder(6, 6, 6, 16)); 50 | } 51 | textPane = new GTextPane(); 52 | textPane.setFont(GConfig.font); 53 | textPane.setEditable(false); 54 | textPane.setOpaque(false); 55 | add(textPane); 56 | // 默认使用白色做背景 57 | setBackground(Color.WHITE); 58 | } 59 | 60 | 61 | // 高级操作通过这个进行 62 | public GTextPane getTextPane() { 63 | return textPane; 64 | } 65 | 66 | public void insertIcon(Icon icon) { 67 | getTextPane().setCaretPosition(getTextPane().getStyledDocument().getLength()); 68 | getTextPane().insertIcon(icon); 69 | } 70 | 71 | /** 72 | * 设置气泡的最佳宽度,此方法消耗双倍运算能力,因此建议只调用一次以获得最小的损失 73 | * @param width 74 | */ 75 | public void setPreferredSize(int width) { 76 | var h = getContentHeight(width, getTextPane().getStyledDocument()); 77 | getTextPane().setPreferredSize(new Dimension(width, h)); 78 | } 79 | 80 | public void insertText(String text) { 81 | var doc = getTextPane().getStyledDocument(); 82 | StyleContext context = new StyleContext(); 83 | Style style = context.addStyle("font", null); 84 | StyleConstants.setForeground(style, getForeground()); 85 | getTextPane().setCaretPosition(doc.getLength()); 86 | try { 87 | doc.insertString(doc.getLength(), text, style); 88 | } catch(Exception e) { 89 | e.printStackTrace(); 90 | } 91 | } 92 | 93 | @Override 94 | protected void paintComponent(final Graphics g) { 95 | Graphics2D g2d = (Graphics2D) g; 96 | RendererUtil.applyQualityRenderingHints(g2d); 97 | g2d.setPaint(getBackground()); 98 | if (isLeft) { 99 | paintLeftBubble(g2d); 100 | } else { 101 | paintRightBubble(g2d); 102 | } 103 | } 104 | 105 | private void paintRightBubble(Graphics2D g2d) { 106 | var width = getWidth(); 107 | var height = getHeight(); 108 | var path = new GeneralPath(); 109 | path.moveTo(width - 10, 18); 110 | path.lineTo(width, 13); 111 | path.lineTo(width - 10, 8); 112 | path.curveTo(width - 10, 8, width - 10, 0, width - 15, 0); 113 | path.lineTo(5, 0); 114 | path.curveTo(5, 0, 0, 0, 0, 5); 115 | path.lineTo(0, height - 5); 116 | path.curveTo(0, height - 5, 0, height, 5, height); 117 | path.lineTo(width - 15, height); 118 | path.curveTo(width - 15, height, width - 10, height, width - 10, height - 5); 119 | path.lineTo(width - 10, 18); 120 | path.closePath(); 121 | g2d.fill(path); 122 | } 123 | 124 | private void paintLeftBubble(Graphics2D g2d) { 125 | var width = getWidth(); 126 | var height = getHeight(); 127 | var path = new GeneralPath(); 128 | path.moveTo(10, 18); 129 | path.lineTo(0, 13); 130 | path.lineTo(10, 8); 131 | path.curveTo(10, 8, 10, 0, 15, 0); 132 | path.lineTo(width - 5, 0); 133 | path.curveTo(width - 5, 0, width, 0, width, 5); 134 | path.lineTo(width, height - 5); 135 | path.curveTo(width, height - 5, width, height, width - 5, height); 136 | path.lineTo(15, height); 137 | path.curveTo(15, height, 10, height, 10, height - 5); 138 | path.lineTo(10, 18); 139 | path.closePath(); 140 | g2d.fill(path); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/api/GUser.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.api; 2 | 3 | import top.grapedge.gclass.base.net.Message; 4 | import top.grapedge.gclass.server.base.Database; 5 | import top.grapedge.gclass.server.base.GServerManager; 6 | import top.grapedge.gclass.server.json.UserJson; 7 | 8 | import java.sql.SQLException; 9 | 10 | /** 11 | * @program: G-ClassManager 12 | * @description: 登录API 13 | * @author: Grapes 14 | * @create: 2019-03-14 13:41 15 | **/ 16 | public class GUser { 17 | public static Message login(UserJson user) { 18 | try { 19 | // 先判断用户名和密码是否和数据库相同 20 | if (checkAccount(user)) { 21 | // 设置登录状态为登录 22 | setUserStatus(user.userid, 1); 23 | // 生成token 24 | var token = 0; 25 | while (GServerManager.containsToken(token)) { 26 | token = (int)(Math.random() * 1e5); 27 | } 28 | GServerManager.addToken(token, user.userid); 29 | return new Message(0, token, "登录成功"); 30 | } else { 31 | return new Message(-1, Integer.MIN_VALUE, "账号或密码错误"); 32 | } 33 | } catch (Exception e) { 34 | e.printStackTrace(); 35 | } 36 | return null; 37 | } 38 | 39 | private static boolean checkAccount(UserJson user) throws SQLException { 40 | var sql = "SELECT userid FROM user WHERE userid=? AND password=?"; 41 | var ps = Database.getInstance().getConn().prepareStatement(sql); 42 | ps.setString(1, user.userid); 43 | ps.setString(2, user.password); 44 | var rs = ps.executeQuery(); 45 | // 如果查询到对应用户就登录 46 | return rs.next(); 47 | } 48 | 49 | private static void setUserStatus(String userid, int status) { 50 | try { 51 | var updateStatus = "UPDATE user SET status=?, last_online=? WHERE userid=?;"; 52 | var ps = Database.getInstance().getConn().prepareStatement(updateStatus); 53 | ps.setInt(1, status); 54 | ps.setDate(2, java.sql.Date.valueOf(java.time.LocalDate.now())); 55 | ps.setString(3, userid); 56 | ps.executeUpdate(); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | } 61 | public static void logout(int token) { 62 | var userid = GServerManager.getUserId(token); 63 | // 设置用户状态为离线 64 | setUserStatus(userid, 0); 65 | } 66 | 67 | /** 68 | * 获得用户登录状态 69 | * @param token 70 | * @return 71 | */ 72 | public static int getStatus(int token) { 73 | var userid = GServerManager.getUserId(token); 74 | try { 75 | var updateStatus = "SELECT status FROM user WHERE userid=?;"; 76 | var ps = Database.getInstance().getConn().prepareStatement(updateStatus); 77 | ps.setString(1, userid); 78 | var rs = ps.executeQuery(); 79 | if (rs.next()) { 80 | return rs.getInt(1); 81 | } 82 | } catch (Exception e) { 83 | e.printStackTrace(); 84 | } 85 | return 0; 86 | } 87 | /** 88 | * 获得用户信息 89 | * @param userid 90 | * @return 91 | */ 92 | public static UserJson getUserInfo(String userid) { 93 | try { 94 | var updateStatus = "SELECT * FROM user WHERE userid=?;"; 95 | var ps = Database.getInstance().getConn().prepareStatement(updateStatus); 96 | ps.setString(1, userid); 97 | var rs = ps.executeQuery(); 98 | if (rs.next()) { 99 | var user = new UserJson(); 100 | user.userid = userid; 101 | user.password = "就不告诉你"; 102 | user.avatar = rs.getInt("avatar"); 103 | user.username = rs.getString("username"); 104 | user.status = rs.getInt("status"); 105 | return user; 106 | } 107 | } catch (Exception e) { 108 | e.printStackTrace(); 109 | } 110 | return null; 111 | } 112 | 113 | public static Message register(UserJson user) { 114 | try { 115 | if(getUserInfo(user.userid) != null) { 116 | return new Message(-1, 0, "学号已存在"); 117 | } else if (user.userid.length() != 12) { 118 | return new Message(-2, 0, "学号错误"); 119 | } 120 | var sql = "INSERT INTO user (userid, password, username, avatar, status) VALUES (?,?,?,?,0);"; 121 | var ps = Database.getInstance().getConn().prepareStatement(sql); 122 | ps.setString(1, user.userid); 123 | ps.setString(2, user.password); 124 | ps.setString(3, user.username); 125 | ps.setInt(4, user.avatar); 126 | ps.executeUpdate(); 127 | return new Message(0, 0, "注册成功"); 128 | } catch (Exception e) { 129 | e.printStackTrace(); 130 | } 131 | return null; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/base/util/ImageUtil.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.base.util; 2 | 3 | import com.jhlabs.image.GaussianFilter; 4 | 5 | import javax.swing.*; 6 | import java.awt.*; 7 | import java.awt.image.BufferedImage; 8 | 9 | /** 10 | * @program: G-ClassManager 11 | * @description: 图片 12 | * @author: Grapes 13 | * @create: 2019-03-05 18:38 14 | **/ 15 | public class ImageUtil { 16 | public static BufferedImage toBufferedImage(Image img) { 17 | if (img instanceof BufferedImage) 18 | { 19 | return (BufferedImage) img; 20 | } 21 | 22 | // Create a buffered image with transparency 23 | BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); 24 | 25 | // Draw the image on to the buffered image 26 | Graphics2D bGr = bimage.createGraphics(); 27 | bGr.drawImage(img, 0, 0, null); 28 | bGr.dispose(); 29 | 30 | // Return the buffered image 31 | return bimage; 32 | } 33 | 34 | public static ImageIcon getRoundImageIcon(Image img) { 35 | try { 36 | BufferedImage master = toBufferedImage(img); 37 | int diameter = Math.min(master.getWidth(), master.getHeight()); 38 | 39 | // 创建一个圆形遮罩 40 | BufferedImage mask = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB); 41 | Graphics2D g2d = mask.createGraphics(); 42 | RendererUtil.applyQualityRenderingHints(g2d); 43 | g2d.fillOval(0, 0, diameter - 1, diameter - 1); 44 | g2d.dispose(); 45 | 46 | // 开始绘制 47 | BufferedImage masked = new BufferedImage(diameter, diameter, BufferedImage.TYPE_INT_ARGB); 48 | g2d = masked.createGraphics(); 49 | RendererUtil.applyQualityRenderingHints(g2d); 50 | 51 | int x = (diameter - master.getWidth()) / 2; 52 | int y = (diameter - master.getHeight()) / 2; 53 | 54 | g2d.drawImage(master, x, y, null); 55 | g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN)); 56 | g2d.drawImage(mask, 0, 0, null); 57 | g2d.dispose(); 58 | return new ImageIcon(masked); 59 | } catch (Exception e){ 60 | e.printStackTrace(); 61 | } 62 | return null; 63 | } 64 | 65 | public static BufferedImage createCompatibleImage(int width, int height) { 66 | return createCompatibleImage(width, height, Transparency.TRANSLUCENT); 67 | } 68 | 69 | public static BufferedImage createCompatibleImage(int width, int height, int transparency) { 70 | BufferedImage image = RendererUtil.getGraphicsConfiguration().createCompatibleImage(width, height, transparency); 71 | image.coerceData(true); 72 | return image; 73 | } 74 | 75 | public static BufferedImage createCompatibleImage(BufferedImage image, 76 | int width, int height) { 77 | return RendererUtil.getGraphicsConfiguration().createCompatibleImage(width, height, image.getTransparency()); 78 | } 79 | 80 | public static BufferedImage createCompatibleImage(BufferedImage image) { 81 | return createCompatibleImage(image, image.getWidth(), image.getHeight()); 82 | } 83 | 84 | public static BufferedImage generateBlur(BufferedImage imgSource, int size, Color color, float alpha) { 85 | GaussianFilter filter = new GaussianFilter(size); 86 | 87 | int imgWidth = imgSource.getWidth(); 88 | int imgHeight = imgSource.getHeight(); 89 | 90 | BufferedImage imgBlur = createCompatibleImage(imgWidth, imgHeight); 91 | Graphics2D g2 = imgBlur.createGraphics(); 92 | RendererUtil.applyQualityRenderingHints(g2); 93 | 94 | g2.drawImage(imgSource, 0, 0, null); 95 | g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha)); 96 | g2.setColor(color); 97 | 98 | g2.fillRect(0, 0, imgSource.getWidth(), imgSource.getHeight()); 99 | g2.dispose(); 100 | 101 | imgBlur = filter.filter(imgBlur, null); 102 | 103 | return imgBlur; 104 | } 105 | 106 | public static BufferedImage generateShadow(BufferedImage imgSource, int size, Color color, float alpha) { 107 | int imgWidth = imgSource.getWidth() + (size * 2); 108 | int imgHeight = imgSource.getHeight() + (size * 2); 109 | 110 | BufferedImage imgMask = createCompatibleImage(imgWidth, imgHeight); 111 | Graphics2D g2 = imgMask.createGraphics(); 112 | RendererUtil.applyQualityRenderingHints(g2); 113 | 114 | int x = Math.round((imgWidth - imgSource.getWidth()) / 2f); 115 | int y = Math.round((imgHeight - imgSource.getHeight()) / 2f); 116 | g2.drawImage(imgSource, x, y, null); 117 | g2.dispose(); 118 | 119 | // ---- Blur here --- 120 | BufferedImage imgGlow = generateBlur(imgMask, (size * 2), color, alpha); 121 | 122 | return imgGlow; 123 | } 124 | 125 | public static void scaleImage(ImageIcon icon, int maxSize) { 126 | if (icon.getIconHeight() > maxSize || icon.getIconWidth() > maxSize) { 127 | Dimension d; 128 | if (icon.getIconWidth() > icon.getIconHeight()) { 129 | double per = (double)maxSize / icon.getIconWidth(); 130 | var height = per * icon.getIconHeight(); 131 | d = new Dimension(maxSize, (int)height); 132 | } else { 133 | double per = (double)maxSize / icon.getIconHeight(); 134 | var width = per * icon.getIconWidth(); 135 | d = new Dimension((int)width, maxSize); 136 | } 137 | icon.setImage(icon.getImage().getScaledInstance(d.width, d.height, Image.SCALE_DEFAULT)); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/server/api/GMessage.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.server.api; 2 | 3 | import top.grapedge.gclass.base.Parser; 4 | import top.grapedge.gclass.base.net.Message; 5 | import top.grapedge.gclass.server.base.Database; 6 | import top.grapedge.gclass.server.base.GServerManager; 7 | import top.grapedge.gclass.server.json.MessageJson; 8 | import top.grapedge.gclass.server.json.UserJson; 9 | 10 | import java.sql.Statement; 11 | import java.sql.Timestamp; 12 | import java.util.ArrayList; 13 | import java.util.Calendar; 14 | import java.util.List; 15 | 16 | /** 17 | * @program: G-ClassManager 18 | * @description: 用于获取聊天的各种信息 19 | * @author: Grapes 20 | * @create: 2019-03-15 11:05 21 | **/ 22 | public class GMessage { 23 | public static Message getNewChat(int token) { 24 | try { 25 | var userid = GServerManager.getUserId(token); 26 | var sql = "SELECT sender FROM message WHERE receiver=? GROUP BY sender;"; 27 | var ps = Database.getInstance().getConn().prepareStatement(sql); 28 | ps.setString(1, userid); 29 | var rs = ps.executeQuery(); 30 | List list = new ArrayList<>(); 31 | while (rs.next()) { 32 | var u = GUser.getUserInfo(rs.getString("sender")); 33 | if (u != null) 34 | list.add(u); 35 | } 36 | return new Message(0, 0, Parser.toJson(list)); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | } 40 | return null; 41 | } 42 | 43 | public static boolean removeMessage(int msgid) { 44 | try { 45 | var sql = "DELETE FROM message WHERE msgid = ?;"; 46 | var ps = Database.getInstance().getConn().prepareStatement(sql); 47 | ps.setInt(1, msgid); 48 | ps.executeUpdate(); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | } 52 | return false; 53 | } 54 | 55 | private static List getNormalMessage(MessageJson messageJson) { 56 | try { 57 | var sql = "SELECT * FROM message WHERE type=0 AND receiver=? AND sender=? ORDER BY time;"; 58 | var ps = Database.getInstance().getConn().prepareStatement(sql); 59 | ps.setString(1, messageJson.receiver); 60 | ps.setString(2, messageJson.sender); 61 | var rs = ps.executeQuery(); 62 | List list = new ArrayList<>(); 63 | while (rs.next()) { 64 | var msg = new MessageJson(); 65 | msg.msgid = rs.getInt("msgid"); 66 | msg.receiver = rs.getString("receiver"); 67 | msg.sender = rs.getString("sender"); 68 | msg.text = rs.getString("text"); 69 | msg.time = rs.getTimestamp("time").getTime(); 70 | msg.type = rs.getInt("type"); 71 | list.add(msg); 72 | // 阅读过消息后就移除这条消息 73 | removeMessage(msg.msgid); 74 | } 75 | return list; 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | } 79 | return new ArrayList<>(); 80 | } 81 | 82 | // 得到群组消息 83 | private static List getClassMessage(int token, MessageJson messageJson) { 84 | try { 85 | var sql = "SELECT * FROM message WHERE type=1 AND receiver=? AND time > ? ORDER BY time;"; 86 | var ps = Database.getInstance().getConn().prepareStatement(sql); 87 | ps.setString(1, messageJson.receiver); 88 | ps.setTimestamp(2, GGroup.getLastVisitedTime(token, messageJson.receiver)); 89 | 90 | GGroup.setLastVisitedTime(token, messageJson.receiver, new Timestamp(Calendar.getInstance().getTimeInMillis())); 91 | 92 | var rs = ps.executeQuery(); 93 | List list = new ArrayList<>(); 94 | while (rs.next()) { 95 | var msg = new MessageJson(); 96 | msg.msgid = rs.getInt("msgid"); 97 | msg.receiver = rs.getString("receiver"); 98 | msg.sender = rs.getString("sender"); 99 | msg.text = rs.getString("text"); 100 | msg.time = rs.getTimestamp("time").getTime(); 101 | msg.type = rs.getInt("type"); 102 | list.add(msg); 103 | // 群组消息不清数据库 104 | } 105 | return list; 106 | } catch (Exception e) { 107 | e.printStackTrace(); 108 | } 109 | return new ArrayList<>(); 110 | } 111 | 112 | public static Message getMessage(int token, MessageJson messageJson) { 113 | try { 114 | //if (!(userid.equals(messageJson.sender) || userid.equals(messageJson.receiver))) return null; 115 | if (messageJson.type == 0) { 116 | return new Message(0, 0, Parser.toJson(getNormalMessage(messageJson))); 117 | } else { 118 | return new Message(0, 0, Parser.toJson(getClassMessage(token, messageJson))); 119 | } 120 | } catch (Exception e) { 121 | e.printStackTrace(); 122 | } 123 | return null; 124 | } 125 | 126 | public static void addMessage(MessageJson json) { 127 | try { 128 | 129 | var sql = "INSERT INTO message (sender, receiver, text, time, type) VALUES (?,?,?,?,?);"; 130 | var ps = Database.getInstance().getConn().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 131 | ps.setString(1, json.sender); 132 | ps.setString(2, json.receiver); 133 | ps.setString(3, json.text); 134 | ps.setTimestamp(4, new Timestamp(json.time)); 135 | ps.setInt(5, json.type); 136 | ps.executeUpdate(); 137 | } catch (Exception e) { 138 | e.printStackTrace(); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GButton.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.GConfig; 4 | import top.grapedge.ui.base.animation.base.Animator; 5 | import top.grapedge.ui.base.animation.color.ColorAnimatable; 6 | import top.grapedge.ui.base.animation.color.ColorRange; 7 | import top.grapedge.ui.base.util.RendererUtil; 8 | 9 | import javax.swing.*; 10 | import javax.swing.border.EmptyBorder; 11 | import java.awt.*; 12 | import java.awt.event.MouseAdapter; 13 | import java.awt.event.MouseEvent; 14 | import java.time.Duration; 15 | 16 | /** 17 | * @program: UIFrame 18 | * @description: 重构按钮类 19 | * @author: Grapes 20 | * @create: 2019-03-04 20:26 21 | **/ 22 | public class GButton extends JButton { 23 | private Duration animationTime = Duration.ofMillis(200); 24 | private ColorAnimatable transitionAnimatable; 25 | 26 | private Color textColor = Color.WHITE; 27 | private Color normalColor = new Color(0x515A5A); 28 | private Color hoverColor = new Color(0x616A6B); 29 | private Color pressColor = new Color(0x424949); 30 | private double radiusWidth = 0.0, radiusHeight; 31 | 32 | public GButton() { 33 | super(); 34 | setOpaque(false); 35 | setFocusPainted(false); 36 | //setBorderPainted(false); 37 | setBorder(new EmptyBorder(8, 10, 8, 10)); 38 | setFont(GConfig.font); 39 | setForeground(textColor); 40 | setContentAreaFilled(false); 41 | setBackground(normalColor); 42 | addMouseListener(new MouseAdapter() { 43 | @Override 44 | public void mouseEntered(MouseEvent e) { 45 | setButtonColor(); 46 | } 47 | 48 | @Override 49 | public void mouseExited(MouseEvent e) { 50 | setButtonColor(); 51 | } 52 | 53 | @Override 54 | public void mousePressed(MouseEvent e) { 55 | setButtonColor(); 56 | } 57 | 58 | @Override 59 | public void mouseReleased(MouseEvent e) { 60 | setButtonColor(); 61 | } 62 | }); 63 | 64 | } 65 | 66 | public GButton(String text) { 67 | this(); 68 | setText(text); 69 | } 70 | 71 | public void setRadius(double radius) { 72 | this.radiusWidth = this.radiusHeight = radius; 73 | } 74 | 75 | public void setRadius(double radiusWidth, double radiusHeight) { 76 | this.radiusWidth = radiusWidth; 77 | this.radiusHeight = radiusHeight; 78 | } 79 | 80 | public void setRadiusWidth(double radiusWidth) { 81 | this.radiusWidth = radiusWidth; 82 | } 83 | 84 | public void setRadiusHeight(double radiusHeight) { 85 | this.radiusHeight = radiusHeight; 86 | } 87 | 88 | public void setHoverColor(Color hoverColor) { 89 | this.hoverColor = hoverColor; 90 | setButtonColor(); 91 | } 92 | 93 | public void setNormalColor(Color normalColor) { 94 | this.normalColor = normalColor; 95 | setBackground(normalColor); 96 | } 97 | 98 | public void setPressColor(Color pressColor) { 99 | this.pressColor = pressColor; 100 | setButtonColor(); 101 | } 102 | 103 | public void setTextColor(Color textColor) { 104 | this.textColor = textColor; 105 | setForeground(this.textColor); 106 | } 107 | 108 | public void setAnimationTime(Duration animationTime) { 109 | this.animationTime = animationTime; 110 | } 111 | 112 | protected void setButtonColor() { 113 | Color targetColor; 114 | if (getModel().isPressed()) { 115 | targetColor = pressColor; 116 | } else if (getModel().isRollover()) { 117 | targetColor = hoverColor; 118 | } else { 119 | targetColor = normalColor; 120 | } 121 | setColorTransitionAnimatable(targetColor); 122 | } 123 | 124 | protected void setColorTransitionAnimatable(Color targetColor) { 125 | double progress = stopAnimation(); 126 | transitionAnimatable = new ColorAnimatable( 127 | new ColorRange(getBackground(), targetColor), 128 | preferredAnimationTime(progress), 129 | animator -> setBackground((Color) animator.getValue()) 130 | ); 131 | Animator.INSTANCE.add(transitionAnimatable); 132 | } 133 | 134 | protected double stopAnimation() { 135 | if (transitionAnimatable != null) { 136 | Animator.INSTANCE.remove(transitionAnimatable); 137 | return transitionAnimatable.getProgress(); 138 | } 139 | return 0.0; 140 | } 141 | 142 | protected Duration preferredAnimationTime(double currentProgress) { 143 | if (currentProgress > 0.0 && currentProgress < 1.0) { 144 | double remainingProgress = 1.0 - currentProgress; 145 | double runningTime = animationTime.toMillis() * remainingProgress; 146 | return Duration.ofMillis((long)runningTime); 147 | } 148 | 149 | return animationTime; 150 | } 151 | 152 | 153 | @Override 154 | protected void paintComponent(Graphics g) { 155 | Graphics2D g2d = (Graphics2D)g; 156 | 157 | // 抗锯齿 158 | RendererUtil.applyQualityRenderingHints(g2d, true); 159 | /*绘制按钮*/ 160 | g2d.setColor(getBackground()); 161 | if (radiusWidth < 0.0) radiusWidth = 0.0; 162 | if (radiusHeight < 0.0) radiusHeight = 0.0; 163 | 164 | int rw = (int)radiusWidth; 165 | int rh = (int)radiusHeight; 166 | if (radiusWidth <= 1.0) { 167 | rw = (int)(getWidth() * radiusWidth); 168 | } 169 | if (radiusHeight <= 1.0) { 170 | rh = (int)(getHeight() * radiusHeight); 171 | } 172 | 173 | g2d.fillRoundRect(0, 0, getWidth(), getHeight(), rw, rh); 174 | 175 | super.paintComponent(g); 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /src/top/grapedge/gclass/client/frame/CreateClassDialog.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.client.frame; 2 | 3 | import top.grapedge.gclass.client.base.GPost; 4 | import top.grapedge.gclass.server.json.ClassJson; 5 | import top.grapedge.ui.base.GConfig; 6 | import top.grapedge.ui.base.util.ColorUtil; 7 | import top.grapedge.ui.component.*; 8 | import top.grapedge.ui.misc.GLimitFilter; 9 | import top.grapedge.ui.misc.GNumberFilter; 10 | 11 | import javax.swing.*; 12 | import java.awt.*; 13 | import java.awt.event.MouseAdapter; 14 | import java.awt.event.MouseEvent; 15 | import java.awt.event.MouseMotionAdapter; 16 | 17 | /** 18 | * @program: G-ClassManager 19 | * @description: 20 | * @author: Grapes 21 | * @create: 2019-03-12 19:16 22 | **/ 23 | public class CreateClassDialog extends JDialog { 24 | // 窗体高度 25 | private int width = 540; 26 | // 窗体宽度 27 | private int height = 540; 28 | // 阴影大小 29 | private int shadowSize = 8; 30 | 31 | private MainFrame mainFrame; 32 | public CreateClassDialog(MainFrame parent, String title) { 33 | super(parent, title); 34 | this.mainFrame = parent; 35 | setModal(true); 36 | initView(); 37 | setUndecorated(true); 38 | setBackground(ColorUtil.TRANSPARENT); 39 | setSize(width, height); 40 | setLocationRelativeTo(null); 41 | setVisible(true); 42 | } 43 | 44 | private int startX, startY; 45 | 46 | private GInput classIndex, gradeIndex, intro; 47 | private void initView() { 48 | GShadowPane container = new GShadowPane(shadowSize, 10); 49 | container.setLayout(new BorderLayout()); 50 | setContentPane(container); 51 | 52 | GLowPoly poly = new GLowPoly(width, height / 3, 50, 50); 53 | poly.setLayout(new FlowLayout(FlowLayout.RIGHT)); 54 | add(poly, BorderLayout.NORTH); 55 | 56 | var close = new GButton(); 57 | ImageIcon closeIcon = new ImageIcon("images/close.png"); 58 | closeIcon.setImage(closeIcon.getImage().getScaledInstance(20, 20, Image.SCALE_DEFAULT)); 59 | close.setIcon(closeIcon); 60 | close.setRadius(7); 61 | close.setNormalColor(ColorUtil.TRANSPARENT); 62 | close.addActionListener(e-> this.dispose()); 63 | 64 | poly.add(close); 65 | 66 | GPanel inputs = new GPanel(new FlowLayout(FlowLayout.CENTER)); 67 | inputs.setBackground(ColorUtil.BACKGROUND); 68 | inputs.setBorder(BorderFactory.createCompoundBorder(inputs.getBorder(), BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY))); 69 | inputs.setBorder(BorderFactory.createCompoundBorder(inputs.getBorder(), BorderFactory.createEmptyBorder(40, 0, 0, 0))); 70 | inputs.setPreferredSize(new Dimension(width, height / 3)); 71 | 72 | GLabel label = new GLabel("创建班级"); 73 | label.setFont(GConfig.createFont(18)); 74 | label.setPreferredSize(new Dimension(300, 30)); 75 | inputs.add(label); 76 | 77 | 78 | 79 | gradeIndex = new GInput(20); 80 | gradeIndex.setHintText("年级"); 81 | gradeIndex.setFilter(new GNumberFilter(4)); 82 | gradeIndex.setPreferredSize(new Dimension(300, 45)); 83 | inputs.add(gradeIndex); 84 | 85 | classIndex = new GInput(20); 86 | classIndex.setHintText("班级"); 87 | classIndex.setFilter(new GNumberFilter(4)); 88 | classIndex.setPreferredSize(new Dimension(300, 45)); 89 | inputs.add(classIndex); 90 | 91 | intro = new GInput(20); 92 | intro.setHintText("一句话介绍"); 93 | intro.setFilter(new GLimitFilter(12)); 94 | intro.setPreferredSize(new Dimension(300, 45)); 95 | inputs.add(intro); 96 | 97 | GButton add = new GButton("创建"); 98 | add.addActionListener(e-> { 99 | if (gradeIndex.isHint() || classIndex.isHint() || intro.isHint()) { 100 | new GMessageDialog(this, "请输入信息", "哼"); 101 | return; 102 | } 103 | createEvent(gradeIndex.getText(), classIndex.getText(), intro.getText()); 104 | }); 105 | add.setPreferredSize(new Dimension(300, 45)); 106 | inputs.add(add); 107 | 108 | add(inputs, BorderLayout.WEST); 109 | 110 | //////////////////////////////////////////// 111 | addDragMoveFunction(); 112 | } 113 | 114 | private void createEvent(String gradeId, String classId, String introText) { 115 | if (gradeId.length() != 4) { 116 | new GMessageDialog(this, "年级为四位数字", "没想到吧"); 117 | } else if (classId.length() == 0 || classId.length() > 4) { 118 | new GMessageDialog(this, "班级编号不对劲鸭", "没想到吧"); 119 | } else if (introText.length() > 12) { 120 | new GMessageDialog(this, "介绍不能超过十二个字", "没想到吧"); 121 | } else { 122 | var json = new ClassJson(); 123 | json.gradeIndex = Integer.parseInt(gradeId); 124 | json.classIndex = Integer.parseInt(classId); 125 | json.avatar = -1; 126 | json.intro = introText; 127 | json.classid = String.format("%04d%04d", json.gradeIndex, json.classIndex); 128 | var result = GPost.getInstance().createClass(json); 129 | if (result.code == 0) { 130 | new GMessageDialog(this, result.props, "想到了"); 131 | mainFrame.onSendClassMessage(json.classid, "我创建了班级:" + json.gradeIndex + "级" + json.classIndex + "班"); 132 | dispose(); 133 | } else { 134 | new GMessageDialog(this, result.props, "没想到吧"); 135 | } 136 | } 137 | } 138 | 139 | private void addDragMoveFunction() { 140 | addMouseListener(new MouseAdapter() { 141 | @Override 142 | public void mousePressed(MouseEvent e) { 143 | startX = e.getX(); 144 | startY = e.getY(); 145 | } 146 | 147 | }); 148 | 149 | addMouseMotionListener(new MouseMotionAdapter() { 150 | @Override 151 | public void mouseDragged(MouseEvent e) { 152 | int wx = getLocation().x; 153 | int wy = getLocation().y; 154 | setLocation(wx + e.getX() - startX, wy + e.getY() - startY); 155 | } 156 | }); 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/WrapLayout.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | /** 4 | * @program: G-ClassManager 5 | * @description: 6 | * @author: Grapes 7 | * @create: 2019-03-16 16:02 8 | **/ 9 | import javax.swing.*; 10 | import java.awt.*; 11 | 12 | /** 13 | * FlowLayout subclass that fully supports wrapping of components. 14 | */ 15 | public class WrapLayout extends FlowLayout 16 | { 17 | private Dimension preferredLayoutSize; 18 | 19 | /** 20 | * Constructs a new WrapLayout with a left 21 | * alignment and a default 5-unit horizontal and vertical gap. 22 | */ 23 | public WrapLayout() 24 | { 25 | super(); 26 | } 27 | 28 | /** 29 | * Constructs a new FlowLayout with the specified 30 | * alignment and a default 5-unit horizontal and vertical gap. 31 | * The value of the alignment argument must be one of 32 | * WrapLayout, WrapLayout, 33 | * or WrapLayout. 34 | * @param align the alignment value 35 | */ 36 | public WrapLayout(int align) 37 | { 38 | super(align); 39 | } 40 | 41 | /** 42 | * Creates a new flow layout manager with the indicated alignment 43 | * and the indicated horizontal and vertical gaps. 44 | *

45 | * The value of the alignment argument must be one of 46 | * WrapLayout, WrapLayout, 47 | * or WrapLayout. 48 | * @param align the alignment value 49 | * @param hgap the horizontal gap between components 50 | * @param vgap the vertical gap between components 51 | */ 52 | public WrapLayout(int align, int hgap, int vgap) 53 | { 54 | super(align, hgap, vgap); 55 | } 56 | 57 | /** 58 | * Returns the preferred dimensions for this layout given the 59 | * visible components in the specified target container. 60 | * @param target the component which needs to be laid out 61 | * @return the preferred dimensions to lay out the 62 | * subcomponents of the specified container 63 | */ 64 | @Override 65 | public Dimension preferredLayoutSize(Container target) 66 | { 67 | return layoutSize(target, true); 68 | } 69 | 70 | /** 71 | * Returns the minimum dimensions needed to layout the visible 72 | * components contained in the specified target container. 73 | * @param target the component which needs to be laid out 74 | * @return the minimum dimensions to lay out the 75 | * subcomponents of the specified container 76 | */ 77 | @Override 78 | public Dimension minimumLayoutSize(Container target) 79 | { 80 | Dimension minimum = layoutSize(target, false); 81 | minimum.width -= (getHgap() + 1); 82 | return minimum; 83 | } 84 | 85 | /** 86 | * Returns the minimum or preferred dimension needed to layout the target 87 | * container. 88 | * 89 | * @param target target to get layout size for 90 | * @param preferred should preferred size be calculated 91 | * @return the dimension to layout the target container 92 | */ 93 | private Dimension layoutSize(Container target, boolean preferred) 94 | { 95 | synchronized (target.getTreeLock()) 96 | { 97 | // Each row must fit with the width allocated to the containter. 98 | // When the container width = 0, the preferred width of the container 99 | // has not yet been calculated so lets ask for the maximum. 100 | 101 | int targetWidth = target.getSize().width; 102 | Container container = target; 103 | 104 | while (container.getSize().width == 0 && container.getParent() != null) 105 | { 106 | container = container.getParent(); 107 | } 108 | 109 | targetWidth = container.getSize().width; 110 | 111 | if (targetWidth == 0) 112 | targetWidth = Integer.MAX_VALUE; 113 | 114 | int hgap = getHgap(); 115 | int vgap = getVgap(); 116 | Insets insets = target.getInsets(); 117 | int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); 118 | int maxWidth = targetWidth - horizontalInsetsAndGap; 119 | 120 | // Fit components into the allowed width 121 | 122 | Dimension dim = new Dimension(0, 0); 123 | int rowWidth = 0; 124 | int rowHeight = 0; 125 | 126 | int nmembers = target.getComponentCount(); 127 | 128 | for (int i = 0; i < nmembers; i++) 129 | { 130 | Component m = target.getComponent(i); 131 | 132 | if (m.isVisible()) 133 | { 134 | Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); 135 | 136 | // Can't add the component to current row. Start a new row. 137 | 138 | if (rowWidth + d.width > maxWidth) 139 | { 140 | addRow(dim, rowWidth, rowHeight); 141 | rowWidth = 0; 142 | rowHeight = 0; 143 | } 144 | 145 | // Add a horizontal gap for all components after the first 146 | 147 | if (rowWidth != 0) 148 | { 149 | rowWidth += hgap; 150 | } 151 | 152 | rowWidth += d.width; 153 | rowHeight = Math.max(rowHeight, d.height); 154 | } 155 | } 156 | 157 | addRow(dim, rowWidth, rowHeight); 158 | 159 | dim.width += horizontalInsetsAndGap; 160 | dim.height += insets.top + insets.bottom + vgap * 2; 161 | 162 | // When using a scroll pane or the DecoratedLookAndFeel we need to 163 | // make sure the preferred size is less than the size of the 164 | // target containter so shrinking the container size works 165 | // correctly. Removing the horizontal gap is an easy way to do this. 166 | 167 | Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); 168 | 169 | if (scrollPane != null && target.isValid()) 170 | { 171 | dim.width -= (hgap + 1); 172 | } 173 | 174 | return dim; 175 | } 176 | } 177 | 178 | /* 179 | * A new row has been completed. Use the dimensions of this row 180 | * to update the preferred size for the container. 181 | * 182 | * @param dim update the width and height when appropriate 183 | * @param rowWidth the width of the row to add 184 | * @param rowHeight the height of the row to add 185 | */ 186 | private void addRow(Dimension dim, int rowWidth, int rowHeight) 187 | { 188 | dim.width = Math.max(dim.width, rowWidth); 189 | 190 | if (dim.height > 0) 191 | { 192 | dim.height += getVgap(); 193 | } 194 | 195 | dim.height += rowHeight; 196 | } 197 | } -------------------------------------------------------------------------------- /src/top/grapedge/gclass/base/MarkDownParser.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.gclass.base; 2 | 3 | import java.util.*; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * @program: G-ClassManager 8 | * @description: 9 | * @author: Grapes 10 | * @create: 2019-03-19 12:50 11 | **/ 12 | public class MarkDownParser { 13 | private ArrayList match(String text, String regex) { 14 | var list = new ArrayList(); 15 | var matcher = Pattern.compile(regex).matcher(text); 16 | while (matcher.find()) { 17 | list.add(matcher.group(0)); 18 | } 19 | return list; 20 | } 21 | private String outdent(String text) { 22 | var find = match(text, "^(\\t| )+"); 23 | return text.replace("^" + (find.size() > 0 ? find.get(0) : ""), ""); 24 | } 25 | 26 | private String encodeAttr(String text) { 27 | return text.replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">"); 28 | } 29 | 30 | private Map> TAGS = new HashMap<>(); 31 | 32 | private ArrayList createArray(T... objects) { 33 | var list = new ArrayList(); 34 | Collections.addAll(list, objects); 35 | return list; 36 | } 37 | 38 | public MarkDownParser() { 39 | TAGS.put("", createArray("", "")); 40 | TAGS.put("_", createArray("", "")); 41 | TAGS.put("~", createArray("", "")); 42 | TAGS.put("\n", createArray("
")); 43 | TAGS.put(" ", createArray("
")); 44 | TAGS.put("-", createArray("


")); 45 | } 46 | 47 | private String getChar(String str, int index) { 48 | return str.length() > index ? String.valueOf(str.charAt(index)) : ""; 49 | } 50 | 51 | private List context = new ArrayList<>(); 52 | 53 | private String tag(String token) { 54 | var desc = TAGS.get(getChar(token.replaceAll("\\*", "_"), 1)); 55 | var end = context.get(context.size() - 1).equals(token); 56 | 57 | if (desc != null) return token; 58 | if (desc.size() < 2) return desc.get(0); 59 | if (end) { 60 | context.remove(token); 61 | } else { 62 | context.add(token); 63 | } 64 | return desc.get(end ? 1 : 0); 65 | } 66 | 67 | private String flush() { 68 | var str = new StringBuilder(); 69 | while (context.size() > 0) str.append(tag(context.get(context.size() - 1))); 70 | return str.toString(); 71 | } 72 | 73 | private interface ReplaceListener { 74 | String onReplace(List groups); 75 | } 76 | private String replaceAll(String text, String regex, ReplaceListener listener) { 77 | var m = Pattern.compile(regex).matcher(text); 78 | var result = new StringBuffer(); 79 | while (m.find()) { 80 | var list = new ArrayList(); 81 | for (var i = 0; i <= m.groupCount(); i++) list.add(m.group(i)); 82 | m.appendReplacement(result, listener.onReplace(list)); 83 | } 84 | m.appendTail(result); 85 | return result.toString(); 86 | } 87 | 88 | public String parse(String text) { 89 | try { 90 | var lines = text.split("\n"); 91 | var builder = new StringBuilder(); 92 | for (var i : lines) if (!i.isEmpty()) { 93 | builder.append(parse(i, null)); 94 | } else { 95 | builder.append("
"); 96 | } 97 | return builder.toString(); 98 | } catch (Exception e) { 99 | return text; 100 | } 101 | } 102 | 103 | private String parse(String text, Map prevLinks) { 104 | Map links = prevLinks == null ? new HashMap<>() : prevLinks; 105 | var tokenizer = Pattern.compile("((?:^|\\n+)(?:\\n---+|\\* \\*(?: \\*)+)\\n)|(?:^``` *(\\w*)\\n([\\s\\S]*?)\\n```$)|((?:(?:^|\\n+)(?:\\t| {2,}).+)+\\n*)|((?:(?:^|\\n)([>*+-]|\\d+\\.)\\s+.*)+)|(?:\\!\\[([^\\]]*?)\\]\\(([^\\)]+?)\\))|(\\[)|(\\](?:\\(([^\\)]+?)\\))?)|(?:(?:^|\\n+)([^\\s].*)\\n(\\-{3,}|={3,})(?:\\n+|$))|(?:(?:^|\\n+)(#{1,6})\\s*(.+)(?:\\n+|$))|(?:`([^`].*?)`)|( \\n\\n*|\\n{2,}|__|\\*\\*|[_*]|~~)"); 106 | text = replaceAll(text, "^\\[(.+?)\\]:\\s*(.+)$", e-> { 107 | links.put(e.get(1).toLowerCase(), e.get(2)); 108 | return ""; 109 | }).replaceAll("^\\n+|\\n+$", ""); 110 | var token = tokenizer.matcher(text); 111 | int last = 0; 112 | var tmp = ""; 113 | var out = new StringBuilder(); 114 | while (token.find()) { 115 | var prev = text.substring(last, token.start()); 116 | last = token.end(); 117 | var chunk = token.group(0); 118 | if (prev.matches("[^\\\\](\\\\\\\\)*\\\\$")) { 119 | // TODO 120 | } else if (token.group(3) != null || token.group(4) != null) { 121 | chunk = "
"+outdent(encodeAttr(token.group(3) != null ? token.group(3) : token.group(4)).replace("^\\n+|\\n+$", "")) + "
"; 123 | } else if (token.group(6) != null) { 124 | tmp = token.group(6); 125 | var token5 = token.group(5); 126 | if (tmp.matches("\\.")) { 127 | token5 = token5.replaceAll("^\\d+", ""); 128 | } 129 | var inner = parse(outdent(token5.replaceAll("^\\s*[>*+.-]", "")), null); 130 | if (tmp.equals(">")) tmp = "blockquote"; 131 | else { 132 | tmp = tmp.matches("\\.") ? "ol" : "ul"; 133 | inner = replaceAll(inner, "^(.*)(\\n|$)", e-> "
  • " + e.get(1) + "
  • "); 134 | } 135 | chunk = "<" + tmp + ">" + inner + ""; 136 | } else if (token.group(8) != null) { 137 | chunk = "\"""; 138 | } else if (token.group(10) != null) { 139 | out = new StringBuilder(out.toString().replaceAll("", "")); 140 | chunk = flush() + ""; 141 | } else if (token.group(9) != null) { 142 | chunk = ""; 143 | } else if (token.group(12) != null || token.group(14) != null) { 144 | tmp = 'h' + String.valueOf(token.group(14) != null ? token.group(14).length() : (token.group(13).charAt(0) == '=' ? 1 : 2)); 145 | chunk = '<'+tmp+'>' + parse(token.group(12) != null ? token.group(12) : token.group(15), links) + ""; 146 | } else if (token.group(16) != null) { 147 | chunk = "" + encodeAttr(token.group(16)) + ""; 148 | } else if (token.group(17) != null || token.group(1) != null) { 149 | chunk = tag(token.group(17) != null ? token.group(17) : "--"); 150 | } 151 | out.append(prev); 152 | out.append(chunk); 153 | } 154 | return (out + text.substring(last) + flush()).trim(); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/top/grapedge/ui/component/GLowPoly.java: -------------------------------------------------------------------------------- 1 | package top.grapedge.ui.component; 2 | 3 | import top.grapedge.ui.base.util.RendererUtil; 4 | 5 | import javax.swing.*; 6 | import java.awt.*; 7 | import java.awt.event.ActionEvent; 8 | import java.awt.event.ActionListener; 9 | 10 | /** 11 | * @program: G-Chat 12 | * @description: 低多边形控件 13 | * @author: Grapes 14 | * @create: 2019-01-25 19:14 15 | **/ 16 | public class GLowPoly extends JPanel implements ActionListener { 17 | 18 | private int cWidth, cHeight, triWidth, triHeight; 19 | 20 | private Vertex points[][]; 21 | 22 | private TColor colors[][]; 23 | 24 | private Vector2D light, lightSpeed = new Vector2D((int) (Math.random() * 5+1), (int) (Math.random() * 2+1)); 25 | 26 | private int interval = 16; 27 | private int time = 0; 28 | 29 | public GLowPoly(int width, int height, int triWidth, int triHeight) { 30 | this.cWidth = width; 31 | this.cHeight = height; 32 | this.triWidth = triWidth; 33 | this.triHeight = triHeight; 34 | // 初始化网格生成 35 | initView(); 36 | light = new Vector2D((int)(Math.random() * cWidth), (int)(Math.random() * cHeight)); 37 | // 动画更新 38 | new Timer(interval, this).start(); 39 | } 40 | 41 | private void initView() { 42 | int row = cHeight / triHeight + 5, col = cWidth / triWidth + 5; 43 | points = new Vertex[row][col]; 44 | colors = new TColor[row][col]; 45 | // 初始化各个点 46 | for (int i = 0; i < points.length; i++) { 47 | for (int j = 0; j < points[i].length; j++) { 48 | points[i][j] = new Vertex(new Vector2D(j * triWidth - triWidth * 3 + (int)(Math.random() * triWidth / 3), i * triHeight - triHeight * 2 + (int)(Math.random() * triHeight / 3))); 49 | colors[i][j] = new TColor(); 50 | } 51 | } 52 | } 53 | 54 | private void setBright() { 55 | for (Vertex[] point : points) { 56 | for (Vertex aPoint : point) { 57 | aPoint.light = 1.0 - (Math.sqrt((light.x - aPoint.point.x) * (light.x - aPoint.point.x) + 58 | (light.y - aPoint.point.y) * (light.y - aPoint.point.y)) / 100.0); 59 | 60 | if (aPoint.light < 0.0) aPoint.light = 0.0; 61 | } 62 | } 63 | } 64 | 65 | @Override 66 | public Dimension getPreferredSize() { 67 | return new Dimension(cWidth, cHeight); 68 | } 69 | 70 | private double sin01(int t, int T) { 71 | return Math.abs(Math.sin(t * (2 * Math.PI) / T)); 72 | } 73 | 74 | // 移动一个点 75 | private void movePoint(Vertex vertex) { 76 | Vector2D ori = vertex.original; 77 | Vector2D tar = vertex.target; 78 | vertex.point = new Vector2D((int) (ori.x + (tar.x - ori.x) * sin01(time + vertex.moveTime, 30000)), ori.y + (int)((tar.y - ori.y) * sin01(time + vertex.moveTime, 30000))); 79 | } 80 | 81 | // 移动一个点 82 | private void moveColor(TColor tc, double light) { 83 | tc.up = moveColor(tc.oriUp, tc.tarUp, tc.moveTime + time, light); 84 | tc.down = moveColor(tc.oriDown, tc.tarDown, tc.moveTime + time, light); 85 | } 86 | 87 | private Color moveColor(Color ori, Color tar, int t, double light) { 88 | int or = ori.getRed(), tr = tar.getRed(); 89 | double per = sin01(t, 10000); 90 | double r = (or + (tr - or) * per); 91 | if (light > 0) { 92 | double add = (130 - r) * light; 93 | if (r != 0) { 94 | r += add; 95 | } 96 | } 97 | return new Color((int)r, (int)r, (int)r); 98 | } 99 | 100 | 101 | @Override 102 | protected void paintComponent(Graphics g) { 103 | Graphics2D g2d = (Graphics2D)g; 104 | RendererUtil.applyQualityRenderingHints(g2d); 105 | 106 | for (var i : points) { 107 | for (var v : i) { 108 | movePoint(v); 109 | } 110 | } 111 | 112 | light.x += lightSpeed.x; 113 | light.y += lightSpeed.y; 114 | setBright(); 115 | if (light.x > cWidth) { 116 | light.x = cWidth; 117 | lightSpeed.x = -(int)(Math.random() * 5 + 1); 118 | } 119 | if (light.x < 0) { 120 | light.x = 0; 121 | lightSpeed.x = (int)(Math.random() * 5 + 1); 122 | } 123 | if (light.y > cHeight) { 124 | light.y = cHeight; 125 | lightSpeed.y = -(int)(Math.random() * 2 + 1); 126 | } 127 | if (light.y < 0) { 128 | light.y = 0; 129 | lightSpeed.y = (int)(Math.random() * 2 + 1); 130 | } 131 | //System.out.println(light.x + "," + light.y); 132 | for (int i = 0; i < colors.length - 1; i++) { 133 | for (int j = 0; j < colors[i].length - 1; j++) { 134 | moveColor(colors[i][j], points[i][j].light); 135 | } 136 | } 137 | 138 | for (int i = 0; i < points.length - 1; i++) { 139 | for (int j = 0; j < points[i].length - 1; j++) { 140 | drawTriangle(points[i][j], points[i][j + 1], points[i + 1][j], colors[i][j].up, g2d); 141 | drawTriangle(points[i][j + 1], points[i + 1][j], points[i + 1][j + 1], colors[i][j].down, g2d); 142 | } 143 | } 144 | 145 | } 146 | 147 | // 绘制三角形 148 | private void drawTriangle(Vertex a, Vertex b, Vertex c, Color color, Graphics g) { 149 | int x[] = {a.point.x, b.point.x, c.point.x }; 150 | int y[] = {a.point.y, b.point.y, c.point.y }; 151 | g.setColor(color); 152 | g.drawPolygon(x, y, 3); 153 | g.fillPolygon(x, y, 3); 154 | } 155 | 156 | @Override 157 | public void actionPerformed(ActionEvent e) { 158 | // 更新时间 159 | time += interval; 160 | if (getParent() != null) { 161 | getParent().repaint(); 162 | } else { 163 | repaint(); 164 | } 165 | } 166 | 167 | // 用于记录各个点的信息 168 | private class Vector2D { 169 | int x; 170 | int y; 171 | 172 | Vector2D(int x, int y) { 173 | this.x = x; 174 | this.y = y; 175 | } 176 | } 177 | 178 | private class Vertex { 179 | Vector2D point; 180 | Vector2D original; 181 | Vector2D target; 182 | double light = 0f; 183 | int moveTime = (int) (Math.random() * 15000); 184 | 185 | Vertex(Vector2D pos) { 186 | this.point = pos; 187 | this.original = pos; 188 | this.target = new Vector2D(pos.x + (int) (Math.random() * triWidth * 1.7), pos.y + (int)(Math.random() * triHeight * 0.15)); 189 | } 190 | } 191 | 192 | private class TColor { 193 | Color up, down; 194 | Color oriUp, oriDown; 195 | Color tarUp, tarDown; 196 | int moveTime = (int) (Math.random() * 15000); 197 | TColor() { 198 | this.up = getRandomColor(); 199 | this.down = getRandomColor(); 200 | this.oriUp = up; 201 | this.oriDown = down; 202 | this.tarUp = getRandomRGBLight(); 203 | this.tarDown = getRandomRGBLight(); 204 | } 205 | 206 | private Color getRandomColor() { 207 | var randomNum = Math.random(); 208 | randomNum = randomNum / 2 + 0.5; 209 | var r = (int)(randomNum * 50); 210 | return new Color(r, r, r); 211 | } 212 | 213 | private Color getRandomRGBLight() { 214 | var randomNum = Math.random(); 215 | randomNum = randomNum / 2 + 0.5; 216 | var r = 40 + (int)(randomNum * 30); 217 | return new Color(r, r, r); 218 | } 219 | } 220 | 221 | 222 | } 223 | --------------------------------------------------------------------------------