Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

A lightweight and modern Markdown editor built with JavaFX.

![An illustration of the Main interface of MarkNote](src/docs/illustrations/marknote-0.1.1-screenshot-002.png "The Main interface for MarkNote")
![An illustration of the Main interface of MarkNote](src/docs/illustrations/marknote-0.1.0-screenshot-002.png "The Main interface for MarkNote")

**Author:** Frédéric Delorme (McG) - [contact.snapgames@gmail.com](mailto:contact.snapgames@gmail.com)

Expand Down
5 changes: 3 additions & 2 deletions build
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ UNIXLAUNCHER_NOJRE_START
echo "Copying application icons..."
mkdir -p "${DIST_DIR}/icons"
cp ${SRC}/main/resources/images/icons/marknote.svg "${DIST_DIR}/icons/" 2>/dev/null || true
cp ${SRC}/main/resources/images/icons/marknote.ico "${DIST_DIR}/icons/" 2>/dev/null || true
cp ${SRC}/main/resources/images/icons/marknote-128.png "${DIST_DIR}/icons/" 2>/dev/null || true
cp ${SRC}/main/resources/images/icons/marknote-64.png "${DIST_DIR}/icons/" 2>/dev/null || true
cp ${SRC}/main/resources/images/icons/marknote-32.png "${DIST_DIR}/icons/" 2>/dev/null || true
Expand Down Expand Up @@ -448,11 +449,11 @@ copy /Y "%SCRIPT_DIR%%APP_NAME%.bat" "%INSTALL_DIR%\"

:: Create Desktop shortcut using PowerShell
echo Creating Desktop shortcut...
powershell -NoProfile -Command "$ws = New-Object -ComObject WScript.Shell; $sc = $ws.CreateShortcut([System.IO.Path]::Combine([Environment]::GetFolderPath('Desktop'), '%APP_NAME%.lnk')); $sc.TargetPath = '%INSTALL_DIR%\%APP_NAME%.bat'; $sc.WorkingDirectory = '%INSTALL_DIR%'; $sc.IconLocation = '%INSTALL_DIR%\icons\marknote-128.png'; $sc.Description = 'MarkNote - Markdown Note Editor'; $sc.Save()"
powershell -NoProfile -Command "$ws = New-Object -ComObject WScript.Shell; $sc = $ws.CreateShortcut([System.IO.Path]::Combine([Environment]::GetFolderPath('Desktop'), '%APP_NAME%.lnk')); $sc.TargetPath = '%INSTALL_DIR%\%APP_NAME%.bat'; $sc.WorkingDirectory = '%INSTALL_DIR%'; $sc.IconLocation = '%INSTALL_DIR%\icons\marknote.ico'; $sc.Description = 'MarkNote - Markdown Note Editor'; $sc.Save()"

:: Create Start Menu shortcut
echo Creating Start Menu shortcut...
powershell -NoProfile -Command "$ws = New-Object -ComObject WScript.Shell; $sc = $ws.CreateShortcut('%START_MENU%\%APP_NAME%.lnk'); $sc.TargetPath = '%INSTALL_DIR%\%APP_NAME%.bat'; $sc.WorkingDirectory = '%INSTALL_DIR%'; $sc.IconLocation = '%INSTALL_DIR%\icons\marknote-128.png'; $sc.Description = 'MarkNote - Markdown Note Editor'; $sc.Save()"
powershell -NoProfile -Command "$ws = New-Object -ComObject WScript.Shell; $sc = $ws.CreateShortcut('%START_MENU%\%APP_NAME%.lnk'); $sc.TargetPath = '%INSTALL_DIR%\%APP_NAME%.bat'; $sc.WorkingDirectory = '%INSTALL_DIR%'; $sc.IconLocation = '%INSTALL_DIR%\icons\marknote.ico'; $sc.Description = 'MarkNote - Markdown Note Editor'; $sc.Save()"

echo.
echo =========================================
Expand Down
121 changes: 81 additions & 40 deletions src/main/java/MarkNote.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import config.AppConfig;
import config.ThemeManager;
import ui.BasePanel;
import ui.ConsolePanel;
import ui.DockingManager;
import ui.DockZone;
import ui.DocumentTab;
import ui.FrontMatterPanel;
import ui.ImagePreviewTab;
Expand All @@ -24,6 +27,7 @@
import utils.DocumentService;
import utils.GitService;
import utils.IndexService;
import utils.LogService;

import javafx.application.Application;
import javafx.application.Platform;
Expand Down Expand Up @@ -70,9 +74,12 @@ public class MarkNote extends Application {
private Menu recentMenu;

// Panels et SplitPanes pour la gestion de l'affichage
private SplitPane mainSplit;
private SplitPane editorSplit;
private SplitPane leftSplit;
private DockingManager dockingManager;

private ConsolePanel consolePanel;
private CheckMenuItem showConsoleMenuItem;
private boolean consoleDebugEnabled = false;

private GitService gitService;

Expand All @@ -86,6 +93,17 @@ public void init() {
config = new AppConfig();
config.load();

// Vérifier l'argument --console-debug
for (String arg : getParameters().getRaw()) {
if ("--console-debug".equals(arg)) {
consoleDebugEnabled = true;
break;
}
}

// Initialiser le service de logging
LogService.getInstance().setConsoleDebugEnabled(consoleDebugEnabled);

// Appliquer la langue configurée
String language = config.getLanguage();
if (language != null && !language.equals("system")) {
Expand Down Expand Up @@ -210,23 +228,29 @@ public void start(Stage stage) {
visualLinkPanel.updateDiagram(indexService.getEntries());
});

// Conteneur gauche : explorateur + tag cloud + diagramme réseau
// (redimensionnable)
leftSplit = new SplitPane(projectExplorerPanel, tagCloudPanel, visualLinkPanel);
leftSplit.setOrientation(Orientation.VERTICAL);
leftSplit.setDividerPositions(0.55, 0.78);

// SplitPane éditeur | preview
// SplitPane éditeur | preview (zone centrale)
editorSplit = new SplitPane(mainTabPane, previewPanel);
editorSplit.setOrientation(Orientation.HORIZONTAL);
editorSplit.setDividerPositions(0.5);

// SplitPane principal : explorateur | éditeur/preview
mainSplit = new SplitPane(leftSplit, editorSplit);
mainSplit.setOrientation(Orientation.HORIZONTAL);
mainSplit.setDividerPositions(0.2);
// Initialiser le DockingManager
dockingManager = new DockingManager(root);
dockingManager.setCenterContent(editorSplit);

root.setCenter(mainSplit);
// Dock des panels à gauche
dockingManager.dock(projectExplorerPanel, DockZone.LEFT);
dockingManager.dock(tagCloudPanel, DockZone.LEFT);
dockingManager.dock(visualLinkPanel, DockZone.LEFT);

// Console panel (si --console-debug est passé)
if (consoleDebugEnabled) {
consolePanel = new ConsolePanel();
dockingManager.dock(consolePanel, DockZone.BOTTOM);
consolePanel.startCapture();
}

// Appliquer le layout
root.setCenter(dockingManager.getRootNode());

// Status bar en bas
root.setBottom(statusBar);
Expand Down Expand Up @@ -339,18 +363,10 @@ private MenuBar createMenuBar() {
showProjectPanel.setAccelerator(KeyCombination.keyCombination("Ctrl+E"));
showProjectPanel.setSelected(true);
showProjectPanel.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
// The project explorer and tag cloud are in a vertical SplitPane (leftSplit).
// We need to find it — it's the parent of projectExplorerPanel.
javafx.scene.Parent leftPane = projectExplorerPanel.getParent();
if (leftPane == null)
leftPane = projectExplorerPanel; // fallback
if (isSelected) {
if (!mainSplit.getItems().contains(leftPane)) {
mainSplit.getItems().addFirst(leftPane);
mainSplit.setDividerPositions(0.2);
}
dockingManager.showPanel(projectExplorerPanel, DockZone.LEFT);
} else {
mainSplit.getItems().remove(leftPane);
dockingManager.hidePanel(projectExplorerPanel);
}
});

Expand Down Expand Up @@ -379,13 +395,9 @@ private MenuBar createMenuBar() {
showTagCloud.setSelected(true);
showTagCloud.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
if (isSelected) {
if (!leftSplit.getItems().contains(tagCloudPanel)) {
// Insérer après l'explorateur (index 1) ou en fin
int idx = leftSplit.getItems().indexOf(projectExplorerPanel);
leftSplit.getItems().add(idx + 1, tagCloudPanel);
}
dockingManager.showPanel(tagCloudPanel, DockZone.LEFT);
} else {
leftSplit.getItems().remove(tagCloudPanel);
dockingManager.hidePanel(tagCloudPanel);
}
});

Expand All @@ -397,11 +409,9 @@ private MenuBar createMenuBar() {
showNetworkDiagram.setSelected(true);
showNetworkDiagram.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
if (isSelected) {
if (!leftSplit.getItems().contains(visualLinkPanel)) {
leftSplit.getItems().add(visualLinkPanel);
}
dockingManager.showPanel(visualLinkPanel, DockZone.LEFT);
} else {
leftSplit.getItems().remove(visualLinkPanel);
dockingManager.hidePanel(visualLinkPanel);
}
});

Expand All @@ -415,6 +425,28 @@ private MenuBar createMenuBar() {
viewMenu.getItems().addAll(showProjectPanel, showPreviewPanel, new SeparatorMenuItem(), showTagCloud,
showNetworkDiagram, new SeparatorMenuItem(), showWelcomeItem);

// Option Console (uniquement si --console-debug est actif)
if (consoleDebugEnabled) {
showConsoleMenuItem = new CheckMenuItem(messages.getString("menu.view.console"));
showConsoleMenuItem.setSelected(true);
showConsoleMenuItem.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
if (isSelected) {
dockingManager.showPanel(consolePanel);
} else {
dockingManager.hidePanel(consolePanel);
}
});

// Callback fermeture du panel -> décoche le menu
consolePanel.setOnClose(() -> showConsoleMenuItem.setSelected(false));

// Callback détachement du panel
consolePanel.setOnDetach(() -> detachPanel(consolePanel));

viewMenu.getItems().add(viewMenu.getItems().size() - 1, new SeparatorMenuItem());
viewMenu.getItems().add(viewMenu.getItems().size() - 1, showConsoleMenuItem);
}

// == Menu Aide ==
Menu helpMenu = new Menu(messages.getString("menu.help"));

Expand Down Expand Up @@ -527,16 +559,25 @@ private void detachPanel(BasePanel panel) {
}
}

// Masquer le panel dans le SplitPane
leftSplit.getItems().remove(panel);
// Mémoriser la zone de dock actuelle
DockZone originalZone = dockingManager.getZone(panel);

// Masquer le panel via le DockingManager
dockingManager.undock(panel);

// Décocher le menu console si nécessaire
if (panel == consolePanel && showConsoleMenuItem != null) {
showConsoleMenuItem.setSelected(false);
}

// Créer l'onglet
DetachedPanelTab detachedTab = new DetachedPanelTab(panel);
detachedTab.setOnCloseAction(() -> {
// Réafficher le panel si l'option est activée
if (config.isReattachDiagramOnTabClose() || !leftSplit.getItems().contains(panel)) {
if (!leftSplit.getItems().contains(panel)) {
leftSplit.getItems().add(panel);
// Réafficher le panel dans sa zone d'origine
if (originalZone != null) {
dockingManager.dock(panel, originalZone);
if (panel == consolePanel && showConsoleMenuItem != null) {
showConsoleMenuItem.setSelected(true);
}
}
});
Expand Down
Binary file added src/main/java/tools/IcoConverter.class
Binary file not shown.
161 changes: 161 additions & 0 deletions src/main/java/tools/IcoConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package tools;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;

public class IcoConverter {

// Les 4 tailles standard pour les fichiers ICO Windows
private static final int[] ICON_SIZES = { 16, 32, 48, 256 };

public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java IcoConverter <input_image_path> <output_ico_path>");
return;
}
try {
// Charger votre image source (PNG, JPG, etc.)
BufferedImage image = ImageIO.read(new File(args[0]));

// Créer le fichier ICO avec les 4 tailles
File icoFile = new File(args[1]);
writeMultiSizeIco(image, icoFile, ICON_SIZES);

System.out.println("ICO file with " + ICON_SIZES.length + " sizes created successfully!");
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* Écrit un BufferedImage dans un fichier ICO avec plusieurs tailles
*/
public static void writeMultiSizeIco(BufferedImage sourceImage, File output, int[] sizes) throws IOException {
// Préparer toutes les images redimensionnées
BufferedImage[] images = new BufferedImage[sizes.length];
for (int i = 0; i < sizes.length; i++) {
images[i] = resizeImage(sourceImage, sizes[i], sizes[i]);
images[i] = convertToARGB(images[i]);
}

try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(output))) {
// Écrire l'en-tête ICO
writeIcoHeader(dos, images.length);

// Calculer les offsets pour chaque image
int offset = 6 + (16 * images.length); // 6 bytes header + 16 bytes per directory entry

// Écrire toutes les entrées de répertoire
ByteArrayOutputStream[] imageDataArray = new ByteArrayOutputStream[images.length];
for (int i = 0; i < images.length; i++) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream tempDos = new DataOutputStream(baos);
writeIconData(tempDos, images[i]);
imageDataArray[i] = baos;

writeIconDirEntry(dos, images[i], imageDataArray[i].size(), offset);
offset += imageDataArray[i].size();
}

// Écrire toutes les données d'image
for (ByteArrayOutputStream baos : imageDataArray) {
dos.write(baos.toByteArray());
}
}
}

