Skip to content

Commit a7fb93c

Browse files
committed
Updated and fixed Build Errors
1 parent 6fef028 commit a7fb93c

56 files changed

Lines changed: 549 additions & 151 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/main/java/com/tarek/notetool/Board.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,9 @@ public void displayBoard() {
198198
});
199199
System.out.println("\n========================================");
200200
}
201+
202+
@Override
203+
public String toString() {
204+
return name; // Important for ComboBox display
205+
}
201206
}

src/main/java/com/tarek/notetool/MainViewController.java

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,20 @@ private VBox createColumn(Column boardColumn) {
422422
Dragboard db = event.getDragboard();
423423
boolean success = false;
424424
if (db.hasString()) {
425-
UUID noteId = UUID.fromString(db.getString());
426-
// Dropping on a column moves the note to that column.
427-
handleReorderNote(noteId, boardColumn.getId(), -1); // -1 means add to end
428-
success = true;
425+
String content = db.getString();
426+
try {
427+
// Try to parse as a UUID for note reordering
428+
UUID noteId = UUID.fromString(content);
429+
// Dropping on a column moves the note to that column.
430+
handleReorderNote(noteId, boardColumn.getId(), -1); // -1 means add to end
431+
success = true;
432+
} catch (IllegalArgumentException e) {
433+
// If it's not a UUID, treat it as text to create a new note
434+
if (!boardColumn.getName().equalsIgnoreCase("Archived")) {
435+
handleNewNoteWithTitle(boardColumn, content);
436+
success = true;
437+
}
438+
}
429439
}
430440
event.setDropCompleted(success);
431441
event.consume();
@@ -1130,7 +1140,11 @@ public void setupShortcuts(Scene scene) {
11301140
);
11311141
scene.getAccelerators().put(
11321142
new KeyCodeCombination(KeyCode.N, KeyCombination.CONTROL_DOWN),
1133-
() -> showInfo("Quick Add", "Quick Add can be accessed from the Welcome screen.")
1143+
() -> {
1144+
if (noteManager != null && boardScrollPane.getScene() != null) {
1145+
QuickAddViewController.show(noteManager, (Stage) boardScrollPane.getScene().getWindow());
1146+
}
1147+
}
11341148
);
11351149
}
11361150

@@ -1158,6 +1172,43 @@ private void showInfo(String header, String content) {
11581172
alert.showAndWait();
11591173
}
11601174

