├── .gitignore ├── .idea ├── .gitignore ├── artifacts │ └── CodeLite_jar.xml ├── encodings.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── pom.xml └── src └── main ├── java ├── App.java ├── CustomNode.java ├── EditorView.java ├── ProjectView.java └── WelcomeView.java └── resources ├── META-INF └── MANIFEST.MF ├── icons ├── folder_icon.png └── folder_icon_24.png └── themes ├── blue.xml ├── eclipse.xml ├── monokai.xml ├── night.xml ├── purple.xml └── red.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/artifacts/CodeLite_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | $PROJECT_DIR$/out/artifacts/CodeLite_jar 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeLite 2 | 3 | A minimalistic code editor built using Java swing, flatlaf and RSyntaxTextArea. 4 | This project is meant for learning and experimental purposes, by no means this is a production-ready code editor, but rather a fun attempt to learn and create my own code editor 5 | 6 | ![Screenshot 2024-10-06 135007](https://github.com/user-attachments/assets/8fe92fed-70f6-4766-939f-de9b4d6775ad) 7 | 8 | # Features 9 | * Syntax highlighting 10 | * Auto save 11 | * adding, deleting or renaming files 12 | * open native terminal 13 | * language support for Java, python, C, C++ and Javascript (more to be added soon) 14 | * Dark and light theme 15 | * various color schemes, including Monokai and Eclipse themes. 16 | 17 | # Snapshots 18 | 19 | ![Screenshot 2024-10-06 135111](https://github.com/user-attachments/assets/01ddcdfb-4193-4715-a049-d92649097acf) 20 | 21 | ![Screenshot 2024-10-06 135225](https://github.com/user-attachments/assets/ddd2a519-6f58-4c30-b9c1-1a62ef8ce963) 22 | 23 | ![Screenshot 2024-10-06 135324](https://github.com/user-attachments/assets/fdced105-d52d-4cb1-887b-2559fad88b81) 24 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | CodeLite 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 21 13 | 21 14 | UTF-8 15 | 16 | 17 | 18 | 19 | com.formdev 20 | flatlaf 21 | 3.5.1 22 | 23 | 24 | com.formdev 25 | flatlaf-extras 26 | 3.5.1 27 | 28 | 29 | com.formdev 30 | flatlaf-fonts-jetbrains-mono 31 | 2.304 32 | 33 | 34 | com.fifesoft 35 | rsyntaxtextarea 36 | 3.5.1 37 | 38 | 39 | com.formdev 40 | flatlaf-fonts-inter 41 | 4.0 42 | 43 | 44 | com.fifesoft 45 | autocomplete 46 | 3.3.1 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/App.java: -------------------------------------------------------------------------------- 1 | 2 | import com.formdev.flatlaf.fonts.inter.FlatInterFont; 3 | import com.formdev.flatlaf.fonts.jetbrains_mono.FlatJetBrainsMonoFont; 4 | import com.formdev.flatlaf.themes.FlatMacDarkLaf; 5 | import com.formdev.flatlaf.themes.FlatMacLightLaf; 6 | import org.fife.ui.rsyntaxtextarea.SyntaxConstants; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | public class App extends JFrame { 14 | 15 | public WelcomeView welcomeView; 16 | public JSplitPane rootPanel; 17 | public ProjectView projectView; 18 | public EditorView editorView; 19 | 20 | public JPanel rightSplitPanel; 21 | public JPanel toolPanel; 22 | 23 | public JButton openTerminalButton; 24 | public JButton saveFileButton; 25 | 26 | public String os = System.getProperty("os.name").toLowerCase(); 27 | public String currentFileParentPath; 28 | public ProcessBuilder pb; 29 | 30 | public JMenuBar menuBar; 31 | public JMenu settingsMenu, themeItem, colorSchemeItem, languageItem; 32 | public JMenuItem closeProjectItem, newProjectItem, 33 | darkThemeItem, lightThemeItem, 34 | monokaiItem, eclipseItem, nightItem, redItem, blueItem, purpleItem, 35 | javaItem, pythonItem, cItem, jsItem, 36 | autoSaveItem, projectViewItem, 37 | exitItem; 38 | 39 | public boolean autoSave = false; 40 | public boolean projectViewEnabled = true; 41 | public Timer autoSaveTimer; 42 | 43 | public boolean darkTheme = true; 44 | public Font editorFont; 45 | 46 | public App() { 47 | setSize(800, 500); 48 | setTitle("CodeLite"); 49 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 50 | setLayout(new BorderLayout()); 51 | setLocationRelativeTo(null); 52 | } 53 | 54 | public void init() { 55 | editorFont = new Font(FlatJetBrainsMonoFont.FAMILY, Font.PLAIN, 18); 56 | 57 | welcomeView = new WelcomeView(this); 58 | 59 | projectView = new ProjectView(this); 60 | projectView.setMinimumSize(new Dimension(200, 0)); 61 | projectView.init(); 62 | projectView.initActionListeners(); 63 | 64 | editorView = new EditorView(this); 65 | 66 | rightSplitPanel = new JPanel(); 67 | 68 | toolPanel = new JPanel(); 69 | rootPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, projectView, rightSplitPanel); 70 | 71 | rightSplitPanel.setLayout(new BorderLayout()); 72 | toolPanel.setLayout(new FlowLayout(FlowLayout.CENTER)); 73 | 74 | rightSplitPanel.add(editorView.getContentPanel(), BorderLayout.CENTER); 75 | rightSplitPanel.add(toolPanel, BorderLayout.NORTH); 76 | 77 | saveFileButton = new JButton("Save"); 78 | saveFileButton.setEnabled(false); 79 | saveFileButton.setFont(new Font(FlatJetBrainsMonoFont.FAMILY, Font.PLAIN, 14)); 80 | saveFileButton.setBackground(new Color(67, 175, 21)); 81 | saveFileButton.addActionListener(e -> projectView.saveFile()); 82 | 83 | 84 | currentFileParentPath = projectView.projectPath; 85 | 86 | openTerminalButton = new JButton("Open Terminal"); 87 | openTerminalButton.setFont(new Font(FlatJetBrainsMonoFont.FAMILY, Font.PLAIN, 14)); 88 | openTerminalButton.setBackground(new Color(30, 126, 248)); 89 | openTerminalButton.addActionListener(e -> { 90 | try { 91 | 92 | if (os.contains("win")) { 93 | pb = new ProcessBuilder("cmd" , "/c" , "start" , "powershell.exe"); 94 | } else if (os.contains("mac")) { 95 | pb = new ProcessBuilder("open", "-a", "Terminal"); 96 | } else if (os.contains("nix") || os.contains("nux") || os.contains("bsd")) { 97 | pb = new ProcessBuilder("x-terminal-emulator"); 98 | } else 99 | JOptionPane.showMessageDialog(null, "Unsupported Operating System", "Error", JOptionPane.ERROR_MESSAGE); 100 | 101 | 102 | if (!projectView.getSelectedCustomNode().getParent().isLeaf()) { 103 | pb.directory(new File(projectView.projectPath)); 104 | } else { 105 | pb.directory(new File(currentFileParentPath)); 106 | } 107 | pb.start(); 108 | 109 | } catch (IOException ex) { 110 | System.err.println("Failed to open terminal: " + ex.getMessage()); 111 | } catch (UnsupportedOperationException ex) { 112 | System.err.println(ex.getMessage()); 113 | } catch (NullPointerException ex) { 114 | JOptionPane.showMessageDialog(null, "Select a file to open the terminal", "Error", JOptionPane.ERROR_MESSAGE); 115 | } 116 | }); 117 | 118 | menuBar = new JMenuBar(); 119 | settingsMenu = new JMenu("Settings", true); 120 | 121 | newProjectItem = new JMenuItem("Open new Project"); 122 | closeProjectItem = new JMenuItem("Close project"); 123 | 124 | themeItem = new JMenu("Theme"); 125 | darkThemeItem = new JMenuItem("Dark"); 126 | lightThemeItem = new JMenuItem("Light"); 127 | 128 | colorSchemeItem = new JMenu("Color scheme"); 129 | monokaiItem = new JMenuItem("Monokai"); 130 | eclipseItem = new JMenuItem("Eclipse"); 131 | nightItem = new JMenuItem("Night"); 132 | redItem = new JMenuItem("Reversal Red"); 133 | blueItem = new JMenuItem("Amplified Blue"); 134 | purpleItem = new JMenuItem("Hollow Purple"); 135 | 136 | languageItem = new JMenu("Language support"); 137 | javaItem = new JMenuItem("Java"); 138 | pythonItem = new JMenuItem("Python"); 139 | cItem = new JMenuItem("C/C++"); 140 | jsItem = new JMenuItem("Javascript"); 141 | 142 | autoSaveItem = new JMenuItem("Auto save : Off"); 143 | projectViewItem = new JMenuItem("Project view : Enabled"); 144 | exitItem = new JMenuItem("Exit CodeLite"); 145 | 146 | newProjectItem.addActionListener(e -> { 147 | projectView.getProjectTree().removeAll(); 148 | projectView.root.removeAllChildren(); 149 | projectView.openProject(); 150 | }); 151 | 152 | closeProjectItem.addActionListener(e -> { 153 | projectView.getProjectTree().removeAll(); 154 | projectView.root.removeAllChildren(); 155 | projectView.projectFiles.clear(); 156 | 157 | setContentPane(welcomeView); 158 | this.setSize(800, 500); 159 | this.setLocationRelativeTo(null); 160 | }); 161 | 162 | darkThemeItem.addActionListener(e -> { 163 | try { 164 | darkTheme = true; 165 | UIManager.setLookAndFeel(new FlatMacDarkLaf()); 166 | welcomeView.openProjectButton.setBackground(new Color(20, 125, 241)); 167 | 168 | SwingUtilities.updateComponentTreeUI(this); 169 | editorView.setFont(editorFont); 170 | projectView.refreshTree(); 171 | } catch (UnsupportedLookAndFeelException ex) { 172 | throw new RuntimeException(ex); 173 | } 174 | }); 175 | 176 | lightThemeItem.addActionListener(e -> { 177 | try { 178 | darkTheme = false; 179 | UIManager.setLookAndFeel(new FlatMacLightLaf()); 180 | welcomeView.openProjectButton.setBackground(new Color(12, 182, 41)); 181 | SwingUtilities.updateComponentTreeUI(this); 182 | editorView.setFont(editorFont); 183 | projectView.refreshTree(); 184 | } catch (UnsupportedLookAndFeelException ex) { 185 | throw new RuntimeException(ex); 186 | } 187 | }); 188 | 189 | monokaiItem.addActionListener(e -> editorView.setColorScheme("Monokai")); 190 | eclipseItem.addActionListener(e -> editorView.setColorScheme("Eclipse")); 191 | nightItem.addActionListener(e -> editorView.setColorScheme("Night")); 192 | redItem.addActionListener(e -> editorView.setColorScheme("Red")); 193 | blueItem.addActionListener(e -> editorView.setColorScheme("Blue")); 194 | purpleItem.addActionListener(e -> editorView.setColorScheme("Purple")); 195 | javaItem.addActionListener(e -> editorView.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA)); 196 | pythonItem.addActionListener(e -> editorView.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_PYTHON)); 197 | cItem.addActionListener(e -> editorView.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_C)); 198 | jsItem.addActionListener(e -> editorView.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT)); 199 | autoSaveItem.addActionListener(e -> { 200 | if (autoSave) { 201 | autoSave = false; 202 | autoSaveItem.setText("Auto save : Off"); 203 | saveFileButton.setVisible(true); 204 | autoSaveTimer.stop(); 205 | } 206 | else { 207 | autoSave = true; 208 | autoSaveItem.setText("Auto save : On"); 209 | saveFileButton.setVisible(false); 210 | autoSaveTimer.start(); 211 | } 212 | }); 213 | projectViewItem.addActionListener(e -> { 214 | if (projectViewEnabled) { 215 | projectViewEnabled = false; 216 | projectView.setVisible(false); 217 | rootPanel.setDividerLocation(0); 218 | projectViewItem.setText("Project view : Disabled"); 219 | } else { 220 | projectViewEnabled = true; 221 | projectView.setVisible(true); 222 | 223 | rootPanel.setDividerLocation(200); 224 | projectViewItem.setText("Project view : Enabled"); 225 | 226 | revalidate(); 227 | repaint(); 228 | } 229 | }); 230 | exitItem.addActionListener(e -> System.exit(0)); 231 | } 232 | public void addComponent() { 233 | projectView.addComponent(); 234 | 235 | menuBar.add(settingsMenu); 236 | settingsMenu.add(newProjectItem); 237 | settingsMenu.add(closeProjectItem); 238 | 239 | settingsMenu.addSeparator(); 240 | settingsMenu.add(themeItem); 241 | settingsMenu.add(colorSchemeItem); 242 | settingsMenu.addSeparator(); 243 | settingsMenu.add(languageItem); 244 | settingsMenu.add(autoSaveItem); 245 | settingsMenu.add(projectViewItem); 246 | settingsMenu.addSeparator(); 247 | 248 | themeItem.add(darkThemeItem); 249 | themeItem.add(lightThemeItem); 250 | 251 | colorSchemeItem.add(monokaiItem); 252 | colorSchemeItem.add(eclipseItem); 253 | colorSchemeItem.add(nightItem); 254 | colorSchemeItem.add(redItem); 255 | colorSchemeItem.add(blueItem); 256 | colorSchemeItem.add(purpleItem); 257 | 258 | languageItem.add(javaItem); 259 | languageItem.add(pythonItem); 260 | languageItem.add(cItem); 261 | languageItem.add(jsItem); 262 | 263 | settingsMenu.add(exitItem); 264 | 265 | toolPanel.add(openTerminalButton); 266 | toolPanel.add(saveFileButton); 267 | 268 | this.add(rootPanel, BorderLayout.CENTER); 269 | this.add(welcomeView); 270 | setJMenuBar(menuBar); 271 | 272 | revalidate(); 273 | repaint(); 274 | 275 | setVisible(true); 276 | } 277 | 278 | public void launch() { 279 | setContentPane(rootPanel); 280 | 281 | this.setExtendedState(MAXIMIZED_BOTH); 282 | editorView.setColorScheme("Monokai"); 283 | } 284 | 285 | public static void main(String[] args){ 286 | FlatMacDarkLaf.setup(); 287 | FlatJetBrainsMonoFont.install(); 288 | FlatInterFont.install(); 289 | 290 | UIManager.put("defaultFont", new Font(FlatInterFont.FAMILY, Font.PLAIN, 13)); 291 | 292 | SwingUtilities.invokeLater(() -> { 293 | 294 | App app = new App(); 295 | app.init(); 296 | app.addComponent(); 297 | }); 298 | } 299 | 300 | 301 | } 302 | -------------------------------------------------------------------------------- /src/main/java/CustomNode.java: -------------------------------------------------------------------------------- 1 | import javax.swing.tree.DefaultMutableTreeNode; 2 | 3 | public class CustomNode extends DefaultMutableTreeNode{ 4 | 5 | private String nodeName, content, path; 6 | public boolean isDirectory; 7 | 8 | public CustomNode(String name, String content, String path) { 9 | super(name); 10 | this.nodeName = name; 11 | this.content = content; 12 | this.path = path; 13 | } 14 | 15 | public String getNodeName() { 16 | return nodeName; 17 | } 18 | 19 | public String getContent() { 20 | return content; 21 | } 22 | 23 | public String getFilePath() { 24 | return path; 25 | } 26 | 27 | public void setFilePath(String newPath) { 28 | this.path = newPath; 29 | } 30 | 31 | public void setContent(String newContent){ 32 | content = newContent; 33 | } 34 | 35 | public void setNodeName(String newName) { 36 | this.nodeName = newName; 37 | this.setUserObject(newName); 38 | } 39 | 40 | 41 | 42 | } -------------------------------------------------------------------------------- /src/main/java/EditorView.java: -------------------------------------------------------------------------------- 1 | import com.formdev.flatlaf.fonts.jetbrains_mono.FlatJetBrainsMonoFont; 2 | import org.fife.ui.rsyntaxtextarea.*; 3 | import org.fife.ui.rtextarea.RTextScrollPane; 4 | 5 | import javax.swing.*; 6 | import java.awt.*; 7 | import java.awt.event.*; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | 11 | 12 | public class EditorView extends RSyntaxTextArea implements KeyListener{ 13 | 14 | private final RTextScrollPane editorScrollPane; 15 | 16 | InputStream monokaiStream, eclipseStream, nightStream, redStream, blueStream, purpleStream; 17 | Theme monokaiTheme, eclipseTheme, nightTheme, redTheme, blueTheme, purpleTheme; 18 | private final App app; 19 | 20 | public EditorView(App app) { 21 | super(); 22 | this.app = app; 23 | this.addKeyListener(this); 24 | 25 | monokaiStream = EditorView.class.getResourceAsStream("/themes/monokai.xml"); 26 | eclipseStream = EditorView.class.getResourceAsStream("/themes/eclipse.xml"); 27 | nightStream = EditorView.class.getResourceAsStream("/themes/night.xml"); 28 | redStream = EditorView.class.getResourceAsStream("/themes/red.xml"); 29 | blueStream = EditorView.class.getResourceAsStream("/themes/blue.xml"); 30 | purpleStream = EditorView.class.getResourceAsStream("/themes/purple.xml"); 31 | 32 | try { 33 | monokaiTheme = Theme.load(monokaiStream); 34 | eclipseTheme = Theme.load(eclipseStream); 35 | nightTheme = Theme.load(nightStream); 36 | redTheme = Theme.load(redStream); 37 | blueTheme = Theme.load(blueStream); 38 | purpleTheme = Theme.load(purpleStream); 39 | } catch (IOException e) { 40 | throw new RuntimeException(e); 41 | } 42 | 43 | this.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); 44 | this.setCodeFoldingEnabled(true); 45 | this.setRoundedSelectionEdges(true); 46 | 47 | editorScrollPane = new RTextScrollPane(this); 48 | } 49 | 50 | public void setColorScheme(String colorScheme) { 51 | switch (colorScheme) { 52 | case "Monokai" : monokaiTheme.apply(this); 53 | break; 54 | case "Eclipse" : eclipseTheme.apply(this); 55 | break; 56 | case "Night" : nightTheme.apply(this); 57 | break; 58 | case "Red" : redTheme.apply(this); 59 | break; 60 | case "Blue" : blueTheme.apply(this); 61 | break; 62 | case "Purple" : purpleTheme.apply(this); 63 | break; 64 | } 65 | this.setFont(app.editorFont); 66 | } 67 | 68 | 69 | public JScrollPane getContentPanel() { 70 | return editorScrollPane; 71 | } 72 | 73 | private int fontSize = 12; 74 | private static final int MIN_FONT_SIZE = 8; 75 | private static final int MAX_FONT_SIZE = 72; 76 | 77 | @Override 78 | public void keyTyped(KeyEvent e) {} 79 | 80 | @Override 81 | public void keyPressed(KeyEvent e) { 82 | if (e.isControlDown()) { 83 | if (e.getKeyCode() == KeyEvent.VK_PLUS || e.getKeyCode() == KeyEvent.VK_EQUALS) { 84 | if (fontSize < MAX_FONT_SIZE) { 85 | fontSize++; 86 | } 87 | } else if (e.getKeyCode() == KeyEvent.VK_MINUS) { 88 | if (fontSize > MIN_FONT_SIZE) { 89 | fontSize--; 90 | } 91 | } 92 | this.setFont(new Font(FlatJetBrainsMonoFont.FAMILY, Font.PLAIN, fontSize)); 93 | } 94 | } 95 | 96 | @Override 97 | public void keyReleased(KeyEvent e) {} 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/ProjectView.java: -------------------------------------------------------------------------------- 1 | import com.formdev.flatlaf.fonts.inter.FlatInterFont; 2 | 3 | import javax.swing.*; 4 | import javax.swing.tree.DefaultMutableTreeNode; 5 | import javax.swing.tree.DefaultTreeCellRenderer; 6 | import javax.swing.tree.DefaultTreeModel; 7 | import java.awt.*; 8 | import java.awt.event.MouseAdapter; 9 | import java.awt.event.MouseEvent; 10 | import java.io.File; 11 | import java.io.FileNotFoundException; 12 | import java.io.FileWriter; 13 | import java.io.IOException; 14 | import java.util.ArrayList; 15 | import java.util.Objects; 16 | import java.util.Scanner; 17 | 18 | public class ProjectView extends JPanel { 19 | 20 | private final App app; 21 | 22 | public CustomNode root; 23 | private JTree projectTree; 24 | private JScrollPane projectScrollPane; 25 | public String directoryPath; 26 | 27 | public ArrayList projectFiles = new ArrayList<>(); 28 | public String projectPath = null; 29 | 30 | private static Icon folderIcon; 31 | 32 | private JPopupMenu popupMenu; 33 | 34 | public ProjectView(App app) { 35 | this.app = app; 36 | this.setPreferredSize(new Dimension(300, 1200)); 37 | 38 | setLayout(new BorderLayout()); 39 | } 40 | 41 | public void init() { 42 | root = new CustomNode("Project", null, projectPath); 43 | projectTree = new JTree(root); 44 | projectTree.setFont(new Font(FlatInterFont.FAMILY, Font.PLAIN, 16)); 45 | projectScrollPane = new JScrollPane(projectTree); 46 | 47 | folderIcon = new ImageIcon(Objects.requireNonNull 48 | (ProjectView.class.getResource("/icons/folder_icon_24.png"))); 49 | refreshTree(); 50 | createPopupMenu(); 51 | } 52 | 53 | public void initActionListeners() { 54 | projectTree.addMouseListener(new MouseAdapter() { 55 | @Override 56 | public void mouseClicked(MouseEvent e) { 57 | super.mouseClicked(e); 58 | try { 59 | CustomNode node = (CustomNode) projectTree.getLastSelectedPathComponent(); 60 | if (!node.isDirectory) { 61 | setEditorContent(app.editorView, node); 62 | app.saveFileButton.setEnabled(true); 63 | CustomNode parentNode = (CustomNode) node.getParent(); 64 | app.currentFileParentPath = parentNode.getFilePath(); 65 | 66 | } else { 67 | app.saveFileButton.setEnabled(false); 68 | } 69 | } catch (NullPointerException pointerException) { 70 | System.out.println("No File Selected"); 71 | } catch (ClassCastException classCastException) { 72 | System.out.println("Exception"); 73 | } 74 | } 75 | @Override 76 | public void mouseReleased(MouseEvent e) { 77 | if (e.isPopupTrigger()) { 78 | int row = projectTree.getClosestRowForLocation(e.getX(), e.getY()); 79 | projectTree.setSelectionRow(row); 80 | popupMenu.show(e.getComponent(), e.getX(), e.getY()); 81 | } 82 | } 83 | 84 | }); 85 | 86 | } 87 | 88 | public void setEditorContent(EditorView editorView, CustomNode node) { 89 | editorView.setText(node.getContent()); 90 | app.setTitle("CodeLite - " + node.getNodeName()); 91 | } 92 | 93 | public void addComponent() { 94 | this.add(projectScrollPane, BorderLayout.CENTER); 95 | } 96 | 97 | public void openProject() { 98 | JFileChooser chooser = new JFileChooser(); 99 | chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 100 | 101 | int result = chooser.showOpenDialog(null); 102 | File file = chooser.getSelectedFile(); 103 | directoryPath = file.getAbsolutePath(); 104 | 105 | if (result == JFileChooser.APPROVE_OPTION) { 106 | projectPath = file.getAbsolutePath(); 107 | openDirectory(file); 108 | } 109 | } 110 | 111 | public void openDirectory(File inputFile) { 112 | openDirectoryRecursive(inputFile, root); 113 | DefaultTreeModel treeModel = (DefaultTreeModel) projectTree.getModel(); 114 | treeModel.reload(); 115 | } 116 | 117 | private void openDirectoryRecursive(File inputFile, DefaultMutableTreeNode parentNode) { 118 | File[] files = inputFile.listFiles(); 119 | if (files != null) { 120 | for (File file : files) { 121 | CustomNode node; 122 | if (file.isDirectory()) { 123 | node = new CustomNode(file.getName(), "", file.getAbsolutePath()); 124 | node.isDirectory = true; 125 | openDirectoryRecursive(file, node); 126 | } else { 127 | try { 128 | Scanner scanner = new Scanner(file); 129 | StringBuilder data = new StringBuilder(); 130 | while (scanner.hasNextLine()) { 131 | data.append(scanner.nextLine()).append("\n"); 132 | } 133 | scanner.close(); 134 | node = new CustomNode(file.getName(), data.toString(), file.getAbsolutePath()); 135 | node.isDirectory = false; 136 | projectFiles.add(node); 137 | 138 | } catch (FileNotFoundException e) { 139 | System.err.println("File not found: " + file.getAbsolutePath()); 140 | continue; 141 | } 142 | } 143 | parentNode.add(node); 144 | } 145 | } 146 | 147 | } 148 | 149 | public void saveFile() { 150 | CustomNode node = (CustomNode) projectTree.getLastSelectedPathComponent(); 151 | if (node == null) { 152 | JOptionPane.showMessageDialog(null, "No file selected.", "Error", JOptionPane.ERROR_MESSAGE); 153 | return; 154 | } 155 | 156 | node.setContent(app.editorView.getText()); 157 | 158 | File savedFile = new File(node.getFilePath()); 159 | try (FileWriter writer = new FileWriter(savedFile)) { 160 | writer.write(node.getContent()); 161 | } catch (IOException e) { 162 | JOptionPane.showMessageDialog(this, 163 | "Error saving file: " + savedFile.getName() + "\n" + e.getMessage(), 164 | "Save Error", 165 | JOptionPane.ERROR_MESSAGE); 166 | } 167 | } 168 | 169 | private void createPopupMenu() { 170 | popupMenu = new JPopupMenu(); 171 | 172 | JMenuItem addFileItem = new JMenuItem("New File"); 173 | JMenuItem addFolderItem = new JMenuItem("New Folder"); 174 | JMenuItem deleteItem = new JMenuItem("Delete"); 175 | JMenuItem renameItem = new JMenuItem("Rename"); 176 | 177 | addFileItem.addActionListener(e -> createFile(false)); 178 | addFolderItem.addActionListener(e -> createFile(true)); 179 | deleteItem.addActionListener(e -> deleteFile()); 180 | renameItem.addActionListener(e -> renameFile()); 181 | 182 | popupMenu.add(addFileItem); 183 | popupMenu.add(addFolderItem); 184 | popupMenu.add(deleteItem); 185 | popupMenu.add(renameItem); 186 | } 187 | 188 | private void createFile(boolean isDirectory) { 189 | CustomNode selectedNode = (CustomNode) projectTree.getLastSelectedPathComponent(); 190 | File parentFile; 191 | CustomNode newNode; 192 | 193 | if (!isDirectory) { 194 | String fileName = JOptionPane.showInputDialog(null, "Enter file name"); 195 | try { 196 | parentFile = new File(selectedNode.getFilePath()); 197 | } catch (NullPointerException e) { 198 | parentFile = new File(projectPath); 199 | } 200 | 201 | File newFile = new File(parentFile.getAbsolutePath(), fileName); 202 | newNode = new CustomNode(fileName, "", newFile.getAbsolutePath()); 203 | 204 | if (parentFile.isDirectory()) selectedNode.add(newNode); 205 | else root.add(newNode); 206 | } else { 207 | String folderName = JOptionPane.showInputDialog(null, "Enter folder name"); 208 | try { 209 | parentFile = new File(selectedNode.getFilePath()); 210 | } catch (NullPointerException e) { 211 | parentFile = new File(projectPath); 212 | } 213 | 214 | File newFolder = new File(parentFile.getAbsolutePath(), folderName); 215 | boolean folderCreated = newFolder.mkdir(); 216 | if (!folderCreated) JOptionPane.showMessageDialog(null, "Unable to create folder", 217 | "Error", JOptionPane.ERROR_MESSAGE); 218 | else { 219 | newNode = new CustomNode(folderName, null, newFolder.getAbsolutePath()); 220 | 221 | if (parentFile.isDirectory()) selectedNode.add(newNode); 222 | else root.add(newNode); 223 | } 224 | } 225 | refreshTree(); 226 | } 227 | 228 | private void deleteFile() { 229 | CustomNode selectedNode = (CustomNode) projectTree.getLastSelectedPathComponent(); 230 | try { 231 | File selectedFile = new File(selectedNode.getFilePath()); 232 | if (selectedFile.exists()) { 233 | boolean deleted; 234 | if (selectedFile.isDirectory()) deleted = deleteDirectory(selectedFile); 235 | else deleted = selectedFile.delete(); 236 | 237 | 238 | if (deleted) { 239 | DefaultTreeModel model = (DefaultTreeModel) projectTree.getModel(); 240 | model.removeNodeFromParent(selectedNode); 241 | model.reload(); 242 | } else { 243 | JOptionPane.showMessageDialog(null, 244 | "Could not delete the file/folder", 245 | "Error", 246 | JOptionPane.ERROR_MESSAGE); 247 | } 248 | } 249 | } catch (NullPointerException e) { 250 | JOptionPane.showMessageDialog(null, "Cannot delete opened project", "Error", JOptionPane.ERROR_MESSAGE); 251 | } 252 | 253 | } 254 | 255 | private boolean deleteDirectory(File directory) { 256 | File[] files = directory.listFiles(); 257 | if (files != null) { 258 | for (File file : files) { 259 | if (file.isDirectory()) { 260 | deleteDirectory(file); 261 | } else { 262 | file.delete(); 263 | } 264 | } 265 | } 266 | return directory.delete(); 267 | } 268 | 269 | private void renameFile() { 270 | CustomNode selectedNode = (CustomNode) projectTree.getLastSelectedPathComponent(); 271 | 272 | if (selectedNode.isDirectory) JOptionPane.showMessageDialog(null, "Cannot rename folders", "Error", JOptionPane.ERROR_MESSAGE); 273 | else { 274 | 275 | String newName = JOptionPane.showInputDialog(null, "Enter new name", selectedNode.getNodeName()); 276 | if (newName == null || newName.trim().isEmpty()) { 277 | return; 278 | } 279 | 280 | File currentFile = new File(selectedNode.getFilePath()); 281 | File parentFile = currentFile.getParentFile(); 282 | File newFile = new File(parentFile, newName); 283 | 284 | if (currentFile.renameTo(newFile)) { 285 | 286 | selectedNode.setNodeName(newName); 287 | selectedNode.setFilePath(newFile.getAbsolutePath()); 288 | 289 | 290 | DefaultTreeModel model = (DefaultTreeModel) projectTree.getModel(); 291 | model.nodeChanged(selectedNode); 292 | 293 | refreshTree(); 294 | } else { 295 | JOptionPane.showMessageDialog(null, 296 | "Could not rename the file", 297 | "Rename Error", 298 | JOptionPane.ERROR_MESSAGE); 299 | } 300 | } 301 | } 302 | 303 | public void refreshTree() { 304 | DefaultTreeModel model = (DefaultTreeModel) projectTree.getModel(); 305 | DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) projectTree.getCellRenderer(); 306 | renderer.setClosedIcon(folderIcon); 307 | renderer.setOpenIcon(folderIcon); 308 | model.reload(); 309 | } 310 | 311 | public JTree getProjectTree() { 312 | return projectTree; 313 | } 314 | 315 | public CustomNode getSelectedCustomNode() { 316 | return (CustomNode) projectTree.getLastSelectedPathComponent(); 317 | } 318 | 319 | } 320 | -------------------------------------------------------------------------------- /src/main/java/WelcomeView.java: -------------------------------------------------------------------------------- 1 | import com.formdev.flatlaf.fonts.jetbrains_mono.FlatJetBrainsMonoFont; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.awt.event.ComponentEvent; 6 | import java.awt.event.ComponentListener; 7 | 8 | public class WelcomeView extends JPanel implements ComponentListener { 9 | 10 | private final App app; 11 | 12 | private final JLabel titleLabel; 13 | private final JLabel mottoLabel; 14 | public JButton openProjectButton; 15 | 16 | GradientPaint gradient; 17 | 18 | int titleWidth = 400, titleHeight = 200, mottoWidth = 400, mottoHeight = 100; 19 | 20 | public WelcomeView(App app) { 21 | this.app = app; 22 | this.addComponentListener(this); 23 | this.setBounds(0, 0, app.getWidth(), app.getHeight()); 24 | this.setLayout(null); 25 | this.setOpaque(false); 26 | 27 | titleLabel = new JLabel("{CodeLite}"); 28 | titleLabel.setFont(new Font(FlatJetBrainsMonoFont.FAMILY, Font.BOLD, 52)); 29 | titleLabel.setForeground(Color.WHITE); 30 | titleLabel.setHorizontalAlignment(SwingConstants.CENTER); 31 | 32 | mottoLabel = new JLabel("Code editing simplified"); 33 | mottoLabel.setFont(new Font(FlatJetBrainsMonoFont.FAMILY, Font.PLAIN, 24)); 34 | mottoLabel.setForeground(Color.WHITE); 35 | mottoLabel.setHorizontalAlignment(SwingConstants.CENTER); 36 | 37 | openProjectButton = new JButton("Open Project"); 38 | openProjectButton.setForeground(Color.WHITE); 39 | openProjectButton.setFont(new Font(FlatJetBrainsMonoFont.FAMILY, Font.PLAIN, 18)); 40 | 41 | openProjectButton.setBackground(new Color(12, 100, 181)); 42 | 43 | openProjectButton.addActionListener(e -> { 44 | app.projectView.openProject(); 45 | app.launch(); 46 | }); 47 | 48 | this.add(titleLabel); 49 | this.add(mottoLabel); 50 | this.add(openProjectButton); 51 | } 52 | @Override 53 | public void paintComponent(Graphics g) { 54 | super.paintComponent(g); 55 | Graphics2D g2D = (Graphics2D) g; 56 | 57 | if (app.darkTheme) { 58 | gradient = new GradientPaint(0, 0, new Color(32, 32, 32), getWidth(), getHeight(), new Color(40, 40, 40)); 59 | } else { 60 | gradient = new GradientPaint(0, 0, new Color(6, 74, 181), getWidth(), getHeight(), new Color(84, 166, 251)); 61 | } 62 | g2D.setPaint(gradient); 63 | g2D.fillRect(0, 0, getWidth(), getHeight()); 64 | } 65 | 66 | @Override 67 | public void componentResized(ComponentEvent e) { 68 | if (titleLabel != null && mottoLabel!=null && openProjectButton!= null) { 69 | titleLabel.setBounds(getWidth() / 2 - titleWidth / 2, 70 | (getHeight() / 2 - titleHeight / 2) - 50, 71 | titleWidth, titleHeight); 72 | mottoLabel.setBounds(getWidth() / 2 - mottoWidth / 2, 73 | titleLabel.getY() + titleHeight / 2, 74 | mottoWidth, mottoHeight); 75 | openProjectButton.setBounds(getWidth() / 2 - titleWidth / 4, 76 | mottoLabel.getY() + mottoHeight, 77 | titleWidth / 2, mottoHeight / 2); 78 | } 79 | } 80 | 81 | @Override 82 | public void componentMoved(ComponentEvent e) { 83 | 84 | } 85 | 86 | @Override 87 | public void componentShown(ComponentEvent e) { 88 | 89 | } 90 | 91 | @Override 92 | public void componentHidden(ComponentEvent e) { 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: App 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/icons/folder_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gufranthakur/CodeLite/31202dd9420bb93564dbdbdbacfbecd256178879/src/main/resources/icons/folder_icon.png -------------------------------------------------------------------------------- /src/main/resources/icons/folder_icon_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gufranthakur/CodeLite/31202dd9420bb93564dbdbdbacfbecd256178879/src/main/resources/icons/folder_icon_24.png -------------------------------------------------------------------------------- /src/main/resources/themes/blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |