From e4d996f4a6f588d234ac1ae83dcf62ac4e896209 Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Tue, 16 Jun 2026 17:53:18 +0200 Subject: [PATCH 01/16] feat: add scripts for gui launcher windows, linux, macos --- .gitattributes | 4 + gui/scripts/linux/create-shortcuts.sh | 113 +++++++++++++++++++++++ gui/scripts/linux/launch-gui.sh | 35 +++++++ gui/scripts/macos/create-shortcuts.sh | 113 +++++++++++++++++++++++ gui/scripts/macos/launch-gui.command | 37 ++++++++ gui/scripts/windows/create-shortcuts.ps1 | 80 ++++++++++++++++ gui/scripts/windows/launch-gui.bat | 36 ++++++++ 7 files changed, 418 insertions(+) create mode 100644 gui/scripts/linux/create-shortcuts.sh create mode 100644 gui/scripts/linux/launch-gui.sh create mode 100644 gui/scripts/macos/create-shortcuts.sh create mode 100644 gui/scripts/macos/launch-gui.command create mode 100644 gui/scripts/windows/create-shortcuts.ps1 create mode 100644 gui/scripts/windows/launch-gui.bat diff --git a/.gitattributes b/.gitattributes index 7e7a79ac69..6d1dc4b290 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,9 @@ * eol=lf *.bat eol=crlf +*.ps1 eol=crlf +*.ps2 eol=crlf +*.sh eol=lf +*.command eol=lf *.png binary *.zip binary *.tgz binary diff --git a/gui/scripts/linux/create-shortcuts.sh b/gui/scripts/linux/create-shortcuts.sh new file mode 100644 index 0000000000..e59ff6bf42 --- /dev/null +++ b/gui/scripts/linux/create-shortcuts.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +# IDEasy GUI Shortcut Creator for Linux +# Creates .desktop files for easy GUI launching from the Application Menu and Desktop +# Supports: GNOME, KDE, XFCE, and other freedesktop-compatible environments + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +LAUNCHER_SCRIPT="$SCRIPT_DIR/launch-gui.sh" + +# Set error handling - exit on error but allow catching specific errors +set -o pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +function print_error() { + echo -e "${RED}Error: $1${NC}" >&2 +} + +function print_success() { + echo -e "${GREEN}✓ $1${NC}" +} + +function print_info() { + echo -e "${YELLOW}ℹ $1${NC}" +} + +# Verify launcher script exists and is executable +if [ ! -f "$LAUNCHER_SCRIPT" ]; then + print_error "Launcher script not found: $LAUNCHER_SCRIPT" + exit 1 +fi + +chmod +x "$LAUNCHER_SCRIPT" + +# Create Desktop Entry (.desktop file) +function create_desktop_entry() { + local target_dir="$1" + local desktop_file="$target_dir/ideasy-gui.desktop" + + if [ ! -f "$LAUNCHER_SCRIPT" ]; then + print_error "Launcher script not found: $LAUNCHER_SCRIPT" + return 1 + fi + + if ! mkdir -p "$target_dir" 2>/dev/null; then + print_error "Failed to create directory: $target_dir" + return 1 + fi + + # Use absolute path to ensure it works from anywhere + local absolute_launcher="$(cd "$(dirname "$LAUNCHER_SCRIPT")"; pwd)/$(basename "$LAUNCHER_SCRIPT")" + + if ! cat > "$desktop_file" </dev/null; then + print_error "Failed to make executable: $desktop_file" + return 1 + fi + + print_success "Created: $desktop_file" + return 0 +} + +# Create application menu entry +APPLICATIONS_DIR="$HOME/.local/share/applications" +if ! create_desktop_entry "$APPLICATIONS_DIR"; then + print_info "Note: Could not create application menu entry (may require additional permissions)" +else + print_info "Launch from: Application Menu or Launcher" +fi + +# Create Desktop entry +DESKTOP_DIR="$HOME/Desktop" +if [ -d "$DESKTOP_DIR" ]; then + if ! create_desktop_entry "$DESKTOP_DIR"; then + print_info "Note: Desktop entry creation requires write permissions" + else + print_info "Launch from: Desktop" + fi +else + print_info "Desktop directory not found (Desktop feature may not be available)" +fi + +echo "" +print_success "IDEasy GUI shortcuts ready!" +echo "" +echo "Usage:" +echo " • Open your Application Menu and search for 'IDEasy GUI'" +echo " • Or double-click the shortcut on your Desktop" +echo " • First launch may take longer as Maven downloads dependencies" +echo "" + diff --git a/gui/scripts/linux/launch-gui.sh b/gui/scripts/linux/launch-gui.sh new file mode 100644 index 0000000000..137fb7c468 --- /dev/null +++ b/gui/scripts/linux/launch-gui.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# IDEasy GUI Launcher for Linux +# This script launches the IDEasy GUI using the native 'ide gui' command +# The GUI runs in the background, the script exits immediately + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +if [ ! -f "$PROJECT_ROOT/pom.xml" ]; then + echo "Error: IDEasy project root not found at $PROJECT_ROOT" + echo "Please ensure this script is located in: IDEasy/gui/scripts/linux/" + exit 1 +fi + +if ! command -v ide &> /dev/null; then + echo "" + echo "Error: IDEasy is not installed" + echo "" + echo "Please install IDEasy first:" + echo "https://github.com/devonfw/IDEasy#setup" + echo "" + exit 1 +fi + +cd "$PROJECT_ROOT" + +# Launch IDE GUI in background and exit immediately +# Redirect output to suppress any console messages +ide gui > /dev/null 2>&1 & + +# Give the process a moment to start (may need time for Maven dependencies on first run) +sleep 3 + +exit 0 diff --git a/gui/scripts/macos/create-shortcuts.sh b/gui/scripts/macos/create-shortcuts.sh new file mode 100644 index 0000000000..a7e8431c5c --- /dev/null +++ b/gui/scripts/macos/create-shortcuts.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +# IDEasy GUI Shortcut Creator for macOS +# Creates shortcuts for easy GUI launching from Finder +# Supports creating: Applications folder alias and Desktop alias + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +LAUNCHER_SCRIPT="$SCRIPT_DIR/launch-gui.command" + +# Set error handling - exit on error but allow catching specific errors +set -o pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +function print_error() { + echo -e "${RED}Error: $1${NC}" >&2 +} + +function print_success() { + echo -e "${GREEN}✓ $1${NC}" +} + +function print_info() { + echo -e "${YELLOW}ℹ $1${NC}" +} + +# Verify launcher script exists and is executable +if [ ! -f "$LAUNCHER_SCRIPT" ]; then + print_error "Launcher script not found: $LAUNCHER_SCRIPT" + exit 1 +fi + +chmod +x "$LAUNCHER_SCRIPT" + +# Create an alias/shortcut using osascript (AppleScript) +function create_alias() { + local source="$1" + local target_dir="$2" + local target_name="$3" + + if [ ! -f "$source" ]; then + print_error "Source file not found: $source" + return 1 + fi + + if ! mkdir -p "$target_dir" 2>/dev/null; then + print_error "Failed to create directory: $target_dir" + return 1 + fi + + # Properly escape paths for AppleScript + local escaped_source="$(printf '%s\n' "$source" | sed 's/\\/\\\\/g; s/"/\\"/g')" + local escaped_target_dir="$(printf '%s\n' "$target_dir" | sed 's/\\/\\\\/g; s/"/\\"/g')" + local escaped_target_name="$(printf '%s\n' "$target_name" | sed 's/\\/\\\\/g; s/"/\\"/g')" + + local output + output=$(osascript 2>&1 < IDEasy GUI" +fi + +# Create Desktop alias +DESKTOP_DIR="$HOME/Desktop" +if create_alias "$LAUNCHER_SCRIPT" "$DESKTOP_DIR" "IDEasy GUI"; then + print_info "Launch from: Desktop" +else + print_info "Note: Desktop shortcut creation requires manual steps" + print_info "You can manually create a shortcut by:" + print_info "1. Open Finder" + print_info "2. Go to $SCRIPT_DIR" + print_info "3. Right-click 'launch-gui.command' → Make Alias" + print_info "4. Drag alias to Desktop or Applications" +fi + +echo "" +print_success "IDEasy GUI shortcuts ready!" +echo "" +echo "Usage:" +echo " • Open Finder and go to Applications" +echo " • Double-click 'IDEasy GUI' to launch" +echo " • Or double-click the Desktop shortcut" +echo "" diff --git a/gui/scripts/macos/launch-gui.command b/gui/scripts/macos/launch-gui.command new file mode 100644 index 0000000000..d7538fd7a3 --- /dev/null +++ b/gui/scripts/macos/launch-gui.command @@ -0,0 +1,37 @@ +#!/bin/bash + +# IDEasy GUI Launcher for macOS +# This script launches the IDEasy GUI using the native 'ide gui' command +# The GUI runs in the background, the script exits immediately + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +if [ ! -f "$PROJECT_ROOT/pom.xml" ]; then + echo "Error: IDEasy project root not found at $PROJECT_ROOT" + echo "Please ensure this script is located in: IDEasy/gui/scripts/macos/" + read -p "Press Enter to close..." + exit 1 +fi + +if ! command -v ide &> /dev/null; then + echo "" + echo "Error: IDEasy is not installed" + echo "" + echo "Please install IDEasy first:" + echo "https://github.com/devonfw/IDEasy#setup" + echo "" + read -p "Press Enter to close..." + exit 1 +fi + +cd "$PROJECT_ROOT" + +# Launch IDE GUI in background and exit immediately +ide gui > /dev/null 2>&1 & + +# Give the process a moment to start (may need time for Maven dependencies on first run) +sleep 3 + +# Close this terminal window after the GUI is launched +exit 0 diff --git a/gui/scripts/windows/create-shortcuts.ps1 b/gui/scripts/windows/create-shortcuts.ps1 new file mode 100644 index 0000000000..c42c146a85 --- /dev/null +++ b/gui/scripts/windows/create-shortcuts.ps1 @@ -0,0 +1,80 @@ +#Requires -Version 5.0 + +# IDEasy GUI Launcher - Create Windows Start Menu Shortcut and Desktop Link +# This script creates shortcuts to launch the IDEasy GUI from Windows Start Menu and Desktop + +param ( + [switch]$SkipDesktop, + [switch]$SkipStartMenu +) + +$ErrorActionPreference = 'Stop' + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$guiDir = Split-Path -Parent (Split-Path -Parent $scriptDir) +$projectRoot = Split-Path -Parent $guiDir + +$launcherBat = Join-Path $scriptDir "launch-gui.bat" +$pomFile = Join-Path $projectRoot "pom.xml" + +if (-not (Test-Path $launcherBat)) { + Write-Host "Error: launch-gui.bat not found" -ForegroundColor Red + exit 1 +} + +if (-not (Test-Path $pomFile)) { + Write-Host "Error: pom.xml not found" -ForegroundColor Red + exit 1 +} + +Write-Host "IDEasy GUI Launcher Setup" -ForegroundColor Cyan +Write-Host "Project: $projectRoot" -ForegroundColor Cyan +Write-Host "" + +function Create-Shortcut { + param( + [string]$Path, + [string]$Target, + [string]$Description + ) + + $wshShell = New-Object -ComObject WScript.Shell + + try { + Write-Host "Creating shortcut: $Path" + + $shortcut = $wshShell.CreateShortcut($Path) + $shortcut.TargetPath = $Target + $shortcut.WorkingDirectory = $projectRoot + $shortcut.Description = $Description + + $iconPath = Join-Path $scriptDir "icon.ico" + if (Test-Path $iconPath) { + $shortcut.IconLocation = "$iconPath,0" + } else { + $shortcut.IconLocation = "$env:SystemRoot\system32\shell32.dll,1" + } + + $shortcut.Save() + Write-Host "Created: $Path" -ForegroundColor Green + } + catch { + Write-Host "Error creating shortcut: $($_.Exception.Message)" -ForegroundColor Red + } + finally { + [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshShell) | Out-Null + } +} + +if (-not $SkipDesktop) { + $desktopShortcut = Join-Path "$env:USERPROFILE\Desktop" "IDEasy GUI.lnk" + Create-Shortcut -Path $desktopShortcut -Target $launcherBat -Description "Launch IDEasy GUI" +} + +if (-not $SkipStartMenu) { + $startMenuShortcut = Join-Path "$env:APPDATA\Microsoft\Windows\Start Menu\Programs" "IDEasy GUI.lnk" + Create-Shortcut -Path $startMenuShortcut -Target $launcherBat -Description "Launch IDEasy GUI" +} + +Write-Host "" +Write-Host "Setup complete!" -ForegroundColor Green diff --git a/gui/scripts/windows/launch-gui.bat b/gui/scripts/windows/launch-gui.bat new file mode 100644 index 0000000000..2d612be744 --- /dev/null +++ b/gui/scripts/windows/launch-gui.bat @@ -0,0 +1,36 @@ +@echo off +setlocal + +REM IDEasy GUI Launcher for Windows + +set "SCRIPT_DIR=%~dp0" + +REM Check if ide command exists +where ide >nul 2>&1 || ( + echo. + echo Error: IDEasy is not installed or not in PATH + echo https://github.com/devonfw/IDEasy#setup + echo. + pause + exit /b 1 +) + +REM Determine project root safely +pushd "%SCRIPT_DIR%..\..\.." +set "PROJECT_ROOT=%CD%" +popd + +REM Check project structure +if not exist "%PROJECT_ROOT%\pom.xml" ( + echo Error: IDEasy project root not found at %PROJECT_ROOT% + echo. + pause + exit /b 1 +) + +REM Launch GUI +cd /d "%PROJECT_ROOT%" +echo Starting IDEasy GUI... +START "" ide gui + +exit /b 0 \ No newline at end of file From e7c5854380a53fe23deece82e2834f0afac2357b Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Wed, 17 Jun 2026 12:13:56 +0200 Subject: [PATCH 02/16] feat: update scripts and using correct icon --- .gitignore | 3 + gui/scripts/linux/create-shortcuts.sh | 36 ++++++----- gui/scripts/linux/launch-gui.sh | 7 +- gui/scripts/macos/create-shortcuts.sh | 28 +++++--- gui/scripts/macos/launch-gui.command | 7 +- gui/scripts/windows/create-shortcuts.ps1 | 82 ++++++++++++++++++++---- gui/scripts/windows/launch-gui.bat | 4 +- 7 files changed, 117 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index 4cfa743f86..5f8dc176d2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ eclipse-target/ generated/ node_modules +# Generated GUI assets (created at runtime, not to be committed) +gui/scripts/windows/ideasy-gui.ico + # Package Files # *.jar *.war diff --git a/gui/scripts/linux/create-shortcuts.sh b/gui/scripts/linux/create-shortcuts.sh index e59ff6bf42..16d28b694e 100644 --- a/gui/scripts/linux/create-shortcuts.sh +++ b/gui/scripts/linux/create-shortcuts.sh @@ -5,8 +5,8 @@ # Supports: GNOME, KDE, XFCE, and other freedesktop-compatible environments SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" LAUNCHER_SCRIPT="$SCRIPT_DIR/launch-gui.sh" +ICON_PATH="$SCRIPT_DIR/../../src/main/resources/com/devonfw/ide/gui/assets/devonfw.png" # Set error handling - exit on error but allow catching specific errors set -o pipefail @@ -37,32 +37,31 @@ fi chmod +x "$LAUNCHER_SCRIPT" +# Resolve icon: use bundled devonfw.png if available, otherwise fall back to system theme +if [ -f "$ICON_PATH" ]; then + ICON="$(cd "$(dirname "$ICON_PATH")" && pwd)/$(basename "$ICON_PATH")" +else + ICON="application-x-executable" +fi + # Create Desktop Entry (.desktop file) function create_desktop_entry() { local target_dir="$1" local desktop_file="$target_dir/ideasy-gui.desktop" - - if [ ! -f "$LAUNCHER_SCRIPT" ]; then - print_error "Launcher script not found: $LAUNCHER_SCRIPT" - return 1 - fi - + if ! mkdir -p "$target_dir" 2>/dev/null; then print_error "Failed to create directory: $target_dir" return 1 fi - - # Use absolute path to ensure it works from anywhere - local absolute_launcher="$(cd "$(dirname "$LAUNCHER_SCRIPT")"; pwd)/$(basename "$LAUNCHER_SCRIPT")" - + if ! cat > "$desktop_file" </dev/null || true print_info "Launch from: Application Menu or Launcher" fi -# Create Desktop entry -DESKTOP_DIR="$HOME/Desktop" +# Determine desktop directory via XDG standard (respects custom Desktop locations) +DESKTOP_DIR=$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop") if [ -d "$DESKTOP_DIR" ]; then if ! create_desktop_entry "$DESKTOP_DIR"; then print_info "Note: Desktop entry creation requires write permissions" else + # GNOME 3.28+: mark desktop file as trusted so it can be launched by double-click + gio set "$DESKTOP_DIR/ideasy-gui.desktop" "metadata::trusted" true 2>/dev/null || true print_info "Launch from: Desktop" fi else @@ -110,4 +113,3 @@ echo " • Open your Application Menu and search for 'IDEasy GUI'" echo " • Or double-click the shortcut on your Desktop" echo " • First launch may take longer as Maven downloads dependencies" echo "" - diff --git a/gui/scripts/linux/launch-gui.sh b/gui/scripts/linux/launch-gui.sh index 137fb7c468..1a87233c60 100644 --- a/gui/scripts/linux/launch-gui.sh +++ b/gui/scripts/linux/launch-gui.sh @@ -26,10 +26,7 @@ fi cd "$PROJECT_ROOT" # Launch IDE GUI in background and exit immediately -# Redirect output to suppress any console messages -ide gui > /dev/null 2>&1 & - -# Give the process a moment to start (may need time for Maven dependencies on first run) -sleep 3 +LOG_FILE="$HOME/.ideasy-gui.log" +ide gui >> "$LOG_FILE" 2>&1 & exit 0 diff --git a/gui/scripts/macos/create-shortcuts.sh b/gui/scripts/macos/create-shortcuts.sh index a7e8431c5c..4e262b948b 100644 --- a/gui/scripts/macos/create-shortcuts.sh +++ b/gui/scripts/macos/create-shortcuts.sh @@ -5,7 +5,6 @@ # Supports creating: Applications folder alias and Desktop alias SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" LAUNCHER_SCRIPT="$SCRIPT_DIR/launch-gui.command" # Set error handling - exit on error but allow catching specific errors @@ -38,26 +37,27 @@ fi chmod +x "$LAUNCHER_SCRIPT" # Create an alias/shortcut using osascript (AppleScript) +# Falls back to a symlink if Finder is not running or AppleScript fails function create_alias() { local source="$1" local target_dir="$2" local target_name="$3" - + if [ ! -f "$source" ]; then print_error "Source file not found: $source" return 1 fi - + if ! mkdir -p "$target_dir" 2>/dev/null; then print_error "Failed to create directory: $target_dir" return 1 fi - + # Properly escape paths for AppleScript local escaped_source="$(printf '%s\n' "$source" | sed 's/\\/\\\\/g; s/"/\\"/g')" local escaped_target_dir="$(printf '%s\n' "$target_dir" | sed 's/\\/\\\\/g; s/"/\\"/g')" local escaped_target_name="$(printf '%s\n' "$target_name" | sed 's/\\/\\\\/g; s/"/\\"/g')" - + local output output=$(osascript 2>&1 </dev/null; then + print_success "Created symlink: $target_dir/$target_name.command" + return 0 + fi + + print_error "Failed to create shortcut: $target_dir/$target_name" + [ -n "$output" ] && print_error "Reason: $output" + return 1 } # Create Applications alias diff --git a/gui/scripts/macos/launch-gui.command b/gui/scripts/macos/launch-gui.command index d7538fd7a3..91618250f3 100644 --- a/gui/scripts/macos/launch-gui.command +++ b/gui/scripts/macos/launch-gui.command @@ -28,10 +28,7 @@ fi cd "$PROJECT_ROOT" # Launch IDE GUI in background and exit immediately -ide gui > /dev/null 2>&1 & +LOG_FILE="$HOME/.ideasy-gui.log" +ide gui >> "$LOG_FILE" 2>&1 & -# Give the process a moment to start (may need time for Maven dependencies on first run) -sleep 3 - -# Close this terminal window after the GUI is launched exit 0 diff --git a/gui/scripts/windows/create-shortcuts.ps1 b/gui/scripts/windows/create-shortcuts.ps1 index c42c146a85..f1c1226899 100644 --- a/gui/scripts/windows/create-shortcuts.ps1 +++ b/gui/scripts/windows/create-shortcuts.ps1 @@ -31,29 +31,86 @@ Write-Host "IDEasy GUI Launcher Setup" -ForegroundColor Cyan Write-Host "Project: $projectRoot" -ForegroundColor Cyan Write-Host "" +# Wrap the PNG bytes directly into an ICO container (Windows Vista+ supports PNG frames in ICO). +# This preserves full color depth and alpha transparency without any image processing. +# The ICO is written next to the scripts so the shortcut has a stable reference path. +# Returns $true on success, $false on failure. +function Convert-PngToIco { + param([string]$PngPath, [string]$IcoPath) + try { + $pngBytes = [System.IO.File]::ReadAllBytes($PngPath) + + # PNG dimensions are stored big-endian in the IHDR chunk at bytes 16-23 + $width = ($pngBytes[16] -shl 24) -bor ($pngBytes[17] -shl 16) -bor ($pngBytes[18] -shl 8) -bor $pngBytes[19] + $height = ($pngBytes[20] -shl 24) -bor ($pngBytes[21] -shl 16) -bor ($pngBytes[22] -shl 8) -bor $pngBytes[23] + + # ICO directory encodes 256 as 0 + $icoW = if ($width -ge 256) { [byte]0 } else { [byte]$width } + $icoH = if ($height -ge 256) { [byte]0 } else { [byte]$height } + + $sizeBytes = [System.BitConverter]::GetBytes([int32]$pngBytes.Length) + $offsetBytes = [System.BitConverter]::GetBytes([int32](6 + 16)) # header(6) + one dir entry(16) + + # ICO header (6 bytes) + single directory entry (16 bytes) + $icoHeader = [byte[]]( + 0x00, 0x00, # reserved + 0x01, 0x00, # type: icon + 0x01, 0x00, # image count: 1 + $icoW, $icoH, 0x00, 0x00, # w, h, colorCount, reserved + 0x01, 0x00, # color planes + 0x20, 0x00, # bits per pixel (32) + $sizeBytes[0], $sizeBytes[1], $sizeBytes[2], $sizeBytes[3], # image data size + $offsetBytes[0], $offsetBytes[1], $offsetBytes[2], $offsetBytes[3] # image data offset + ) + + $stream = [System.IO.FileStream]::new($IcoPath, [System.IO.FileMode]::Create) + $stream.Write($icoHeader, 0, $icoHeader.Length) + $stream.Write($pngBytes, 0, $pngBytes.Length) + $stream.Close() + return $true + } + catch { + return $false + } +} + +# Resolve icon location: prefer devonfw.png converted to ICO, fall back to shell32 +$pngSource = Join-Path $guiDir "src\main\resources\com\devonfw\ide\gui\assets\devonfw.png" +$generatedIco = Join-Path $scriptDir "ideasy-gui.ico" +$iconLocation = "$env:SystemRoot\system32\shell32.dll,1" + +if (Test-Path $pngSource) { + if (-not (Test-Path $generatedIco)) { + if (Convert-PngToIco -PngPath $pngSource -IcoPath $generatedIco) { + Write-Host "Icon generated from devonfw.png" -ForegroundColor Cyan + } else { + Write-Host "Note: Icon conversion failed, using default icon" -ForegroundColor Yellow + } + } + if (Test-Path $generatedIco) { + $iconLocation = "$generatedIco,0" + } +} elseif (Test-Path (Join-Path $scriptDir "icon.ico")) { + $iconLocation = "$(Join-Path $scriptDir 'icon.ico'),0" +} + function Create-Shortcut { param( [string]$Path, [string]$Target, [string]$Description ) - - $wshShell = New-Object -ComObject WScript.Shell + $wshShell = $null try { Write-Host "Creating shortcut: $Path" + $wshShell = New-Object -ComObject WScript.Shell $shortcut = $wshShell.CreateShortcut($Path) $shortcut.TargetPath = $Target $shortcut.WorkingDirectory = $projectRoot $shortcut.Description = $Description - - $iconPath = Join-Path $scriptDir "icon.ico" - if (Test-Path $iconPath) { - $shortcut.IconLocation = "$iconPath,0" - } else { - $shortcut.IconLocation = "$env:SystemRoot\system32\shell32.dll,1" - } + $shortcut.IconLocation = $iconLocation $shortcut.Save() Write-Host "Created: $Path" -ForegroundColor Green @@ -62,12 +119,15 @@ function Create-Shortcut { Write-Host "Error creating shortcut: $($_.Exception.Message)" -ForegroundColor Red } finally { - [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshShell) | Out-Null + if ($wshShell) { + [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshShell) | Out-Null + } } } if (-not $SkipDesktop) { - $desktopShortcut = Join-Path "$env:USERPROFILE\Desktop" "IDEasy GUI.lnk" + # Use Shell32 to resolve the Desktop folder — works correctly with OneDrive-redirected Desktops + $desktopShortcut = Join-Path ([Environment]::GetFolderPath('Desktop')) "IDEasy GUI.lnk" Create-Shortcut -Path $desktopShortcut -Target $launcherBat -Description "Launch IDEasy GUI" } diff --git a/gui/scripts/windows/launch-gui.bat b/gui/scripts/windows/launch-gui.bat index 2d612be744..7bbe8dc26f 100644 --- a/gui/scripts/windows/launch-gui.bat +++ b/gui/scripts/windows/launch-gui.bat @@ -31,6 +31,6 @@ if not exist "%PROJECT_ROOT%\pom.xml" ( REM Launch GUI cd /d "%PROJECT_ROOT%" echo Starting IDEasy GUI... -START "" ide gui +START "" /B ide gui >> "%USERPROFILE%\.ideasy-gui.log" 2>&1 -exit /b 0 \ No newline at end of file +exit /b 0 From 2a7d8720f135866dc383b1fe95b7b294a14e66ae Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Thu, 18 Jun 2026 11:12:32 +0200 Subject: [PATCH 03/16] fix: minor directory bug for linux --- gui/scripts/linux/create-shortcuts.sh | 11 +++++++++-- gui/scripts/linux/launch-gui.sh | 24 ++++++++++++++++-------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/gui/scripts/linux/create-shortcuts.sh b/gui/scripts/linux/create-shortcuts.sh index 16d28b694e..b7775ce6a7 100644 --- a/gui/scripts/linux/create-shortcuts.sh +++ b/gui/scripts/linux/create-shortcuts.sh @@ -60,7 +60,7 @@ Version=1.0 Type=Application Name=IDEasy GUI Comment=Launch IDEasy Integrated Development Environment GUI -Exec="$LAUNCHER_SCRIPT" +Exec=$LAUNCHER_SCRIPT Icon=$ICON Terminal=false Categories=Development;IDE; @@ -91,8 +91,15 @@ else print_info "Launch from: Application Menu or Launcher" fi -# Determine desktop directory via XDG standard (respects custom Desktop locations) +# Determine desktop directory via XDG standard (respects custom Desktop locations). +# On systems where xdg-user-dirs was never configured (e.g. minimal WM setups), +# xdg-user-dir falls back to printing $HOME itself — guard against that so we +# don't drop a loose .desktop file directly into the user's home directory. DESKTOP_DIR=$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop") +DESKTOP_DIR="${DESKTOP_DIR%/}" +if [ -z "$DESKTOP_DIR" ] || [ "$DESKTOP_DIR" = "$HOME" ]; then + DESKTOP_DIR="$HOME/Desktop" +fi if [ -d "$DESKTOP_DIR" ]; then if ! create_desktop_entry "$DESKTOP_DIR"; then print_info "Note: Desktop entry creation requires write permissions" diff --git a/gui/scripts/linux/launch-gui.sh b/gui/scripts/linux/launch-gui.sh index 1a87233c60..f0763b2780 100644 --- a/gui/scripts/linux/launch-gui.sh +++ b/gui/scripts/linux/launch-gui.sh @@ -6,27 +6,35 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +LOG_FILE="$HOME/.ideasy-gui.log" + +# When launched from an application menu (Terminal=false), there is no console to +# show errors on. Redirect everything to a log file from the start so failures +# (e.g. 'ide' missing from the launcher's PATH) are diagnosable after the fact +exec >> "$LOG_FILE" 2>&1 +echo "---- $(date) ----" + +function notify_error() { + command -v notify-send &> /dev/null && notify-send -i dialog-error "IDEasy GUI" "$1" +} if [ ! -f "$PROJECT_ROOT/pom.xml" ]; then echo "Error: IDEasy project root not found at $PROJECT_ROOT" echo "Please ensure this script is located in: IDEasy/gui/scripts/linux/" + notify_error "Project root not found at $PROJECT_ROOT" exit 1 fi if ! command -v ide &> /dev/null; then - echo "" - echo "Error: IDEasy is not installed" - echo "" - echo "Please install IDEasy first:" - echo "https://github.com/devonfw/IDEasy#setup" - echo "" + echo "Error: IDEasy is not installed or not in PATH" + echo "Please install IDEasy first: https://github.com/devonfw/IDEasy#setup" + notify_error "'ide' command not found in PATH (see $LOG_FILE)" exit 1 fi cd "$PROJECT_ROOT" # Launch IDE GUI in background and exit immediately -LOG_FILE="$HOME/.ideasy-gui.log" -ide gui >> "$LOG_FILE" 2>&1 & +ide gui & exit 0 From 470006ec3faaea35653f7aed4774b160d3199c6a Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Fri, 19 Jun 2026 10:36:28 +0200 Subject: [PATCH 04/16] feat: update linux script --- gui/scripts/linux/launch-gui.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/gui/scripts/linux/launch-gui.sh b/gui/scripts/linux/launch-gui.sh index f0763b2780..84bf508947 100644 --- a/gui/scripts/linux/launch-gui.sh +++ b/gui/scripts/linux/launch-gui.sh @@ -25,16 +25,21 @@ if [ ! -f "$PROJECT_ROOT/pom.xml" ]; then exit 1 fi -if ! command -v ide &> /dev/null; then +# 'ide' is a shell function defined in $IDE_ROOT/_ide/installation/functions and +# sourced by ~/.bashrc / ~/.zshrc — those only get sourced in interactive shells. +# Application launchers (Terminal=false) start a plain, non-interactive shell, so +# 'ide' is invisible there even though it works fine from a terminal. Route through +# an interactive bash so the function gets sourced regardless of launch context. +if ! bash -ic "command -v ide" &> /dev/null; then echo "Error: IDEasy is not installed or not in PATH" echo "Please install IDEasy first: https://github.com/devonfw/IDEasy#setup" - notify_error "'ide' command not found in PATH (see $LOG_FILE)" + notify_error "'ide' command not found (see $LOG_FILE)" exit 1 fi cd "$PROJECT_ROOT" # Launch IDE GUI in background and exit immediately -ide gui & +bash -ic "ide gui" & exit 0 From 1c7a1cca23b707761290721276c14eb41728f900 Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Tue, 23 Jun 2026 15:04:55 +0200 Subject: [PATCH 05/16] fix: not updated root environment --- gui/scripts/windows/launch-gui.bat | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gui/scripts/windows/launch-gui.bat b/gui/scripts/windows/launch-gui.bat index 7bbe8dc26f..753ed321a1 100644 --- a/gui/scripts/windows/launch-gui.bat +++ b/gui/scripts/windows/launch-gui.bat @@ -5,10 +5,24 @@ REM IDEasy GUI Launcher for Windows set "SCRIPT_DIR=%~dp0" +REM Desktop/Start-Menu shortcuts are spawned by explorer.exe, which caches its +REM environment from login and does not pick up registry PATH/IDE_ROOT changes +REM made by the IDEasy installer until the user logs off or restarts explorer. +REM Re-read IDE_ROOT directly from the registry and extend PATH with it here, +REM so the shortcut works even with explorer's stale inherited environment. +if not defined IDE_ROOT ( + for /f "tokens=2,*" %%A in ('reg query "HKCU\Environment" /v IDE_ROOT 2^>nul') do set "IDE_ROOT=%%B" +) +if defined IDE_ROOT ( + set "PATH=%IDE_ROOT%\_ide\installation\bin;%PATH%" +) + REM Check if ide command exists where ide >nul 2>&1 || ( echo. echo Error: IDEasy is not installed or not in PATH + echo If you just installed IDEasy, log off/on once or restart Explorer so + echo the desktop shortcut picks up the updated environment. echo https://github.com/devonfw/IDEasy#setup echo. pause From 87c0e2491fdbca17fd797916d5324c2d2828ad3e Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Tue, 23 Jun 2026 15:13:32 +0200 Subject: [PATCH 06/16] fix: reset gitignore and attributes --- .gitattributes | 4 ---- .gitignore | 3 --- 2 files changed, 7 deletions(-) diff --git a/.gitattributes b/.gitattributes index 6d1dc4b290..7e7a79ac69 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,9 +1,5 @@ * eol=lf *.bat eol=crlf -*.ps1 eol=crlf -*.ps2 eol=crlf -*.sh eol=lf -*.command eol=lf *.png binary *.zip binary *.tgz binary diff --git a/.gitignore b/.gitignore index 5f8dc176d2..4cfa743f86 100644 --- a/.gitignore +++ b/.gitignore @@ -19,9 +19,6 @@ eclipse-target/ generated/ node_modules -# Generated GUI assets (created at runtime, not to be committed) -gui/scripts/windows/ideasy-gui.ico - # Package Files # *.jar *.war From 79e973ce934c7b5bca90737432376576c08e2086 Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Wed, 24 Jun 2026 09:39:54 +0200 Subject: [PATCH 07/16] doc: add issue to changelog --- CHANGELOG.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index b95c6b8a9f..1441b5ec0b 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -6,6 +6,8 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE Release with new features and bugfixes: +* https://github.com/devonfw/IDEasy/issues/1917[#1917]: Allow creation of a shortcut for Windows, macOs and Linux + The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/46?closed=1[milestone 2026.07.001]. From 690be00cc448b96b71914a3a786866250be8190d Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Wed, 24 Jun 2026 10:13:17 +0200 Subject: [PATCH 08/16] feat: update window script to not open a new terminal --- gui/scripts/windows/create-shortcuts.ps1 | 17 +++++++++++++++-- gui/scripts/windows/launch-gui-silent.vbs | 11 +++++++++++ gui/scripts/windows/launch-gui.bat | 2 +- 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 gui/scripts/windows/launch-gui-silent.vbs diff --git a/gui/scripts/windows/create-shortcuts.ps1 b/gui/scripts/windows/create-shortcuts.ps1 index f1c1226899..24e3aeaba7 100644 --- a/gui/scripts/windows/create-shortcuts.ps1 +++ b/gui/scripts/windows/create-shortcuts.ps1 @@ -15,6 +15,7 @@ $guiDir = Split-Path -Parent (Split-Path -Parent $scriptDir) $projectRoot = Split-Path -Parent $guiDir $launcherBat = Join-Path $scriptDir "launch-gui.bat" +$launcherVbs = Join-Path $scriptDir "launch-gui-silent.vbs" $pomFile = Join-Path $projectRoot "pom.xml" if (-not (Test-Path $launcherBat)) { @@ -22,6 +23,11 @@ if (-not (Test-Path $launcherBat)) { exit 1 } +if (-not (Test-Path $launcherVbs)) { + Write-Host "Error: launch-gui-silent.vbs not found" -ForegroundColor Red + exit 1 +} + if (-not (Test-Path $pomFile)) { Write-Host "Error: pom.xml not found" -ForegroundColor Red exit 1 @@ -98,6 +104,7 @@ function Create-Shortcut { param( [string]$Path, [string]$Target, + [string]$Arguments, [string]$Description ) @@ -108,6 +115,7 @@ function Create-Shortcut { $wshShell = New-Object -ComObject WScript.Shell $shortcut = $wshShell.CreateShortcut($Path) $shortcut.TargetPath = $Target + $shortcut.Arguments = $Arguments $shortcut.WorkingDirectory = $projectRoot $shortcut.Description = $Description $shortcut.IconLocation = $iconLocation @@ -125,15 +133,20 @@ function Create-Shortcut { } } +# Target wscript.exe running the silent VBS wrapper instead of launch-gui.bat directly, +# so no console window ever appears - independent of the user's terminal "close on exit" setting. +$wscriptPath = Join-Path "$env:SystemRoot\System32" "wscript.exe" +$launcherArgs = "`"$launcherVbs`"" + if (-not $SkipDesktop) { # Use Shell32 to resolve the Desktop folder — works correctly with OneDrive-redirected Desktops $desktopShortcut = Join-Path ([Environment]::GetFolderPath('Desktop')) "IDEasy GUI.lnk" - Create-Shortcut -Path $desktopShortcut -Target $launcherBat -Description "Launch IDEasy GUI" + Create-Shortcut -Path $desktopShortcut -Target $wscriptPath -Arguments $launcherArgs -Description "Launch IDEasy GUI" } if (-not $SkipStartMenu) { $startMenuShortcut = Join-Path "$env:APPDATA\Microsoft\Windows\Start Menu\Programs" "IDEasy GUI.lnk" - Create-Shortcut -Path $startMenuShortcut -Target $launcherBat -Description "Launch IDEasy GUI" + Create-Shortcut -Path $startMenuShortcut -Target $wscriptPath -Arguments $launcherArgs -Description "Launch IDEasy GUI" } Write-Host "" diff --git a/gui/scripts/windows/launch-gui-silent.vbs b/gui/scripts/windows/launch-gui-silent.vbs new file mode 100644 index 0000000000..f5432adebb --- /dev/null +++ b/gui/scripts/windows/launch-gui-silent.vbs @@ -0,0 +1,11 @@ +' Runs launch-gui.bat completely hidden (no console window), regardless of the +' user's terminal "close on exit" settings. The shortcut targets this file via +' wscript.exe instead of launch-gui.bat directly so no window ever appears. +Dim shell, fso, scriptDir, batPath + +Set shell = CreateObject("WScript.Shell") +Set fso = CreateObject("Scripting.FileSystemObject") +scriptDir = fso.GetParentFolderName(WScript.ScriptFullName) +batPath = fso.BuildPath(scriptDir, "launch-gui.bat") + +shell.Run """" & batPath & """", 0, False diff --git a/gui/scripts/windows/launch-gui.bat b/gui/scripts/windows/launch-gui.bat index 753ed321a1..0f96d1cbc3 100644 --- a/gui/scripts/windows/launch-gui.bat +++ b/gui/scripts/windows/launch-gui.bat @@ -45,6 +45,6 @@ if not exist "%PROJECT_ROOT%\pom.xml" ( REM Launch GUI cd /d "%PROJECT_ROOT%" echo Starting IDEasy GUI... -START "" /B ide gui >> "%USERPROFILE%\.ideasy-gui.log" 2>&1 +START "" /B cmd /c ide gui >> "%USERPROFILE%\.ideasy-gui.log" 2>&1 exit /b 0 From 824a9a559e3ee2b1ac334e78f4ac9c83d40141d3 Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Wed, 24 Jun 2026 10:29:11 +0200 Subject: [PATCH 09/16] feat: add fix for macOs and Linux script --- gui/scripts/linux/launch-gui.sh | 8 +++++++- gui/scripts/macos/launch-gui.command | 11 +++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/gui/scripts/linux/launch-gui.sh b/gui/scripts/linux/launch-gui.sh index 84bf508947..0258583743 100644 --- a/gui/scripts/linux/launch-gui.sh +++ b/gui/scripts/linux/launch-gui.sh @@ -8,6 +8,11 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" LOG_FILE="$HOME/.ideasy-gui.log" +# Ignore SIGHUP so this script (and what it launches) survives even if the +# process that handed off execution (desktop session bus, gio launch, etc.) +# disconnects before we are done. +trap '' HUP + # When launched from an application menu (Terminal=false), there is no console to # show errors on. Redirect everything to a log file from the start so failures # (e.g. 'ide' missing from the launcher's PATH) are diagnosable after the fact @@ -40,6 +45,7 @@ fi cd "$PROJECT_ROOT" # Launch IDE GUI in background and exit immediately -bash -ic "ide gui" & +bash -ic "ide gui" /dev/null; then +# 'ide' is a shell function defined in $IDE_ROOT/_ide/installation/functions and +# sourced by ~/.bashrc / ~/.zshrc — those only get sourced in interactive shells. +# Double-clicking a .command file runs it via its #!/bin/bash shebang directly, +# which is a non-interactive shell, so 'ide' is invisible there even though it +# works fine from a regular Terminal window. Route through an interactive bash +# so the function gets sourced regardless of how this script was launched. +if ! bash -ic "command -v ide" &> /dev/null; then echo "" echo "Error: IDEasy is not installed" echo "" @@ -29,6 +35,7 @@ cd "$PROJECT_ROOT" # Launch IDE GUI in background and exit immediately LOG_FILE="$HOME/.ideasy-gui.log" -ide gui >> "$LOG_FILE" 2>&1 & +bash -ic "ide gui" >> "$LOG_FILE" 2>&1 & +disown exit 0 From 193569bc12abbe1117b2923b9a3913d598e02854 Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Thu, 25 Jun 2026 12:48:54 +0200 Subject: [PATCH 10/16] fix: set executable bit on linux/macos gui shortcut scripts Scripts were committed as 644 (likely from a Windows checkout), requiring a manual chmod +x before they could be run directly after checkout. --- gui/scripts/linux/create-shortcuts.sh | 0 gui/scripts/linux/launch-gui.sh | 0 gui/scripts/macos/create-shortcuts.sh | 0 gui/scripts/macos/launch-gui.command | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gui/scripts/linux/create-shortcuts.sh mode change 100644 => 100755 gui/scripts/linux/launch-gui.sh mode change 100644 => 100755 gui/scripts/macos/create-shortcuts.sh mode change 100644 => 100755 gui/scripts/macos/launch-gui.command diff --git a/gui/scripts/linux/create-shortcuts.sh b/gui/scripts/linux/create-shortcuts.sh old mode 100644 new mode 100755 diff --git a/gui/scripts/linux/launch-gui.sh b/gui/scripts/linux/launch-gui.sh old mode 100644 new mode 100755 diff --git a/gui/scripts/macos/create-shortcuts.sh b/gui/scripts/macos/create-shortcuts.sh old mode 100644 new mode 100755 diff --git a/gui/scripts/macos/launch-gui.command b/gui/scripts/macos/launch-gui.command old mode 100644 new mode 100755 From 1c43f0ec912bca7b69439cbd33550358040e960f Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Tue, 7 Jul 2026 10:39:11 +0200 Subject: [PATCH 11/16] feat: create shortcut on ide installation --- .../tools/ide/tool/IdeasyCommandlet.java | 118 ++++++++++++++ .../main/package/gui/linux/ideasy-gui.desktop | 10 ++ cli/src/main/package/gui/logo.ico | Bin 0 -> 11512 bytes cli/src/main/package/gui/logo.png | Bin 0 -> 11491 bytes gui/scripts/linux/create-shortcuts.sh | 122 -------------- gui/scripts/linux/launch-gui.sh | 51 ------ gui/scripts/macos/create-shortcuts.sh | 121 -------------- gui/scripts/macos/launch-gui.command | 41 ----- gui/scripts/windows/create-shortcuts.ps1 | 153 ------------------ gui/scripts/windows/launch-gui-silent.vbs | 11 -- gui/scripts/windows/launch-gui.bat | 50 ------ 11 files changed, 128 insertions(+), 549 deletions(-) create mode 100644 cli/src/main/package/gui/linux/ideasy-gui.desktop create mode 100644 cli/src/main/package/gui/logo.ico create mode 100644 cli/src/main/package/gui/logo.png delete mode 100755 gui/scripts/linux/create-shortcuts.sh delete mode 100755 gui/scripts/linux/launch-gui.sh delete mode 100755 gui/scripts/macos/create-shortcuts.sh delete mode 100755 gui/scripts/macos/launch-gui.command delete mode 100644 gui/scripts/windows/create-shortcuts.ps1 delete mode 100644 gui/scripts/windows/launch-gui-silent.vbs delete mode 100644 gui/scripts/windows/launch-gui.bat diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java index a762cd2c2b..9fb4435bb7 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java @@ -1,5 +1,6 @@ package com.devonfw.tools.ide.tool; +import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.nio.file.Files; @@ -295,6 +296,7 @@ public void installIdeasy(Path cwd) { addToShellRc(BASHRC, ideRoot, null); addToShellRc(ZSHRC, ideRoot, "autoload -U +X bashcompinit && bashcompinit"); installIdeasyWindowsEnv(ideRoot, installationPath); + installDesktopShortcut(installationPath); IdeLogLevel.SUCCESS.log(LOG, "IDEasy has been installed successfully on your system."); LOG.warn("IDEasy has been setup for new shells but it cannot work in your current shell(s).\n" + "To use it here, run 'source ~/.bashrc' (or your shell config). Otherwise, open a new terminal or reboot."); @@ -331,6 +333,122 @@ private void installIdeasyWindowsEnv(Path ideRoot, Path installationPath) { } } + private void installDesktopShortcut(Path installationPath) { + + try { + if (this.context.getSystemInfo().isLinux()) { + installLinuxDesktopShortcut(installationPath); + } else if (this.context.getSystemInfo().isMac()) { + installMacDesktopShortcut(installationPath); + } else if (this.context.getSystemInfo().isWindows()) { + installWindowsDesktopShortcut(installationPath); + } + } catch (Exception e) { + LOG.warn("Failed to create desktop shortcut: {}", e.getMessage()); + } + } + + private void installLinuxDesktopShortcut(Path installationPath) throws IOException { + + Path templateFile = installationPath.resolve("gui/linux/ideasy-gui.desktop"); + if (!Files.exists(templateFile)) { + LOG.warn("Desktop file template not found at {}. Skipping desktop shortcut creation.", templateFile); + return; + } + Path ideasyBin = installationPath.resolve("bin/ideasy"); + Path logoPath = installationPath.resolve("gui/logo.png"); + String content = Files.readString(templateFile) + .replace("IDEASY_BIN", ideasyBin.toString()) + .replace("IDEASY_ICON", logoPath.toString()); + + Path applicationsDir = this.context.getUserHome().resolve(".local/share/applications"); + this.context.getFileAccess().mkdirs(applicationsDir); + Path desktopFile = applicationsDir.resolve("ideasy-gui.desktop"); + Files.writeString(desktopFile, content); + try { + Files.setPosixFilePermissions(desktopFile, FileAccess.RWX_RX_RX); + } catch (UnsupportedOperationException e) { + LOG.debug("Could not set POSIX permissions on {}", desktopFile); + } + + // Update desktop database so the entry appears in application menus immediately + try { + this.context.newProcess().executable("update-desktop-database").addArg(applicationsDir.toString()) + .run(ProcessMode.DEFAULT_CAPTURE); + } catch (Exception e) { + LOG.debug("update-desktop-database failed (optional): {}", e.getMessage()); + } + + // Mark as trusted for GNOME 3.28+ so the shortcut is executable without prompting + try { + this.context.newProcess().executable("gio") + .addArgs("set", desktopFile.toString(), "metadata::trusted", "true") + .run(ProcessMode.DEFAULT_CAPTURE); + } catch (Exception e) { + LOG.debug("gio set trusted failed (optional): {}", e.getMessage()); + } + + IdeLogLevel.SUCCESS.log(LOG, "Created desktop shortcut at {}", desktopFile); + } + + private void installMacDesktopShortcut(Path installationPath) throws IOException { + + Path ideasyBin = installationPath.resolve("bin/ideasy"); + String content = "#!/bin/bash\n\"" + ideasyBin + "\" gui\n"; + Path applicationsDir = this.context.getUserHome().resolve("Applications"); + this.context.getFileAccess().mkdirs(applicationsDir); + Path commandFile = applicationsDir.resolve("IDEasy GUI.command"); + Files.writeString(commandFile, content); + try { + Files.setPosixFilePermissions(commandFile, FileAccess.RWX_RX_RX); + } catch (UnsupportedOperationException e) { + LOG.debug("Could not set POSIX permissions on {}", commandFile); + } + IdeLogLevel.SUCCESS.log(LOG, "Created macOS launcher at {}", commandFile); + } + + private void installWindowsDesktopShortcut(Path installationPath) { + + Path ideasyExe = installationPath.resolve("bin\\ideasy.exe"); + Path icoPath = installationPath.resolve("gui\\logo.ico"); + String desktopPath = null; + try { + ProcessResult result = this.context.newProcess().executable("powershell") + .addArgs("-Command", "[Environment]::GetFolderPath('Desktop')") + .run(ProcessMode.DEFAULT_CAPTURE); + if (result.isSuccessful() && !result.getOut().isEmpty()) { + desktopPath = result.getOut().get(0).trim(); + } + } catch (Exception e) { + LOG.warn("Could not determine desktop path: {}", e.getMessage()); + } + if (desktopPath != null) { + createWindowsShortcut(Path.of(desktopPath, "IDEasy GUI.lnk"), ideasyExe, icoPath); + } + Path startMenu = this.context.getUserHome() + .resolve("AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs"); + if (Files.isDirectory(startMenu)) { + createWindowsShortcut(startMenu.resolve("IDEasy GUI.lnk"), ideasyExe, icoPath); + } + } + + private void createWindowsShortcut(Path lnkPath, Path targetExe, Path icoPath) { + + String ps = "$ws = New-Object -ComObject WScript.Shell; " + + "$s = $ws.CreateShortcut('" + lnkPath + "'); " + + "$s.TargetPath = '" + targetExe + "'; " + + "$s.Arguments = 'gui'; " + + "$s.IconLocation = '" + icoPath + ",0'; " + + "$s.Save()"; + try { + this.context.newProcess().executable("powershell").addArgs("-Command", ps) + .run(ProcessMode.DEFAULT_CAPTURE); + IdeLogLevel.SUCCESS.log(LOG, "Created shortcut at {}", lnkPath); + } catch (Exception e) { + LOG.warn("Failed to create shortcut at {}: {}", lnkPath, e.getMessage()); + } + } + private void setGitLongpaths() { this.context.getGitContext().findGitRequired(); Path configPath = this.context.getUserHome().resolve(".gitconfig"); diff --git a/cli/src/main/package/gui/linux/ideasy-gui.desktop b/cli/src/main/package/gui/linux/ideasy-gui.desktop new file mode 100644 index 0000000000..166e3ed293 --- /dev/null +++ b/cli/src/main/package/gui/linux/ideasy-gui.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=$[project.version] +Type=Application +Name=IDEasy GUI +Comment=Launch IDEasy GUI +Exec=IDEASY_BIN gui +Icon=IDEASY_ICON +Terminal=false +Categories=Development; +StartupNotify=true diff --git a/cli/src/main/package/gui/logo.ico b/cli/src/main/package/gui/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..a5db696ec1915e46b5b953e7a88a6454d7341e8e GIT binary patch literal 11512 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�C2GuM>} z(}yaBUHsX&l(MjRc4eppDUJF|SxOP?A5iA1ozw&KbQ7htvetn2QAYO$cEuW#&q_kL z+(DM2K;AtStZ?d8aR955r+W3b(36gpqutD{$0z5nW8PNG`C*-%bumLiTQMr&22bB{ z;Po3l9Ah!R^O5J~(1up=24SH zTWh=ikn|C(2E9y@kshfX8tgHmWgwRKsR3_c7$ZL1$nHS}!XmgJCD%b&6>m#Y#JBCo zv##4;ti7EKk7POwb}`}Srx^(zGtRgd%4Qck^hkb3>K z60lxd-5P-(L{fR0{j}bih@~lRR(Ek$WVposry%Em**Fg2ez$emVBfxQ&FZJQ9tmgT zHI0Je{v(h1RUe#T7s6sG@FL0o#Nm6ga6!Qia?-uRy}?*mK|xJ!jUQZS02?Nhe|E%iN5lA6W5kVuW~boUFtyK@ z-QCF;jg=cRgCk}>@6Hk;nr+W_5o9!KEOf-hOmBiSDnRFopb(0o;SS{5*Jy_J_ffBF zu>>DsaO@WY!zgX^oax~5Y4|#0atK1f;TkCEeeOmh+yg@F*7ZOB zBl#qPIrBG@*JSF{hs@LazbQ+%=?bRjz}by-(Vn!-VNWlxi6sW(DhsyIFTY7RGE3$V zu3|CB8LTenQ)^R|zL}TEyH$X#)jqgikgsu3THs`c_%Tr)A@fgIWFglm$Ep8*RC)qR zFADdE;Wra|^5)e&I_{lmQMF*cVLi3eAPpx)k+M`{0Sq4&S;P~tYW$T7E}9mXGon;2 znxEzH*R`^Zf0aV<4QOYc$p#~TI4iK{9fW<`FKrby)&DIb914d6SQ4w) z&a|30FC2~nq1;Az0q@eXKiuAbxRooardJV%rAI~EqVTK9{BI_!=bz6bBf1LtVo zf+7ChV1H9c#;@-@th9qpgwJC%=5OZHCOV8oW?H*dcYPks7#-$9oY1B?)!r*|8V}sN zJ{nWuM~)Jwq%5J`RpH)x8ct$c0$Fwm|J*Y7Mw#@=kZ7Kiq;apL>*nV4^i0lF_5qcM zsrFxPRIgyA=U~RkSZ+b^%&@bOq zwzl#z?prC!!Jt-uEqonY6vK}sfwcrzfvVjXeWgNjSZd!J_^9ftDBlwoSK#xC&d&J< zh%!`W3Oat*%;~~Z*Fb`9QvUTIBs3VY^O9Hjwc`)Ply&nBG68L~ z)kcgOVJ|$Hl{_BrIq$Pd5{!&?e1Ipe#L4pLn0Pm&m`^t;v-`)9h5tM`Sx3bdu(^jAV;uQR?;eP!(n|BlgJw(1=HZDvrAs7XQ%n8y=wXN|B`iOg*rjA($b z(i0WKep*=5ImqslTe9=+{lwr*H>-DWxc87-odN4Sclb3VGV5x_KFz3afY7+CR8^uh z<9En?t`Qxwjz_~~ppfUy3%n{~(Kqs3$7ub* zpYzOddWs0Ks+DA`MpG>V;#fj?P$vJB``U>mDP=4&S#BV%fAv#C*|rqy^QLp1b3xqP zCu-ic54Q5a^Ex>=JX!i5-S1aE*~R0IhkA0wOnDT}tuG51#6G2L_A>Sh9`muqhJTV3 zBK&r3=}K8(00h%oN&UK?{RtzC^4pknz%-6eEuY8c_sC#a$Pmn0k3fjm1K#&-WT40sML7CyZyfoC3P}$;tGiAMI6sjrI=O!>6 zYjM1ERr+?1Ry#T>Dr&p=9~<0-;b*x~&p6f`$B1&0D&zgANjS;K$e#V?aaS^R;mQJ! zO?$`Vf5xQ0!Qq0q#oJW9sTFUh9|Y0;S4Zt)*3Wo6@Q`5rrGgn~F>T}q?ZLZRmWKd*B8}mKZ-NK@SM5^Xmgv_b6&n;6YT;Qv&ZK2d+|0x%0xhg1xmFWdji z-GdFtS8md_2bOA8J>+lvpE Iw+#RP09bpZKL7v# literal 0 HcmV?d00001 diff --git a/cli/src/main/package/gui/logo.png b/cli/src/main/package/gui/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..82bc932633c7695b8b5a86c717ad461249b7ac58 GIT binary patch literal 11491 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 sAZTQA4chGXPvA0g!1N1NP&2 -} - -function print_success() { - echo -e "${GREEN}✓ $1${NC}" -} - -function print_info() { - echo -e "${YELLOW}ℹ $1${NC}" -} - -# Verify launcher script exists and is executable -if [ ! -f "$LAUNCHER_SCRIPT" ]; then - print_error "Launcher script not found: $LAUNCHER_SCRIPT" - exit 1 -fi - -chmod +x "$LAUNCHER_SCRIPT" - -# Resolve icon: use bundled devonfw.png if available, otherwise fall back to system theme -if [ -f "$ICON_PATH" ]; then - ICON="$(cd "$(dirname "$ICON_PATH")" && pwd)/$(basename "$ICON_PATH")" -else - ICON="application-x-executable" -fi - -# Create Desktop Entry (.desktop file) -function create_desktop_entry() { - local target_dir="$1" - local desktop_file="$target_dir/ideasy-gui.desktop" - - if ! mkdir -p "$target_dir" 2>/dev/null; then - print_error "Failed to create directory: $target_dir" - return 1 - fi - - if ! cat > "$desktop_file" </dev/null; then - print_error "Failed to make executable: $desktop_file" - return 1 - fi - - print_success "Created: $desktop_file" - return 0 -} - -# Create application menu entry -APPLICATIONS_DIR="$HOME/.local/share/applications" -if ! create_desktop_entry "$APPLICATIONS_DIR"; then - print_info "Note: Could not create application menu entry (may require additional permissions)" -else - # Refresh the application menu database so the entry appears immediately - update-desktop-database "$APPLICATIONS_DIR" 2>/dev/null || true - print_info "Launch from: Application Menu or Launcher" -fi - -# Determine desktop directory via XDG standard (respects custom Desktop locations). -# On systems where xdg-user-dirs was never configured (e.g. minimal WM setups), -# xdg-user-dir falls back to printing $HOME itself — guard against that so we -# don't drop a loose .desktop file directly into the user's home directory. -DESKTOP_DIR=$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop") -DESKTOP_DIR="${DESKTOP_DIR%/}" -if [ -z "$DESKTOP_DIR" ] || [ "$DESKTOP_DIR" = "$HOME" ]; then - DESKTOP_DIR="$HOME/Desktop" -fi -if [ -d "$DESKTOP_DIR" ]; then - if ! create_desktop_entry "$DESKTOP_DIR"; then - print_info "Note: Desktop entry creation requires write permissions" - else - # GNOME 3.28+: mark desktop file as trusted so it can be launched by double-click - gio set "$DESKTOP_DIR/ideasy-gui.desktop" "metadata::trusted" true 2>/dev/null || true - print_info "Launch from: Desktop" - fi -else - print_info "Desktop directory not found (Desktop feature may not be available)" -fi - -echo "" -print_success "IDEasy GUI shortcuts ready!" -echo "" -echo "Usage:" -echo " • Open your Application Menu and search for 'IDEasy GUI'" -echo " • Or double-click the shortcut on your Desktop" -echo " • First launch may take longer as Maven downloads dependencies" -echo "" diff --git a/gui/scripts/linux/launch-gui.sh b/gui/scripts/linux/launch-gui.sh deleted file mode 100755 index 0258583743..0000000000 --- a/gui/scripts/linux/launch-gui.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -# IDEasy GUI Launcher for Linux -# This script launches the IDEasy GUI using the native 'ide gui' command -# The GUI runs in the background, the script exits immediately - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -LOG_FILE="$HOME/.ideasy-gui.log" - -# Ignore SIGHUP so this script (and what it launches) survives even if the -# process that handed off execution (desktop session bus, gio launch, etc.) -# disconnects before we are done. -trap '' HUP - -# When launched from an application menu (Terminal=false), there is no console to -# show errors on. Redirect everything to a log file from the start so failures -# (e.g. 'ide' missing from the launcher's PATH) are diagnosable after the fact -exec >> "$LOG_FILE" 2>&1 -echo "---- $(date) ----" - -function notify_error() { - command -v notify-send &> /dev/null && notify-send -i dialog-error "IDEasy GUI" "$1" -} - -if [ ! -f "$PROJECT_ROOT/pom.xml" ]; then - echo "Error: IDEasy project root not found at $PROJECT_ROOT" - echo "Please ensure this script is located in: IDEasy/gui/scripts/linux/" - notify_error "Project root not found at $PROJECT_ROOT" - exit 1 -fi - -# 'ide' is a shell function defined in $IDE_ROOT/_ide/installation/functions and -# sourced by ~/.bashrc / ~/.zshrc — those only get sourced in interactive shells. -# Application launchers (Terminal=false) start a plain, non-interactive shell, so -# 'ide' is invisible there even though it works fine from a terminal. Route through -# an interactive bash so the function gets sourced regardless of launch context. -if ! bash -ic "command -v ide" &> /dev/null; then - echo "Error: IDEasy is not installed or not in PATH" - echo "Please install IDEasy first: https://github.com/devonfw/IDEasy#setup" - notify_error "'ide' command not found (see $LOG_FILE)" - exit 1 -fi - -cd "$PROJECT_ROOT" - -# Launch IDE GUI in background and exit immediately -bash -ic "ide gui" &2 -} - -function print_success() { - echo -e "${GREEN}✓ $1${NC}" -} - -function print_info() { - echo -e "${YELLOW}ℹ $1${NC}" -} - -# Verify launcher script exists and is executable -if [ ! -f "$LAUNCHER_SCRIPT" ]; then - print_error "Launcher script not found: $LAUNCHER_SCRIPT" - exit 1 -fi - -chmod +x "$LAUNCHER_SCRIPT" - -# Create an alias/shortcut using osascript (AppleScript) -# Falls back to a symlink if Finder is not running or AppleScript fails -function create_alias() { - local source="$1" - local target_dir="$2" - local target_name="$3" - - if [ ! -f "$source" ]; then - print_error "Source file not found: $source" - return 1 - fi - - if ! mkdir -p "$target_dir" 2>/dev/null; then - print_error "Failed to create directory: $target_dir" - return 1 - fi - - # Properly escape paths for AppleScript - local escaped_source="$(printf '%s\n' "$source" | sed 's/\\/\\\\/g; s/"/\\"/g')" - local escaped_target_dir="$(printf '%s\n' "$target_dir" | sed 's/\\/\\\\/g; s/"/\\"/g')" - local escaped_target_name="$(printf '%s\n' "$target_name" | sed 's/\\/\\\\/g; s/"/\\"/g')" - - local output - output=$(osascript 2>&1 </dev/null; then - print_success "Created symlink: $target_dir/$target_name.command" - return 0 - fi - - print_error "Failed to create shortcut: $target_dir/$target_name" - [ -n "$output" ] && print_error "Reason: $output" - return 1 -} - -# Create Applications alias -APPS_DIR="$HOME/Applications" -if ! create_alias "$LAUNCHER_SCRIPT" "$APPS_DIR" "IDEasy GUI"; then - print_info "Note: Could not create Applications shortcut (may require additional permissions)" -else - print_info "Launch from: Applications > IDEasy GUI" -fi - -# Create Desktop alias -DESKTOP_DIR="$HOME/Desktop" -if create_alias "$LAUNCHER_SCRIPT" "$DESKTOP_DIR" "IDEasy GUI"; then - print_info "Launch from: Desktop" -else - print_info "Note: Desktop shortcut creation requires manual steps" - print_info "You can manually create a shortcut by:" - print_info "1. Open Finder" - print_info "2. Go to $SCRIPT_DIR" - print_info "3. Right-click 'launch-gui.command' → Make Alias" - print_info "4. Drag alias to Desktop or Applications" -fi - -echo "" -print_success "IDEasy GUI shortcuts ready!" -echo "" -echo "Usage:" -echo " • Open Finder and go to Applications" -echo " • Double-click 'IDEasy GUI' to launch" -echo " • Or double-click the Desktop shortcut" -echo "" diff --git a/gui/scripts/macos/launch-gui.command b/gui/scripts/macos/launch-gui.command deleted file mode 100755 index a3300755b7..0000000000 --- a/gui/scripts/macos/launch-gui.command +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -# IDEasy GUI Launcher for macOS -# This script launches the IDEasy GUI using the native 'ide gui' command -# The GUI runs in the background, the script exits immediately - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" - -if [ ! -f "$PROJECT_ROOT/pom.xml" ]; then - echo "Error: IDEasy project root not found at $PROJECT_ROOT" - echo "Please ensure this script is located in: IDEasy/gui/scripts/macos/" - read -p "Press Enter to close..." - exit 1 -fi - -# 'ide' is a shell function defined in $IDE_ROOT/_ide/installation/functions and -# sourced by ~/.bashrc / ~/.zshrc — those only get sourced in interactive shells. -# Double-clicking a .command file runs it via its #!/bin/bash shebang directly, -# which is a non-interactive shell, so 'ide' is invisible there even though it -# works fine from a regular Terminal window. Route through an interactive bash -# so the function gets sourced regardless of how this script was launched. -if ! bash -ic "command -v ide" &> /dev/null; then - echo "" - echo "Error: IDEasy is not installed" - echo "" - echo "Please install IDEasy first:" - echo "https://github.com/devonfw/IDEasy#setup" - echo "" - read -p "Press Enter to close..." - exit 1 -fi - -cd "$PROJECT_ROOT" - -# Launch IDE GUI in background and exit immediately -LOG_FILE="$HOME/.ideasy-gui.log" -bash -ic "ide gui" >> "$LOG_FILE" 2>&1 & -disown - -exit 0 diff --git a/gui/scripts/windows/create-shortcuts.ps1 b/gui/scripts/windows/create-shortcuts.ps1 deleted file mode 100644 index 24e3aeaba7..0000000000 --- a/gui/scripts/windows/create-shortcuts.ps1 +++ /dev/null @@ -1,153 +0,0 @@ -#Requires -Version 5.0 - -# IDEasy GUI Launcher - Create Windows Start Menu Shortcut and Desktop Link -# This script creates shortcuts to launch the IDEasy GUI from Windows Start Menu and Desktop - -param ( - [switch]$SkipDesktop, - [switch]$SkipStartMenu -) - -$ErrorActionPreference = 'Stop' - -$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path -$guiDir = Split-Path -Parent (Split-Path -Parent $scriptDir) -$projectRoot = Split-Path -Parent $guiDir - -$launcherBat = Join-Path $scriptDir "launch-gui.bat" -$launcherVbs = Join-Path $scriptDir "launch-gui-silent.vbs" -$pomFile = Join-Path $projectRoot "pom.xml" - -if (-not (Test-Path $launcherBat)) { - Write-Host "Error: launch-gui.bat not found" -ForegroundColor Red - exit 1 -} - -if (-not (Test-Path $launcherVbs)) { - Write-Host "Error: launch-gui-silent.vbs not found" -ForegroundColor Red - exit 1 -} - -if (-not (Test-Path $pomFile)) { - Write-Host "Error: pom.xml not found" -ForegroundColor Red - exit 1 -} - -Write-Host "IDEasy GUI Launcher Setup" -ForegroundColor Cyan -Write-Host "Project: $projectRoot" -ForegroundColor Cyan -Write-Host "" - -# Wrap the PNG bytes directly into an ICO container (Windows Vista+ supports PNG frames in ICO). -# This preserves full color depth and alpha transparency without any image processing. -# The ICO is written next to the scripts so the shortcut has a stable reference path. -# Returns $true on success, $false on failure. -function Convert-PngToIco { - param([string]$PngPath, [string]$IcoPath) - try { - $pngBytes = [System.IO.File]::ReadAllBytes($PngPath) - - # PNG dimensions are stored big-endian in the IHDR chunk at bytes 16-23 - $width = ($pngBytes[16] -shl 24) -bor ($pngBytes[17] -shl 16) -bor ($pngBytes[18] -shl 8) -bor $pngBytes[19] - $height = ($pngBytes[20] -shl 24) -bor ($pngBytes[21] -shl 16) -bor ($pngBytes[22] -shl 8) -bor $pngBytes[23] - - # ICO directory encodes 256 as 0 - $icoW = if ($width -ge 256) { [byte]0 } else { [byte]$width } - $icoH = if ($height -ge 256) { [byte]0 } else { [byte]$height } - - $sizeBytes = [System.BitConverter]::GetBytes([int32]$pngBytes.Length) - $offsetBytes = [System.BitConverter]::GetBytes([int32](6 + 16)) # header(6) + one dir entry(16) - - # ICO header (6 bytes) + single directory entry (16 bytes) - $icoHeader = [byte[]]( - 0x00, 0x00, # reserved - 0x01, 0x00, # type: icon - 0x01, 0x00, # image count: 1 - $icoW, $icoH, 0x00, 0x00, # w, h, colorCount, reserved - 0x01, 0x00, # color planes - 0x20, 0x00, # bits per pixel (32) - $sizeBytes[0], $sizeBytes[1], $sizeBytes[2], $sizeBytes[3], # image data size - $offsetBytes[0], $offsetBytes[1], $offsetBytes[2], $offsetBytes[3] # image data offset - ) - - $stream = [System.IO.FileStream]::new($IcoPath, [System.IO.FileMode]::Create) - $stream.Write($icoHeader, 0, $icoHeader.Length) - $stream.Write($pngBytes, 0, $pngBytes.Length) - $stream.Close() - return $true - } - catch { - return $false - } -} - -# Resolve icon location: prefer devonfw.png converted to ICO, fall back to shell32 -$pngSource = Join-Path $guiDir "src\main\resources\com\devonfw\ide\gui\assets\devonfw.png" -$generatedIco = Join-Path $scriptDir "ideasy-gui.ico" -$iconLocation = "$env:SystemRoot\system32\shell32.dll,1" - -if (Test-Path $pngSource) { - if (-not (Test-Path $generatedIco)) { - if (Convert-PngToIco -PngPath $pngSource -IcoPath $generatedIco) { - Write-Host "Icon generated from devonfw.png" -ForegroundColor Cyan - } else { - Write-Host "Note: Icon conversion failed, using default icon" -ForegroundColor Yellow - } - } - if (Test-Path $generatedIco) { - $iconLocation = "$generatedIco,0" - } -} elseif (Test-Path (Join-Path $scriptDir "icon.ico")) { - $iconLocation = "$(Join-Path $scriptDir 'icon.ico'),0" -} - -function Create-Shortcut { - param( - [string]$Path, - [string]$Target, - [string]$Arguments, - [string]$Description - ) - - $wshShell = $null - try { - Write-Host "Creating shortcut: $Path" - - $wshShell = New-Object -ComObject WScript.Shell - $shortcut = $wshShell.CreateShortcut($Path) - $shortcut.TargetPath = $Target - $shortcut.Arguments = $Arguments - $shortcut.WorkingDirectory = $projectRoot - $shortcut.Description = $Description - $shortcut.IconLocation = $iconLocation - - $shortcut.Save() - Write-Host "Created: $Path" -ForegroundColor Green - } - catch { - Write-Host "Error creating shortcut: $($_.Exception.Message)" -ForegroundColor Red - } - finally { - if ($wshShell) { - [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshShell) | Out-Null - } - } -} - -# Target wscript.exe running the silent VBS wrapper instead of launch-gui.bat directly, -# so no console window ever appears - independent of the user's terminal "close on exit" setting. -$wscriptPath = Join-Path "$env:SystemRoot\System32" "wscript.exe" -$launcherArgs = "`"$launcherVbs`"" - -if (-not $SkipDesktop) { - # Use Shell32 to resolve the Desktop folder — works correctly with OneDrive-redirected Desktops - $desktopShortcut = Join-Path ([Environment]::GetFolderPath('Desktop')) "IDEasy GUI.lnk" - Create-Shortcut -Path $desktopShortcut -Target $wscriptPath -Arguments $launcherArgs -Description "Launch IDEasy GUI" -} - -if (-not $SkipStartMenu) { - $startMenuShortcut = Join-Path "$env:APPDATA\Microsoft\Windows\Start Menu\Programs" "IDEasy GUI.lnk" - Create-Shortcut -Path $startMenuShortcut -Target $wscriptPath -Arguments $launcherArgs -Description "Launch IDEasy GUI" -} - -Write-Host "" -Write-Host "Setup complete!" -ForegroundColor Green diff --git a/gui/scripts/windows/launch-gui-silent.vbs b/gui/scripts/windows/launch-gui-silent.vbs deleted file mode 100644 index f5432adebb..0000000000 --- a/gui/scripts/windows/launch-gui-silent.vbs +++ /dev/null @@ -1,11 +0,0 @@ -' Runs launch-gui.bat completely hidden (no console window), regardless of the -' user's terminal "close on exit" settings. The shortcut targets this file via -' wscript.exe instead of launch-gui.bat directly so no window ever appears. -Dim shell, fso, scriptDir, batPath - -Set shell = CreateObject("WScript.Shell") -Set fso = CreateObject("Scripting.FileSystemObject") -scriptDir = fso.GetParentFolderName(WScript.ScriptFullName) -batPath = fso.BuildPath(scriptDir, "launch-gui.bat") - -shell.Run """" & batPath & """", 0, False diff --git a/gui/scripts/windows/launch-gui.bat b/gui/scripts/windows/launch-gui.bat deleted file mode 100644 index 0f96d1cbc3..0000000000 --- a/gui/scripts/windows/launch-gui.bat +++ /dev/null @@ -1,50 +0,0 @@ -@echo off -setlocal - -REM IDEasy GUI Launcher for Windows - -set "SCRIPT_DIR=%~dp0" - -REM Desktop/Start-Menu shortcuts are spawned by explorer.exe, which caches its -REM environment from login and does not pick up registry PATH/IDE_ROOT changes -REM made by the IDEasy installer until the user logs off or restarts explorer. -REM Re-read IDE_ROOT directly from the registry and extend PATH with it here, -REM so the shortcut works even with explorer's stale inherited environment. -if not defined IDE_ROOT ( - for /f "tokens=2,*" %%A in ('reg query "HKCU\Environment" /v IDE_ROOT 2^>nul') do set "IDE_ROOT=%%B" -) -if defined IDE_ROOT ( - set "PATH=%IDE_ROOT%\_ide\installation\bin;%PATH%" -) - -REM Check if ide command exists -where ide >nul 2>&1 || ( - echo. - echo Error: IDEasy is not installed or not in PATH - echo If you just installed IDEasy, log off/on once or restart Explorer so - echo the desktop shortcut picks up the updated environment. - echo https://github.com/devonfw/IDEasy#setup - echo. - pause - exit /b 1 -) - -REM Determine project root safely -pushd "%SCRIPT_DIR%..\..\.." -set "PROJECT_ROOT=%CD%" -popd - -REM Check project structure -if not exist "%PROJECT_ROOT%\pom.xml" ( - echo Error: IDEasy project root not found at %PROJECT_ROOT% - echo. - pause - exit /b 1 -) - -REM Launch GUI -cd /d "%PROJECT_ROOT%" -echo Starting IDEasy GUI... -START "" /B cmd /c ide gui >> "%USERPROFILE%\.ideasy-gui.log" 2>&1 - -exit /b 0 From 1724881f3eaec34030c63e684deb9dc4576fdce8 Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Tue, 7 Jul 2026 10:39:11 +0200 Subject: [PATCH 12/16] #1917: create desktop shortcut on ide installation --- .../tools/ide/tool/IdeasyCommandlet.java | 115 +++++++++++++ .../main/package/gui/linux/ideasy-gui.desktop | 10 ++ cli/src/main/package/gui/logo.ico | Bin 0 -> 11512 bytes cli/src/main/package/gui/logo.png | Bin 0 -> 11491 bytes gui/scripts/linux/create-shortcuts.sh | 122 -------------- gui/scripts/linux/launch-gui.sh | 51 ------ gui/scripts/macos/create-shortcuts.sh | 121 -------------- gui/scripts/macos/launch-gui.command | 41 ----- gui/scripts/windows/create-shortcuts.ps1 | 153 ------------------ gui/scripts/windows/launch-gui-silent.vbs | 11 -- gui/scripts/windows/launch-gui.bat | 50 ------ 11 files changed, 125 insertions(+), 549 deletions(-) create mode 100644 cli/src/main/package/gui/linux/ideasy-gui.desktop create mode 100644 cli/src/main/package/gui/logo.ico create mode 100644 cli/src/main/package/gui/logo.png delete mode 100755 gui/scripts/linux/create-shortcuts.sh delete mode 100755 gui/scripts/linux/launch-gui.sh delete mode 100755 gui/scripts/macos/create-shortcuts.sh delete mode 100755 gui/scripts/macos/launch-gui.command delete mode 100644 gui/scripts/windows/create-shortcuts.ps1 delete mode 100644 gui/scripts/windows/launch-gui-silent.vbs delete mode 100644 gui/scripts/windows/launch-gui.bat diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java index a762cd2c2b..bd2aabb9dd 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java @@ -1,5 +1,6 @@ package com.devonfw.tools.ide.tool; +import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.nio.file.Files; @@ -295,6 +296,7 @@ public void installIdeasy(Path cwd) { addToShellRc(BASHRC, ideRoot, null); addToShellRc(ZSHRC, ideRoot, "autoload -U +X bashcompinit && bashcompinit"); installIdeasyWindowsEnv(ideRoot, installationPath); + installDesktopShortcut(installationPath); IdeLogLevel.SUCCESS.log(LOG, "IDEasy has been installed successfully on your system."); LOG.warn("IDEasy has been setup for new shells but it cannot work in your current shell(s).\n" + "To use it here, run 'source ~/.bashrc' (or your shell config). Otherwise, open a new terminal or reboot."); @@ -331,6 +333,119 @@ private void installIdeasyWindowsEnv(Path ideRoot, Path installationPath) { } } + private void installDesktopShortcut(Path installationPath) { + + try { + if (this.context.getSystemInfo().isLinux()) { + installLinuxDesktopShortcut(installationPath); + } else if (this.context.getSystemInfo().isMac()) { + installMacDesktopShortcut(installationPath); + } else if (this.context.getSystemInfo().isWindows()) { + installWindowsDesktopShortcut(installationPath); + } + } catch (Exception e) { + LOG.warn("Failed to create desktop shortcut: {}", e.getMessage()); + } + } + + private void installLinuxDesktopShortcut(Path installationPath) throws IOException { + + Path templateFile = installationPath.resolve("gui/linux/ideasy-gui.desktop"); + if (!Files.exists(templateFile)) { + LOG.warn("Desktop file template not found at {}. Skipping desktop shortcut creation.", templateFile); + return; + } + Path ideasyBin = installationPath.resolve("bin/ideasy"); + Path logoPath = installationPath.resolve("gui/logo.png"); + String content = Files.readString(templateFile) + .replace("IDEASY_BIN", ideasyBin.toString()) + .replace("IDEASY_ICON", logoPath.toString()); + + Path applicationsDir = this.context.getUserHome().resolve(".local/share/applications"); + this.context.getFileAccess().mkdirs(applicationsDir); + Path desktopFile = applicationsDir.resolve("ideasy-gui.desktop"); + Files.writeString(desktopFile, content); + try { + Files.setPosixFilePermissions(desktopFile, FileAccess.RWX_RX_RX); + } catch (UnsupportedOperationException e) { + LOG.debug("Could not set POSIX permissions on {}", desktopFile); + } + + // without this, the new entry is only visible in application menus after the next login + try { + this.context.newProcess().executable("update-desktop-database").addArg(applicationsDir.toString()) + .run(ProcessMode.DEFAULT_CAPTURE); + } catch (Exception e) { + LOG.debug("update-desktop-database failed (optional): {}", e.getMessage()); + } + + // Mark as trusted for GNOME 3.28+ so the shortcut is executable without prompting + try { + this.context.newProcess().executable("gio") + .addArgs("set", desktopFile.toString(), "metadata::trusted", "true") + .run(ProcessMode.DEFAULT_CAPTURE); + } catch (Exception e) { + LOG.debug("gio set trusted failed (optional): {}", e.getMessage()); + } + + IdeLogLevel.SUCCESS.log(LOG, "Created desktop shortcut at {}", desktopFile); + } + + private void installMacDesktopShortcut(Path installationPath) throws IOException { + + Path ideasyBin = installationPath.resolve("bin/ideasy"); + String content = "#!/bin/bash\n\"" + ideasyBin + "\" gui\n"; + Path applicationsDir = this.context.getUserHome().resolve("Applications"); + this.context.getFileAccess().mkdirs(applicationsDir); + Path commandFile = applicationsDir.resolve("IDEasy GUI.command"); + Files.writeString(commandFile, content); + try { + Files.setPosixFilePermissions(commandFile, FileAccess.RWX_RX_RX); + } catch (UnsupportedOperationException e) { + LOG.debug("Could not set POSIX permissions on {}", commandFile); + } + IdeLogLevel.SUCCESS.log(LOG, "Created macOS launcher at {}", commandFile); + } + + private void installWindowsDesktopShortcut(Path installationPath) { + + Path ideasyExe = installationPath.resolve("bin\\ideasy.exe"); + Path icoPath = installationPath.resolve("gui\\logo.ico"); + // Shell Folders contains the already-expanded Desktop path, including OneDrive-redirected locations + WindowsHelper helper = WindowsHelper.get(this.context); + String desktopStr = helper.getRegistryValue( + "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", "Desktop"); + Path desktopPath = (desktopStr != null) ? Path.of(desktopStr) : this.context.getUserHome().resolve("Desktop"); + this.context.getFileAccess().mkdirs(desktopPath); + createWindowsShortcut(desktopPath.resolve("IDEasy GUI.lnk"), ideasyExe, icoPath); + Path startMenu = this.context.getUserHome() + .resolve("AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs"); + if (Files.isDirectory(startMenu)) { + createWindowsShortcut(startMenu.resolve("IDEasy GUI.lnk"), ideasyExe, icoPath); + } + } + + private static String psEscapePath(Path path) { + return path.toString().replace("'", "''"); + } + + private void createWindowsShortcut(Path lnkPath, Path targetExe, Path icoPath) { + + String ps = "$ws = New-Object -ComObject WScript.Shell; " + + "$s = $ws.CreateShortcut('" + psEscapePath(lnkPath) + "'); " + + "$s.TargetPath = '" + psEscapePath(targetExe) + "'; " + + "$s.Arguments = 'gui'; " + + "$s.IconLocation = '" + psEscapePath(icoPath) + ",0'; " + + "$s.Save()"; + try { + this.context.newProcess().executable("powershell").addArgs("-Command", ps) + .run(ProcessMode.DEFAULT_CAPTURE); + IdeLogLevel.SUCCESS.log(LOG, "Created shortcut at {}", lnkPath); + } catch (Exception e) { + LOG.warn("Failed to create shortcut at {}: {}", lnkPath, e.getMessage()); + } + } + private void setGitLongpaths() { this.context.getGitContext().findGitRequired(); Path configPath = this.context.getUserHome().resolve(".gitconfig"); diff --git a/cli/src/main/package/gui/linux/ideasy-gui.desktop b/cli/src/main/package/gui/linux/ideasy-gui.desktop new file mode 100644 index 0000000000..166e3ed293 --- /dev/null +++ b/cli/src/main/package/gui/linux/ideasy-gui.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=$[project.version] +Type=Application +Name=IDEasy GUI +Comment=Launch IDEasy GUI +Exec=IDEASY_BIN gui +Icon=IDEASY_ICON +Terminal=false +Categories=Development; +StartupNotify=true diff --git a/cli/src/main/package/gui/logo.ico b/cli/src/main/package/gui/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..a5db696ec1915e46b5b953e7a88a6454d7341e8e GIT binary patch literal 11512 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�C2GuM>} z(}yaBUHsX&l(MjRc4eppDUJF|SxOP?A5iA1ozw&KbQ7htvetn2QAYO$cEuW#&q_kL z+(DM2K;AtStZ?d8aR955r+W3b(36gpqutD{$0z5nW8PNG`C*-%bumLiTQMr&22bB{ z;Po3l9Ah!R^O5J~(1up=24SH zTWh=ikn|C(2E9y@kshfX8tgHmWgwRKsR3_c7$ZL1$nHS}!XmgJCD%b&6>m#Y#JBCo zv##4;ti7EKk7POwb}`}Srx^(zGtRgd%4Qck^hkb3>K z60lxd-5P-(L{fR0{j}bih@~lRR(Ek$WVposry%Em**Fg2ez$emVBfxQ&FZJQ9tmgT zHI0Je{v(h1RUe#T7s6sG@FL0o#Nm6ga6!Qia?-uRy}?*mK|xJ!jUQZS02?Nhe|E%iN5lA6W5kVuW~boUFtyK@ z-QCF;jg=cRgCk}>@6Hk;nr+W_5o9!KEOf-hOmBiSDnRFopb(0o;SS{5*Jy_J_ffBF zu>>DsaO@WY!zgX^oax~5Y4|#0atK1f;TkCEeeOmh+yg@F*7ZOB zBl#qPIrBG@*JSF{hs@LazbQ+%=?bRjz}by-(Vn!-VNWlxi6sW(DhsyIFTY7RGE3$V zu3|CB8LTenQ)^R|zL}TEyH$X#)jqgikgsu3THs`c_%Tr)A@fgIWFglm$Ep8*RC)qR zFADdE;Wra|^5)e&I_{lmQMF*cVLi3eAPpx)k+M`{0Sq4&S;P~tYW$T7E}9mXGon;2 znxEzH*R`^Zf0aV<4QOYc$p#~TI4iK{9fW<`FKrby)&DIb914d6SQ4w) z&a|30FC2~nq1;Az0q@eXKiuAbxRooardJV%rAI~EqVTK9{BI_!=bz6bBf1LtVo zf+7ChV1H9c#;@-@th9qpgwJC%=5OZHCOV8oW?H*dcYPks7#-$9oY1B?)!r*|8V}sN zJ{nWuM~)Jwq%5J`RpH)x8ct$c0$Fwm|J*Y7Mw#@=kZ7Kiq;apL>*nV4^i0lF_5qcM zsrFxPRIgyA=U~RkSZ+b^%&@bOq zwzl#z?prC!!Jt-uEqonY6vK}sfwcrzfvVjXeWgNjSZd!J_^9ftDBlwoSK#xC&d&J< zh%!`W3Oat*%;~~Z*Fb`9QvUTIBs3VY^O9Hjwc`)Ply&nBG68L~ z)kcgOVJ|$Hl{_BrIq$Pd5{!&?e1Ipe#L4pLn0Pm&m`^t;v-`)9h5tM`Sx3bdu(^jAV;uQR?;eP!(n|BlgJw(1=HZDvrAs7XQ%n8y=wXN|B`iOg*rjA($b z(i0WKep*=5ImqslTe9=+{lwr*H>-DWxc87-odN4Sclb3VGV5x_KFz3afY7+CR8^uh z<9En?t`Qxwjz_~~ppfUy3%n{~(Kqs3$7ub* zpYzOddWs0Ks+DA`MpG>V;#fj?P$vJB``U>mDP=4&S#BV%fAv#C*|rqy^QLp1b3xqP zCu-ic54Q5a^Ex>=JX!i5-S1aE*~R0IhkA0wOnDT}tuG51#6G2L_A>Sh9`muqhJTV3 zBK&r3=}K8(00h%oN&UK?{RtzC^4pknz%-6eEuY8c_sC#a$Pmn0k3fjm1K#&-WT40sML7CyZyfoC3P}$;tGiAMI6sjrI=O!>6 zYjM1ERr+?1Ry#T>Dr&p=9~<0-;b*x~&p6f`$B1&0D&zgANjS;K$e#V?aaS^R;mQJ! zO?$`Vf5xQ0!Qq0q#oJW9sTFUh9|Y0;S4Zt)*3Wo6@Q`5rrGgn~F>T}q?ZLZRmWKd*B8}mKZ-NK@SM5^Xmgv_b6&n;6YT;Qv&ZK2d+|0x%0xhg1xmFWdji z-GdFtS8md_2bOA8J>+lvpE Iw+#RP09bpZKL7v# literal 0 HcmV?d00001 diff --git a/cli/src/main/package/gui/logo.png b/cli/src/main/package/gui/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..82bc932633c7695b8b5a86c717ad461249b7ac58 GIT binary patch literal 11491 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 sAZTQA4chGXPvA0g!1N1NP&2 -} - -function print_success() { - echo -e "${GREEN}✓ $1${NC}" -} - -function print_info() { - echo -e "${YELLOW}ℹ $1${NC}" -} - -# Verify launcher script exists and is executable -if [ ! -f "$LAUNCHER_SCRIPT" ]; then - print_error "Launcher script not found: $LAUNCHER_SCRIPT" - exit 1 -fi - -chmod +x "$LAUNCHER_SCRIPT" - -# Resolve icon: use bundled devonfw.png if available, otherwise fall back to system theme -if [ -f "$ICON_PATH" ]; then - ICON="$(cd "$(dirname "$ICON_PATH")" && pwd)/$(basename "$ICON_PATH")" -else - ICON="application-x-executable" -fi - -# Create Desktop Entry (.desktop file) -function create_desktop_entry() { - local target_dir="$1" - local desktop_file="$target_dir/ideasy-gui.desktop" - - if ! mkdir -p "$target_dir" 2>/dev/null; then - print_error "Failed to create directory: $target_dir" - return 1 - fi - - if ! cat > "$desktop_file" </dev/null; then - print_error "Failed to make executable: $desktop_file" - return 1 - fi - - print_success "Created: $desktop_file" - return 0 -} - -# Create application menu entry -APPLICATIONS_DIR="$HOME/.local/share/applications" -if ! create_desktop_entry "$APPLICATIONS_DIR"; then - print_info "Note: Could not create application menu entry (may require additional permissions)" -else - # Refresh the application menu database so the entry appears immediately - update-desktop-database "$APPLICATIONS_DIR" 2>/dev/null || true - print_info "Launch from: Application Menu or Launcher" -fi - -# Determine desktop directory via XDG standard (respects custom Desktop locations). -# On systems where xdg-user-dirs was never configured (e.g. minimal WM setups), -# xdg-user-dir falls back to printing $HOME itself — guard against that so we -# don't drop a loose .desktop file directly into the user's home directory. -DESKTOP_DIR=$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop") -DESKTOP_DIR="${DESKTOP_DIR%/}" -if [ -z "$DESKTOP_DIR" ] || [ "$DESKTOP_DIR" = "$HOME" ]; then - DESKTOP_DIR="$HOME/Desktop" -fi -if [ -d "$DESKTOP_DIR" ]; then - if ! create_desktop_entry "$DESKTOP_DIR"; then - print_info "Note: Desktop entry creation requires write permissions" - else - # GNOME 3.28+: mark desktop file as trusted so it can be launched by double-click - gio set "$DESKTOP_DIR/ideasy-gui.desktop" "metadata::trusted" true 2>/dev/null || true - print_info "Launch from: Desktop" - fi -else - print_info "Desktop directory not found (Desktop feature may not be available)" -fi - -echo "" -print_success "IDEasy GUI shortcuts ready!" -echo "" -echo "Usage:" -echo " • Open your Application Menu and search for 'IDEasy GUI'" -echo " • Or double-click the shortcut on your Desktop" -echo " • First launch may take longer as Maven downloads dependencies" -echo "" diff --git a/gui/scripts/linux/launch-gui.sh b/gui/scripts/linux/launch-gui.sh deleted file mode 100755 index 0258583743..0000000000 --- a/gui/scripts/linux/launch-gui.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -# IDEasy GUI Launcher for Linux -# This script launches the IDEasy GUI using the native 'ide gui' command -# The GUI runs in the background, the script exits immediately - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -LOG_FILE="$HOME/.ideasy-gui.log" - -# Ignore SIGHUP so this script (and what it launches) survives even if the -# process that handed off execution (desktop session bus, gio launch, etc.) -# disconnects before we are done. -trap '' HUP - -# When launched from an application menu (Terminal=false), there is no console to -# show errors on. Redirect everything to a log file from the start so failures -# (e.g. 'ide' missing from the launcher's PATH) are diagnosable after the fact -exec >> "$LOG_FILE" 2>&1 -echo "---- $(date) ----" - -function notify_error() { - command -v notify-send &> /dev/null && notify-send -i dialog-error "IDEasy GUI" "$1" -} - -if [ ! -f "$PROJECT_ROOT/pom.xml" ]; then - echo "Error: IDEasy project root not found at $PROJECT_ROOT" - echo "Please ensure this script is located in: IDEasy/gui/scripts/linux/" - notify_error "Project root not found at $PROJECT_ROOT" - exit 1 -fi - -# 'ide' is a shell function defined in $IDE_ROOT/_ide/installation/functions and -# sourced by ~/.bashrc / ~/.zshrc — those only get sourced in interactive shells. -# Application launchers (Terminal=false) start a plain, non-interactive shell, so -# 'ide' is invisible there even though it works fine from a terminal. Route through -# an interactive bash so the function gets sourced regardless of launch context. -if ! bash -ic "command -v ide" &> /dev/null; then - echo "Error: IDEasy is not installed or not in PATH" - echo "Please install IDEasy first: https://github.com/devonfw/IDEasy#setup" - notify_error "'ide' command not found (see $LOG_FILE)" - exit 1 -fi - -cd "$PROJECT_ROOT" - -# Launch IDE GUI in background and exit immediately -bash -ic "ide gui" &2 -} - -function print_success() { - echo -e "${GREEN}✓ $1${NC}" -} - -function print_info() { - echo -e "${YELLOW}ℹ $1${NC}" -} - -# Verify launcher script exists and is executable -if [ ! -f "$LAUNCHER_SCRIPT" ]; then - print_error "Launcher script not found: $LAUNCHER_SCRIPT" - exit 1 -fi - -chmod +x "$LAUNCHER_SCRIPT" - -# Create an alias/shortcut using osascript (AppleScript) -# Falls back to a symlink if Finder is not running or AppleScript fails -function create_alias() { - local source="$1" - local target_dir="$2" - local target_name="$3" - - if [ ! -f "$source" ]; then - print_error "Source file not found: $source" - return 1 - fi - - if ! mkdir -p "$target_dir" 2>/dev/null; then - print_error "Failed to create directory: $target_dir" - return 1 - fi - - # Properly escape paths for AppleScript - local escaped_source="$(printf '%s\n' "$source" | sed 's/\\/\\\\/g; s/"/\\"/g')" - local escaped_target_dir="$(printf '%s\n' "$target_dir" | sed 's/\\/\\\\/g; s/"/\\"/g')" - local escaped_target_name="$(printf '%s\n' "$target_name" | sed 's/\\/\\\\/g; s/"/\\"/g')" - - local output - output=$(osascript 2>&1 </dev/null; then - print_success "Created symlink: $target_dir/$target_name.command" - return 0 - fi - - print_error "Failed to create shortcut: $target_dir/$target_name" - [ -n "$output" ] && print_error "Reason: $output" - return 1 -} - -# Create Applications alias -APPS_DIR="$HOME/Applications" -if ! create_alias "$LAUNCHER_SCRIPT" "$APPS_DIR" "IDEasy GUI"; then - print_info "Note: Could not create Applications shortcut (may require additional permissions)" -else - print_info "Launch from: Applications > IDEasy GUI" -fi - -# Create Desktop alias -DESKTOP_DIR="$HOME/Desktop" -if create_alias "$LAUNCHER_SCRIPT" "$DESKTOP_DIR" "IDEasy GUI"; then - print_info "Launch from: Desktop" -else - print_info "Note: Desktop shortcut creation requires manual steps" - print_info "You can manually create a shortcut by:" - print_info "1. Open Finder" - print_info "2. Go to $SCRIPT_DIR" - print_info "3. Right-click 'launch-gui.command' → Make Alias" - print_info "4. Drag alias to Desktop or Applications" -fi - -echo "" -print_success "IDEasy GUI shortcuts ready!" -echo "" -echo "Usage:" -echo " • Open Finder and go to Applications" -echo " • Double-click 'IDEasy GUI' to launch" -echo " • Or double-click the Desktop shortcut" -echo "" diff --git a/gui/scripts/macos/launch-gui.command b/gui/scripts/macos/launch-gui.command deleted file mode 100755 index a3300755b7..0000000000 --- a/gui/scripts/macos/launch-gui.command +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -# IDEasy GUI Launcher for macOS -# This script launches the IDEasy GUI using the native 'ide gui' command -# The GUI runs in the background, the script exits immediately - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" - -if [ ! -f "$PROJECT_ROOT/pom.xml" ]; then - echo "Error: IDEasy project root not found at $PROJECT_ROOT" - echo "Please ensure this script is located in: IDEasy/gui/scripts/macos/" - read -p "Press Enter to close..." - exit 1 -fi - -# 'ide' is a shell function defined in $IDE_ROOT/_ide/installation/functions and -# sourced by ~/.bashrc / ~/.zshrc — those only get sourced in interactive shells. -# Double-clicking a .command file runs it via its #!/bin/bash shebang directly, -# which is a non-interactive shell, so 'ide' is invisible there even though it -# works fine from a regular Terminal window. Route through an interactive bash -# so the function gets sourced regardless of how this script was launched. -if ! bash -ic "command -v ide" &> /dev/null; then - echo "" - echo "Error: IDEasy is not installed" - echo "" - echo "Please install IDEasy first:" - echo "https://github.com/devonfw/IDEasy#setup" - echo "" - read -p "Press Enter to close..." - exit 1 -fi - -cd "$PROJECT_ROOT" - -# Launch IDE GUI in background and exit immediately -LOG_FILE="$HOME/.ideasy-gui.log" -bash -ic "ide gui" >> "$LOG_FILE" 2>&1 & -disown - -exit 0 diff --git a/gui/scripts/windows/create-shortcuts.ps1 b/gui/scripts/windows/create-shortcuts.ps1 deleted file mode 100644 index 24e3aeaba7..0000000000 --- a/gui/scripts/windows/create-shortcuts.ps1 +++ /dev/null @@ -1,153 +0,0 @@ -#Requires -Version 5.0 - -# IDEasy GUI Launcher - Create Windows Start Menu Shortcut and Desktop Link -# This script creates shortcuts to launch the IDEasy GUI from Windows Start Menu and Desktop - -param ( - [switch]$SkipDesktop, - [switch]$SkipStartMenu -) - -$ErrorActionPreference = 'Stop' - -$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path -$guiDir = Split-Path -Parent (Split-Path -Parent $scriptDir) -$projectRoot = Split-Path -Parent $guiDir - -$launcherBat = Join-Path $scriptDir "launch-gui.bat" -$launcherVbs = Join-Path $scriptDir "launch-gui-silent.vbs" -$pomFile = Join-Path $projectRoot "pom.xml" - -if (-not (Test-Path $launcherBat)) { - Write-Host "Error: launch-gui.bat not found" -ForegroundColor Red - exit 1 -} - -if (-not (Test-Path $launcherVbs)) { - Write-Host "Error: launch-gui-silent.vbs not found" -ForegroundColor Red - exit 1 -} - -if (-not (Test-Path $pomFile)) { - Write-Host "Error: pom.xml not found" -ForegroundColor Red - exit 1 -} - -Write-Host "IDEasy GUI Launcher Setup" -ForegroundColor Cyan -Write-Host "Project: $projectRoot" -ForegroundColor Cyan -Write-Host "" - -# Wrap the PNG bytes directly into an ICO container (Windows Vista+ supports PNG frames in ICO). -# This preserves full color depth and alpha transparency without any image processing. -# The ICO is written next to the scripts so the shortcut has a stable reference path. -# Returns $true on success, $false on failure. -function Convert-PngToIco { - param([string]$PngPath, [string]$IcoPath) - try { - $pngBytes = [System.IO.File]::ReadAllBytes($PngPath) - - # PNG dimensions are stored big-endian in the IHDR chunk at bytes 16-23 - $width = ($pngBytes[16] -shl 24) -bor ($pngBytes[17] -shl 16) -bor ($pngBytes[18] -shl 8) -bor $pngBytes[19] - $height = ($pngBytes[20] -shl 24) -bor ($pngBytes[21] -shl 16) -bor ($pngBytes[22] -shl 8) -bor $pngBytes[23] - - # ICO directory encodes 256 as 0 - $icoW = if ($width -ge 256) { [byte]0 } else { [byte]$width } - $icoH = if ($height -ge 256) { [byte]0 } else { [byte]$height } - - $sizeBytes = [System.BitConverter]::GetBytes([int32]$pngBytes.Length) - $offsetBytes = [System.BitConverter]::GetBytes([int32](6 + 16)) # header(6) + one dir entry(16) - - # ICO header (6 bytes) + single directory entry (16 bytes) - $icoHeader = [byte[]]( - 0x00, 0x00, # reserved - 0x01, 0x00, # type: icon - 0x01, 0x00, # image count: 1 - $icoW, $icoH, 0x00, 0x00, # w, h, colorCount, reserved - 0x01, 0x00, # color planes - 0x20, 0x00, # bits per pixel (32) - $sizeBytes[0], $sizeBytes[1], $sizeBytes[2], $sizeBytes[3], # image data size - $offsetBytes[0], $offsetBytes[1], $offsetBytes[2], $offsetBytes[3] # image data offset - ) - - $stream = [System.IO.FileStream]::new($IcoPath, [System.IO.FileMode]::Create) - $stream.Write($icoHeader, 0, $icoHeader.Length) - $stream.Write($pngBytes, 0, $pngBytes.Length) - $stream.Close() - return $true - } - catch { - return $false - } -} - -# Resolve icon location: prefer devonfw.png converted to ICO, fall back to shell32 -$pngSource = Join-Path $guiDir "src\main\resources\com\devonfw\ide\gui\assets\devonfw.png" -$generatedIco = Join-Path $scriptDir "ideasy-gui.ico" -$iconLocation = "$env:SystemRoot\system32\shell32.dll,1" - -if (Test-Path $pngSource) { - if (-not (Test-Path $generatedIco)) { - if (Convert-PngToIco -PngPath $pngSource -IcoPath $generatedIco) { - Write-Host "Icon generated from devonfw.png" -ForegroundColor Cyan - } else { - Write-Host "Note: Icon conversion failed, using default icon" -ForegroundColor Yellow - } - } - if (Test-Path $generatedIco) { - $iconLocation = "$generatedIco,0" - } -} elseif (Test-Path (Join-Path $scriptDir "icon.ico")) { - $iconLocation = "$(Join-Path $scriptDir 'icon.ico'),0" -} - -function Create-Shortcut { - param( - [string]$Path, - [string]$Target, - [string]$Arguments, - [string]$Description - ) - - $wshShell = $null - try { - Write-Host "Creating shortcut: $Path" - - $wshShell = New-Object -ComObject WScript.Shell - $shortcut = $wshShell.CreateShortcut($Path) - $shortcut.TargetPath = $Target - $shortcut.Arguments = $Arguments - $shortcut.WorkingDirectory = $projectRoot - $shortcut.Description = $Description - $shortcut.IconLocation = $iconLocation - - $shortcut.Save() - Write-Host "Created: $Path" -ForegroundColor Green - } - catch { - Write-Host "Error creating shortcut: $($_.Exception.Message)" -ForegroundColor Red - } - finally { - if ($wshShell) { - [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshShell) | Out-Null - } - } -} - -# Target wscript.exe running the silent VBS wrapper instead of launch-gui.bat directly, -# so no console window ever appears - independent of the user's terminal "close on exit" setting. -$wscriptPath = Join-Path "$env:SystemRoot\System32" "wscript.exe" -$launcherArgs = "`"$launcherVbs`"" - -if (-not $SkipDesktop) { - # Use Shell32 to resolve the Desktop folder — works correctly with OneDrive-redirected Desktops - $desktopShortcut = Join-Path ([Environment]::GetFolderPath('Desktop')) "IDEasy GUI.lnk" - Create-Shortcut -Path $desktopShortcut -Target $wscriptPath -Arguments $launcherArgs -Description "Launch IDEasy GUI" -} - -if (-not $SkipStartMenu) { - $startMenuShortcut = Join-Path "$env:APPDATA\Microsoft\Windows\Start Menu\Programs" "IDEasy GUI.lnk" - Create-Shortcut -Path $startMenuShortcut -Target $wscriptPath -Arguments $launcherArgs -Description "Launch IDEasy GUI" -} - -Write-Host "" -Write-Host "Setup complete!" -ForegroundColor Green diff --git a/gui/scripts/windows/launch-gui-silent.vbs b/gui/scripts/windows/launch-gui-silent.vbs deleted file mode 100644 index f5432adebb..0000000000 --- a/gui/scripts/windows/launch-gui-silent.vbs +++ /dev/null @@ -1,11 +0,0 @@ -' Runs launch-gui.bat completely hidden (no console window), regardless of the -' user's terminal "close on exit" settings. The shortcut targets this file via -' wscript.exe instead of launch-gui.bat directly so no window ever appears. -Dim shell, fso, scriptDir, batPath - -Set shell = CreateObject("WScript.Shell") -Set fso = CreateObject("Scripting.FileSystemObject") -scriptDir = fso.GetParentFolderName(WScript.ScriptFullName) -batPath = fso.BuildPath(scriptDir, "launch-gui.bat") - -shell.Run """" & batPath & """", 0, False diff --git a/gui/scripts/windows/launch-gui.bat b/gui/scripts/windows/launch-gui.bat deleted file mode 100644 index 0f96d1cbc3..0000000000 --- a/gui/scripts/windows/launch-gui.bat +++ /dev/null @@ -1,50 +0,0 @@ -@echo off -setlocal - -REM IDEasy GUI Launcher for Windows - -set "SCRIPT_DIR=%~dp0" - -REM Desktop/Start-Menu shortcuts are spawned by explorer.exe, which caches its -REM environment from login and does not pick up registry PATH/IDE_ROOT changes -REM made by the IDEasy installer until the user logs off or restarts explorer. -REM Re-read IDE_ROOT directly from the registry and extend PATH with it here, -REM so the shortcut works even with explorer's stale inherited environment. -if not defined IDE_ROOT ( - for /f "tokens=2,*" %%A in ('reg query "HKCU\Environment" /v IDE_ROOT 2^>nul') do set "IDE_ROOT=%%B" -) -if defined IDE_ROOT ( - set "PATH=%IDE_ROOT%\_ide\installation\bin;%PATH%" -) - -REM Check if ide command exists -where ide >nul 2>&1 || ( - echo. - echo Error: IDEasy is not installed or not in PATH - echo If you just installed IDEasy, log off/on once or restart Explorer so - echo the desktop shortcut picks up the updated environment. - echo https://github.com/devonfw/IDEasy#setup - echo. - pause - exit /b 1 -) - -REM Determine project root safely -pushd "%SCRIPT_DIR%..\..\.." -set "PROJECT_ROOT=%CD%" -popd - -REM Check project structure -if not exist "%PROJECT_ROOT%\pom.xml" ( - echo Error: IDEasy project root not found at %PROJECT_ROOT% - echo. - pause - exit /b 1 -) - -REM Launch GUI -cd /d "%PROJECT_ROOT%" -echo Starting IDEasy GUI... -START "" /B cmd /c ide gui >> "%USERPROFILE%\.ideasy-gui.log" 2>&1 - -exit /b 0 From 1644850ea382140259923ff69758c9e1aeaf62f2 Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Tue, 7 Jul 2026 16:22:52 +0200 Subject: [PATCH 13/16] #1917: add tests and fix shortcut creation robustness --- .../tools/ide/tool/IdeasyCommandletTest.java | 25 +++++++++++++++++++ .../Windows/Start Menu/Programs/.gitkeep | 0 .../ide-cli/gui/linux/ideasy-gui.desktop | 10 ++++++++ .../home/Downloads/ide-cli/gui/logo.ico | 1 + .../home/Downloads/ide-cli/gui/logo.png | 1 + 5 files changed, 37 insertions(+) create mode 100644 cli/src/test/resources/ide-projects/install/project/home/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/.gitkeep create mode 100644 cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/linux/ideasy-gui.desktop create mode 100644 cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/logo.ico create mode 100644 cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/logo.png diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/IdeasyCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/IdeasyCommandletTest.java index 945e7fc192..76b354492c 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/IdeasyCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/IdeasyCommandletTest.java @@ -2,6 +2,7 @@ import java.nio.file.Path; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -112,6 +113,30 @@ void testInstallIdeasy(String os) { + "devon\n" + "source ~/.devon/autocomplete\n" + addedRcLines); + verifyDesktopShortcut(context, systemInfo, installationPath); + } + + private void verifyDesktopShortcut(IdeTestContext context, SystemInfo systemInfo, Path installationPath) { + + if (systemInfo.isLinux()) { + Path desktopFile = context.getUserHome().resolve(".local/share/applications/ideasy-gui.desktop"); + assertThat(desktopFile).exists(); + assertThat(desktopFile).content() + .contains("Exec=" + installationPath.resolve("bin/ideasy") + " gui") + .contains("Icon=" + installationPath.resolve("gui/logo.png")); + } else if (systemInfo.isMac()) { + Path commandFile = context.getUserHome().resolve("Applications/IDEasy GUI.command"); + assertThat(commandFile).exists(); + assertThat(commandFile).content() + .contains(installationPath.resolve("bin/ideasy").toString()) + .contains("gui"); + } else if (systemInfo.isWindows()) { + Assumptions.assumeTrue(System.getProperty("os.name", "").toLowerCase().startsWith("win"), + "Skipped: .lnk creation requires PowerShell, which is only available on Windows"); + assertThat(context.getUserHome().resolve("Desktop/IDEasy GUI.lnk")).exists(); + assertThat(context.getUserHome().resolve( + "AppData/Roaming/Microsoft/Windows/Start Menu/Programs/IDEasy GUI.lnk")).exists(); + } } /** diff --git a/cli/src/test/resources/ide-projects/install/project/home/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/.gitkeep b/cli/src/test/resources/ide-projects/install/project/home/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/linux/ideasy-gui.desktop b/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/linux/ideasy-gui.desktop new file mode 100644 index 0000000000..b3df324408 --- /dev/null +++ b/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/linux/ideasy-gui.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=test-SNAPSHOT +Type=Application +Name=IDEasy GUI +Comment=Launch IDEasy GUI +Exec=IDEASY_BIN gui +Icon=IDEASY_ICON +Terminal=false +Categories=Development; +StartupNotify=true diff --git a/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/logo.ico b/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/logo.ico new file mode 100644 index 0000000000..b746109bb2 --- /dev/null +++ b/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/logo.ico @@ -0,0 +1 @@ +logo.ico mock \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/logo.png b/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/logo.png new file mode 100644 index 0000000000..cf45109b9b --- /dev/null +++ b/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/logo.png @@ -0,0 +1 @@ +logo.png mock \ No newline at end of file From e809173ddba813ee739d4fbece86cae5aa4340d4 Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Tue, 7 Jul 2026 16:29:37 +0200 Subject: [PATCH 14/16] #1917: fix duplicate import and improve comment --- .../main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java index b9059c3f53..61cd48ca65 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java @@ -1,6 +1,5 @@ package com.devonfw.tools.ide.tool; -import java.io.IOException; import java.io.IOException; import java.io.Reader; import java.io.Writer; @@ -371,7 +370,7 @@ private void installLinuxDesktopShortcut(Path installationPath) throws IOExcepti LOG.debug("Could not set POSIX permissions on {}", desktopFile); } - // Update desktop database so the entry appears in application menus immediately + // without this, the new entry is only visible in application menus after the next login try { this.context.newProcess().executable("update-desktop-database").addArg(applicationsDir.toString()) .run(ProcessMode.DEFAULT_CAPTURE); From a216be2f701de900f42086e48e18c22a48a55f8c Mon Sep 17 00:00:00 2001 From: Hieu Do Date: Wed, 8 Jul 2026 14:52:19 +0200 Subject: [PATCH 15/16] fix: minor code quality issues --- .../tools/ide/tool/IdeasyCommandlet.java | 37 ++++++++----------- .../main/package/gui/linux/ideasy-gui.desktop | 4 +- .../ide-cli/gui/linux/ideasy-gui.desktop | 4 +- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java index 61cd48ca65..ebd6d2f9a8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java @@ -343,7 +343,7 @@ private void installDesktopShortcut(Path installationPath) { installWindowsDesktopShortcut(installationPath); } } catch (Exception e) { - LOG.warn("Failed to create desktop shortcut: {}", e.getMessage()); + LOG.warn("Failed to create desktop shortcut.", e); } } @@ -357,18 +357,14 @@ private void installLinuxDesktopShortcut(Path installationPath) throws IOExcepti Path ideasyBin = installationPath.resolve("bin/ideasy"); Path logoPath = installationPath.resolve("gui/logo.png"); String content = Files.readString(templateFile) - .replace("IDEASY_BIN", ideasyBin.toString()) - .replace("IDEASY_ICON", logoPath.toString()); + .replace("@IDEASY_BIN@", ideasyBin.toString()) + .replace("@IDEASY_ICON@", logoPath.toString()); Path applicationsDir = this.context.getUserHome().resolve(".local/share/applications"); this.context.getFileAccess().mkdirs(applicationsDir); Path desktopFile = applicationsDir.resolve("ideasy-gui.desktop"); - Files.writeString(desktopFile, content); - try { - Files.setPosixFilePermissions(desktopFile, FileAccess.RWX_RX_RX); - } catch (UnsupportedOperationException e) { - LOG.debug("Could not set POSIX permissions on {}", desktopFile); - } + this.context.getFileAccess().writeFileContent(content, desktopFile); + this.context.getFileAccess().makeExecutable(desktopFile); // without this, the new entry is only visible in application menus after the next login try { @@ -390,19 +386,16 @@ private void installLinuxDesktopShortcut(Path installationPath) throws IOExcepti IdeLogLevel.SUCCESS.log(LOG, "Created desktop shortcut at {}", desktopFile); } - private void installMacDesktopShortcut(Path installationPath) throws IOException { + private void installMacDesktopShortcut(Path installationPath) { Path ideasyBin = installationPath.resolve("bin/ideasy"); - String content = "#!/bin/bash\n\"" + ideasyBin + "\" gui\n"; + String escapedBin = ideasyBin.toString().replace("\"", "\\\""); + String content = "#!/bin/bash\n\"" + escapedBin + "\" gui\n"; Path applicationsDir = this.context.getUserHome().resolve("Applications"); this.context.getFileAccess().mkdirs(applicationsDir); Path commandFile = applicationsDir.resolve("IDEasy GUI.command"); - Files.writeString(commandFile, content); - try { - Files.setPosixFilePermissions(commandFile, FileAccess.RWX_RX_RX); - } catch (UnsupportedOperationException e) { - LOG.debug("Could not set POSIX permissions on {}", commandFile); - } + this.context.getFileAccess().writeFileContent(content, commandFile); + this.context.getFileAccess().makeExecutable(commandFile); IdeLogLevel.SUCCESS.log(LOG, "Created macOS launcher at {}", commandFile); } @@ -414,11 +407,13 @@ private void installWindowsDesktopShortcut(Path installationPath) { WindowsHelper helper = WindowsHelper.get(this.context); String desktopStr = helper.getRegistryValue( "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", "Desktop"); - Path desktopPath = (desktopStr != null) ? Path.of(desktopStr) : this.context.getUserHome().resolve("Desktop"); + Path desktopPath = (desktopStr != null && !desktopStr.isBlank()) ? Path.of(desktopStr) : this.context.getUserHome().resolve("Desktop"); this.context.getFileAccess().mkdirs(desktopPath); createWindowsShortcut(desktopPath.resolve("IDEasy GUI.lnk"), ideasyExe, icoPath); - Path startMenu = this.context.getUserHome() - .resolve("AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs"); + String startMenuStr = helper.getRegistryValue( + "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", "Programs"); + Path startMenu = (startMenuStr != null && !startMenuStr.isBlank()) ? Path.of(startMenuStr) + : this.context.getUserHome().resolve("AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs"); if (Files.isDirectory(startMenu)) { createWindowsShortcut(startMenu.resolve("IDEasy GUI.lnk"), ideasyExe, icoPath); } @@ -441,7 +436,7 @@ private void createWindowsShortcut(Path lnkPath, Path targetExe, Path icoPath) { .run(ProcessMode.DEFAULT_CAPTURE); IdeLogLevel.SUCCESS.log(LOG, "Created shortcut at {}", lnkPath); } catch (Exception e) { - LOG.warn("Failed to create shortcut at {}: {}", lnkPath, e.getMessage()); + LOG.warn("Failed to create shortcut at {}.", lnkPath, e); } } diff --git a/cli/src/main/package/gui/linux/ideasy-gui.desktop b/cli/src/main/package/gui/linux/ideasy-gui.desktop index 166e3ed293..4f2a720d51 100644 --- a/cli/src/main/package/gui/linux/ideasy-gui.desktop +++ b/cli/src/main/package/gui/linux/ideasy-gui.desktop @@ -3,8 +3,8 @@ Version=$[project.version] Type=Application Name=IDEasy GUI Comment=Launch IDEasy GUI -Exec=IDEASY_BIN gui -Icon=IDEASY_ICON +Exec=@IDEASY_BIN@ gui +Icon=@IDEASY_ICON@ Terminal=false Categories=Development; StartupNotify=true diff --git a/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/linux/ideasy-gui.desktop b/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/linux/ideasy-gui.desktop index b3df324408..997eed11b1 100644 --- a/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/linux/ideasy-gui.desktop +++ b/cli/src/test/resources/ide-projects/install/project/home/Downloads/ide-cli/gui/linux/ideasy-gui.desktop @@ -3,8 +3,8 @@ Version=test-SNAPSHOT Type=Application Name=IDEasy GUI Comment=Launch IDEasy GUI -Exec=IDEASY_BIN gui -Icon=IDEASY_ICON +Exec=@IDEASY_BIN@ gui +Icon=@IDEASY_ICON@ Terminal=false Categories=Development; StartupNotify=true From a837418e24e036d2a1fa84c9f9d58da1c4125556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Fri, 10 Jul 2026 18:24:24 +0200 Subject: [PATCH 16/16] Update CHANGELOG for release 2026.07.002 --- CHANGELOG.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 06e8029001..5ce78926d8 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -4,10 +4,9 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE == 2026.07.002 -* https://github.com/devonfw/IDEasy/issues/1917[#1917]: Allow the creation of a desktop shortcut for the GUI - Release with new features and bugfixes: +* https://github.com/devonfw/IDEasy/issues/1917[#1917]: Allow the creation of a desktop shortcut for the GUI The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/47?closed=1[milestone 2026.07.002].