/**
* Redimensionne une image avec une qualité optimale
*/
private static BufferedImage resizeImage(BufferedImage original, int width, int height) {
BufferedImage resized = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = resized.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(original, 0, 0, width, height, null);
g.dispose();
return resized;
}

/**
* Convertit une image en ARGB
*/
private static BufferedImage convertToARGB(BufferedImage image) {
if (image.getType() == BufferedImage.TYPE_INT_ARGB) {
return image;
}
BufferedImage argbImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = argbImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return argbImage;
}

/**
* Écrit l'en-tête du fichier ICO
*/
private static void writeIcoHeader(DataOutputStream dos, int numImages) throws IOException {
dos.writeShort(0); // Réservé (doit être 0)
dos.writeShort(1); // Type (1 = ICO, 2 = CUR)
dos.writeShort(numImages); // Nombre d'images
}

/**
* Écrit l'entrée de répertoire pour une image
*/
private static void writeIconDirEntry(DataOutputStream dos, BufferedImage image, int dataSize, int offset)
throws IOException {
int width = image.getWidth();
int height = image.getHeight();

dos.writeByte(width == 256 ? 0 : width); // Largeur (0 = 256)
dos.writeByte(height == 256 ? 0 : height); // Hauteur (0 = 256)
dos.writeByte(0); // Nombre de couleurs dans la palette (0 = pas de palette)
dos.writeByte(0); // Réservé
dos.writeShort(1); // Nombre de plans de couleur
dos.writeShort(32); // Bits par pixel
dos.writeInt(dataSize); // Taille des données de l'image
dos.writeInt(offset); // Offset des données de l'image
}

/**
* Écrit les données de l'image (format BMP)
*/
private static void writeIconData(DataOutputStream dos, BufferedImage image) throws IOException {
int width = image.getWidth();
int height = image.getHeight();

// En-tête BMP (BITMAPINFOHEADER)
dos.writeInt(40); // Taille de l'en-tête
dos.writeInt(width); // Largeur
dos.writeInt(height * 2); // Hauteur * 2 (image + masque)
dos.writeShort(1); // Nombre de plans
dos.writeShort(32); // Bits par pixel
dos.writeInt(0); // Compression (0 = aucune)
dos.writeInt(0); // Taille de l'image (0 = non compressée)
dos.writeInt(0); // Résolution horizontale
dos.writeInt(0); // Résolution verticale
dos.writeInt(0); // Nombre de couleurs
dos.writeInt(0); // Nombre de couleurs importantes

// Écrire les pixels (de bas en haut, format BMP)
for (int y = height - 1; y >= 0; y--) {
for (int x = 0; x < width; x++) {
int argb = image.getRGB(x, y);
dos.writeByte((argb) & 0xFF); // Bleu
dos.writeByte((argb >> 8) & 0xFF); // Vert
dos.writeByte((argb >> 16) & 0xFF); // Rouge
dos.writeByte((argb >> 24) & 0xFF); // Alpha
}
}

// Écrire le masque AND (tous les bits à 0 car nous utilisons le canal alpha)
int maskSize = ((width + 31) / 32) * 4 * height;
for (int i = 0; i < maskSize; i++) {
dos.writeByte(0);
}
}
}
Loading
Loading