1175+
private void handleNewNoteWithTitle(Column column, String title) {
1176+
Dialog<String> dialog = new Dialog<>();
1177+
dialog.setTitle("New Note");
1178+
dialog.setHeaderText("Create a new note in the " + column.getName() + " column");
1179+
1180+
DialogPane dialogPane = dialog.getDialogPane();
1181+
dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
1182+
1183+
TextField titleField = new TextField(title); // Pre-fill the title
1184+
titleField.setPromptText("Enter note title");
1185+
dialogPane.setContent(titleField);
1186+
1187+
final Button okButton = (Button) dialogPane.lookupButton(ButtonType.OK);
1188+
okButton.setDisable(title.trim().isEmpty());
1189+
titleField.textProperty().addListener((observable, oldValue, newValue) -> {
1190+
okButton.setDisable(newValue.trim().isEmpty());
1191+
});
1192+
1193+
Platform.runLater(titleField::requestFocus);
1194+
1195+
dialog.setResultConverter(dialogButton -> dialogButton == ButtonType.OK ? titleField.getText() : null);
1196+
1197+
Optional<String> result = dialog.showAndWait();
1198+
1199+
result.ifPresent(newTitle -> {
1200+
Note newNote = new Note(newTitle, "");
1201+
newNote.setColumnId(column.getId());
1202+
if (noteManager.getCurrentUser() != null) {
1203+
newNote.setAssignees(List.of(noteManager.getCurrentUser()));
1204+
}
1205+
currentBoard.addNote(newNote);
1206+
noteManager.markAsDirty();
1207+
noteContainersMap.get(column.getId()).getChildren().add(createNoteCard(newNote));
1208+
updateColumnCounts();
1209+
});
1210+
}
1211+
11611212
private void showNoteDetailView(Note note, VBox noteCard) {
11621213
try {
11631214
// Load the fxml file and create a new stage for the popup dialog.

src/main/java/com/tarek/notetool/Note.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,24 @@ public Note duplicate() {
288288
return newNote;
289289
}
290290

291+
/**
292+
* Creates a template from this note.
293+
* A template is a new note with a new ID, but it preserves the title, content,
294+
* goals, tags, and attachments. It does NOT copy assignees, comments, due dates,
295+
* or dependencies, as these are instance-specific.
296+
* @return A new `Note` instance suitable for use as a template.
297+
*/
298+
public Note asTemplate() {
299+
Note templateNote = new Note(this.title, this.content);
300+
templateNote.setPriority(this.priority);
301+
templateNote.setTags(this.tags);
302+
templateNote.setAttachmentPaths(this.attachmentPaths);
303+
templateNote.setReferenceImagePaths(this.referenceImagePaths);
304+
// Deep copy goals
305+
templateNote.setGoals(this.goals.stream().map(Goal::new).collect(Collectors.toList()));
306+
return templateNote;
307+
}
308+
291309
// --- Getters and Setters ---
292310

293311
public UUID getId() {

src/main/java/com/tarek/notetool/NoteDetailViewController.java

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ public record EditorResult(Note savedNote, Set<String> newTags) {
152152
@FXML
153153
private Button copyLinkButton;
154154

155+
@FXML
156+
private Button saveAsTemplateButton;
157+
155158
private Stage dialogStage;
156159
private Note noteCopy; // The editable copy of the note
157160
private Note initialNoteState; // A snapshot of the note's state when the editor was opened
@@ -306,6 +309,13 @@ protected void updateItem(Note.Comment item, boolean empty) {
306309
setupReferencePaneDragAndDrop();
307310
}
308311

312+
// --- NEW: Save as Template Button ---
313+
if (saveAsTemplateButton != null) {
314+
saveAsTemplateButton.setGraphic(new FontIcon(MaterialDesignC.CONTENT_SAVE_COG_OUTLINE));
315+
Tooltip.install(saveAsTemplateButton, new Tooltip("Save as Template"));
316+
saveAsTemplateButton.setOnAction(e -> handleSaveAsTemplate());
317+
}
318+
309319
// --- Debounced Markdown Rendering ---
310320
// This PauseTransition ensures we don't re-render the markdown on every single keystroke,
311321
// which would be inefficient. Instead, it waits for a brief pause in typing.
@@ -776,29 +786,59 @@ protected void updateItem(String item, boolean empty) {
776786
attachmentsListView.setContextMenu(contextMenu);
777787
}
778788

789+
private void setupAttachmentPaneDragAndDrop() {
790+
attachmentsListView.setOnDragOver(event -> {
791+
if (event.getGestureSource() != attachmentsListView && event.getDragboard().hasFiles()) {
792+
event.acceptTransferModes(TransferMode.COPY);
793+
}
794+
event.consume();
795+
});
796+
797+
attachmentsListView.setOnDragDropped(event -> {
798+
Dragboard db = event.getDragboard();
799+
boolean success = false;
800+
if (db.hasFiles()) {
801+
db.getFiles().forEach(file -> {
802+
try {
803+
addAttachmentFromFile(file);
804+
} catch (IOException e) {
805+
showError("Attachment Failed", "Could not attach the dragged file. Error: " + e.getMessage());
806+
}
807+
});
808+
success = true;
809+
}
810+
event.setDropCompleted(success);
811+
event.consume();
812+
});
813+
}
814+
779815
private void handleAddAttachment() {
780816
FileChooser fileChooser = new FileChooser();
781817
fileChooser.setTitle("Attach File");
782818
File selectedFile = fileChooser.showOpenDialog(dialogStage);
783819

784820
if (selectedFile != null) {
785821
try {
786-
Path attachmentsDir = MainApp.getAttachmentsDirectory();
787-
// Create a unique filename to prevent collisions, but keep the original name for context
788-
String uniqueFileName = UUID.randomUUID().toString().substring(0, 8) + "-" + selectedFile.getName();
789-
Path targetPath = attachmentsDir.resolve(uniqueFileName);
790-
791-
Files.copy(selectedFile.toPath(), targetPath, StandardCopyOption.REPLACE_EXISTING);
792-
793-
tempAttachmentPaths.add(uniqueFileName);
794-
attachmentsListView.getItems().add(uniqueFileName);
795-
822+
addAttachmentFromFile(selectedFile);
796823
} catch (IOException e) {
797824
showError("Attachment Failed", "Could not attach the file. Error: " + e.getMessage());
798825
}
799826
}
800827
}
801828

829+
private void addAttachmentFromFile(File file) throws IOException {
830+
if (file == null) return;
831+
832+
Path attachmentsDir = MainApp.getAttachmentsDirectory();
833+
// Create a unique filename to prevent collisions, but keep the original name for context
834+
String uniqueFileName = UUID.randomUUID().toString().substring(0, 8) + "-" + file.getName();
835+
Path targetPath = attachmentsDir.resolve(uniqueFileName);
836+
837+
Files.copy(file.toPath(), targetPath, StandardCopyOption.REPLACE_EXISTING);
838+
839+
tempAttachmentPaths.add(uniqueFileName);
840+
attachmentsListView.getItems().add(uniqueFileName);
841+
}
802842
/**
803843
* Handles text input in the main content area to provide image suggestions.
804844
* When the user types '@' followed by text, it shows a popup with matching
@@ -1040,6 +1080,22 @@ private void handleAddDependency() {
10401080
});
10411081
}
10421082

1083+
private void handleSaveAsTemplate() {
1084+
if (noteManager == null) {
1085+
showError("Error", "Cannot save template. NoteManager is not available.");
1086+
return;
1087+
}
1088+
// Use the current state of the note copy to create a template
1089+
Note templateNote = noteCopy.asTemplate();
1090+
noteManager.saveNoteAsTemplate(templateNote);
1091+
1092+
// Provide user feedback
1093+
Alert info = new Alert(Alert.AlertType.INFORMATION);
1094+
info.initOwner(dialogStage);
1095+
info.setTitle("Template Saved");
1096+
info.setHeaderText("Note template '" + templateNote.getTitle() + "' was saved successfully.");
1097+
info.showAndWait();
1098+
}
10431099
private Set<String> findNewTags() {
10441100
Set<String> newTags = new HashSet<>();
10451101
if (tagComboBox != null) {

src/main/java/com/tarek/notetool/NoteManager.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public class NoteManager {
4747
private User currentUser;
4848
private final Set<String> allTags;
4949
private final List<String> galleryImagePaths;
50+
private final List<Note> noteTemplates;
5051
private transient boolean isDirty = false;
5152
private transient Set<Path> loadedBoardFiles = new HashSet<>();
5253

@@ -68,6 +69,7 @@ public NoteManager() {
6869
this.currentUser = new User("Default User");
6970
this.allTags = new HashSet<>();
7071
this.galleryImagePaths = new ArrayList<>();
72+
this.noteTemplates = new ArrayList<>();
7173
}
7274

7375
public void markAsDirty() {
@@ -124,6 +126,24 @@ public boolean removeGalleryImagePath(String imagePath) {
124126
return false;
125127
}
126128

129+
public void saveNoteAsTemplate(Note noteTemplate) {
130+
if (noteTemplate != null) {
131+
this.noteTemplates.add(noteTemplate);
132+
markAsDirty();
133+
}
134+
}
135+
136+
public List<Note> getNoteTemplates() {
137+
return Collections.unmodifiableList(noteTemplates);
138+
}
139+
140+
public void deleteNoteTemplate(Note noteTemplate) {
141+
if (noteTemplate != null) {
142+
this.noteTemplates.remove(noteTemplate);
143+
markAsDirty();
144+
}
145+
}
146+
127147
/**
128148
* Creates a new board and adds it to the manager.
129149
* @param boardName The name of the new board.
@@ -189,14 +209,37 @@ public Board duplicateBoard(String originalBoardName) {
189209
}
190210

191211
// Create the new board with the same members
192-
Board newBoard = new Board(newBoardName, originalBoard.getMembers(), false);
212+
Board newBoard = new Board(newBoardName, originalBoard.getMembers(), true); // Start with default columns
193213

194214
// Duplicate all notes from the original board to the new one
195-
for (Note originalNote : originalBoard.getAllNotes()) {
215+
originalBoard.getAllNotes().forEach(originalNote -> {
196216
Note newNote = originalNote.duplicate();
197217
newBoard.addNote(newNote);
218+
});
219+
220+
boards.put(newBoardName, newBoard);
221+
markAsDirty();
222+
return newBoard;
223+
}
224+
225+
/**
226+
* Creates a new board from a template board.
227+
* @param newBoardName The name for the new board.
228+
* @param templateBoard The board to use as a template.
229+
* @return The newly created board.
230+
*/
231+
public Board createBoardFromTemplate(String newBoardName, Board templateBoard) {
232+
if (boards.containsKey(newBoardName)) {
233+
throw new IllegalArgumentException("A board with the name '" + newBoardName + "' already exists.");
198234
}
199235

236+
Board newBoard = new Board(newBoardName, templateBoard.getMembers(), false); // Not a 'new' board in the default sense
237+
newBoard.setColumns(templateBoard.getColumns().stream().map(c -> new Column(c.getName())).collect(Collectors.toList()));
238+
239+
templateBoard.getAllNotes().forEach(templateNote -> {
240+
newBoard.addNote(templateNote.duplicate());
241+
});
242+
200243
boards.put(newBoardName, newBoard);
201244
markAsDirty();
202245
return newBoard;
@@ -410,6 +453,7 @@ public void saveToDirectory(Path dataDirectory) throws IOException {
410453
settings.allTags = this.allTags;
411454
settings.recentNoteIds = this.recentNoteIds;
412455
settings.galleryImagePaths = this.galleryImagePaths;
456+
settings.noteTemplates = this.noteTemplates;
413457

414458
try (Writer writer = new FileWriter(settingsFile.toFile())) {
415459
gson.toJson(settings, writer);
@@ -467,6 +511,7 @@ public static NoteManager loadFromDirectory(Path dataDirectory) throws IOExcepti
467511
if (settings.allTags != null) manager.allTags.addAll(settings.allTags);
468512
if (settings.recentNoteIds != null) manager.recentNoteIds.addAll(settings.recentNoteIds);
469513
if (settings.galleryImagePaths != null) manager.galleryImagePaths.addAll(settings.galleryImagePaths);
514+
if (settings.noteTemplates != null) manager.noteTemplates.addAll(settings.noteTemplates);
470515
}
471516
} catch (Exception e) {
472517
System.err.println("Failed to parse preferences.json, using defaults. " + e.getMessage());
@@ -638,5 +683,6 @@ private static class NoteManagerSettings {
638683
Set<String> allTags;
639684
Deque<UUID> recentNoteIds;
640685
List<String> galleryImagePaths;
686+
List<Note> noteTemplates;
641687
}
642688
}

0 commit comments

Comments
 (0)