You've already forked lazarus-ccr
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6801 8e941d3f-bd1b-0410-a28a-d453659cc2b4
458 lines
16 KiB
ObjectPascal
458 lines
16 KiB
ObjectPascal
unit ushortcut;
|
|
|
|
{
|
|
License
|
|
=======
|
|
LazAutoUpdate (c)2015 Gordon Bamber (minesadorada@charcodelvalle.com)
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version with the following modification:
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent modules,and
|
|
to copy and distribute the resulting executable under terms of your choice,
|
|
provided that you also meet, for each linked independent module, the terms
|
|
and conditions of the license of that module. An independent module is a
|
|
module which is not derived from or based on this library. If you modify
|
|
this library, you may extend this exception to your version of the library,
|
|
but you are not obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
Credits
|
|
=======
|
|
Code adapted from fpcup (@BigChimp and @DonAlfredo at freepascal forum)
|
|
|
|
Use
|
|
===
|
|
Use public function 'GetShortCutErrorString' to show errors/info when debugging
|
|
|
|
Linux Shortcut Info
|
|
===================
|
|
|
|
1. FreeDesktop Valid Categories
|
|
===============================
|
|
AudioVideo Application for presenting, creating, or processing multimedia (audio/video)
|
|
Audio An audio application Desktop entry must include AudioVideo as well
|
|
Audio A video application Desktop entry must include AudioVideo as well
|
|
Development An application for development
|
|
Education Educational software
|
|
Game A game
|
|
Graphics Application for viewing, creating, or processing graphics
|
|
Network Network application such as a web browser
|
|
Office An office type application
|
|
Science Scientific software
|
|
Settings Settings applications Entries may appear in a separate menu or as part of a "Control Center"
|
|
System System application, "System Tools" such as say a log viewer or network monitor
|
|
Utility Small utility application, "Accessories"
|
|
|
|
2. Example Desktop File
|
|
=======================
|
|
[Desktop Entry]
|
|
Version=1.0
|
|
Type=Application
|
|
Name=Foo Viewer
|
|
Comment=The best viewer for Foo objects available!
|
|
TryExec=fooview
|
|
Exec=fooview %F
|
|
Icon=fooview
|
|
MimeType=image/x-foo;
|
|
Actions=Gallery;Create;
|
|
|
|
[Desktop Action Gallery]
|
|
Exec=fooview --gallery
|
|
Name=Browse Gallery
|
|
|
|
[Desktop Action Create]
|
|
Exec=fooview --create-new
|
|
Name=Create a new Foo!
|
|
Icon=fooview-new
|
|
}
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, LazUTF8, FileUtil, LazFileUtils
|
|
{$IFDEF LINUX}, process, strutils, LazUTF8Classes{$ENDIF}
|
|
{$IFDEF WINDOWS}, Windows, shlobj {for special folders}, ActiveX,
|
|
ComObj, ShellAPI{$ENDIF} ;
|
|
|
|
function CreateDesktopShortCut(Target, TargetArguments, ShortcutName,
|
|
IconFileName, Category: string): boolean;
|
|
function DeleteDesktopShortcut(ShortcutName: string): boolean;
|
|
function GetShortCutDebugString: string;
|
|
|
|
implementation
|
|
|
|
var
|
|
sDebugString: string;
|
|
// *****************************************************************************
|
|
// Functions and procs to aid Debugging
|
|
// *****************************************************************************
|
|
function GetShortCutDebugString: string;
|
|
begin
|
|
if (sDebugString = '') then
|
|
Result := 'OK'
|
|
else
|
|
Result := sDebugString;
|
|
end;
|
|
// Builds up a string with linebreaks
|
|
procedure AddToDebugString(Astring: string);
|
|
begin
|
|
if (sDebugString = '') then
|
|
sDebugString := LineEnding + '* ' + Astring
|
|
else
|
|
sDebugString := sDebugString + LineEnding + '* ' + Astring;
|
|
end;
|
|
// *****************************************************************************
|
|
|
|
{$IFDEF UNIX}
|
|
//Adapted from sysutils; Unix/Linux only
|
|
function XdgConfigHome: string;
|
|
{ Follows base-dir spec,
|
|
see [http://freedesktop.org/Standards/basedir-spec].
|
|
Always ends with PathDelim. }
|
|
begin
|
|
Result := GetEnvironmentVariable('XDG_CONFIG_HOME');
|
|
if (Result = '') then
|
|
Result := IncludeTrailingPathDelimiter(ExpandFileNameUTF8('~')) +
|
|
'.config' + DirectorySeparator
|
|
else
|
|
Result := IncludeTrailingPathDelimiter(Result);
|
|
end;
|
|
|
|
{$ENDIF UNIX}
|
|
{$IFDEF MSWINDOWS}
|
|
function CreateDesktopShortCut(Target, TargetArguments, ShortcutName,
|
|
IconFileName, Category: string): boolean;
|
|
{
|
|
IN:
|
|
Target: Filename with full path
|
|
TargetArguments: String of arguments
|
|
ShortCutName: Simple string
|
|
IconFileName: Filename with full path
|
|
Category: Simple string (see header of this unit)
|
|
OUT:
|
|
True = Success
|
|
False = Failure
|
|
Use function GetShortCutDebugString to get most recent error as a string
|
|
}
|
|
var
|
|
IObject: IUnknown;
|
|
ISLink: IShellLink;
|
|
IPFile: IPersistFile;
|
|
PIDL: PItemIDList;
|
|
InFolder: array[0..MAX_PATH] of char;
|
|
LinkName: WideString;
|
|
begin
|
|
Result := True;
|
|
sDebugString := '';
|
|
// Simple failure check
|
|
if not FileExistsUTF8(Target) then
|
|
begin
|
|
AddToDebugString('Filename ' + Target + ' does not exist');
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
|
|
try
|
|
{ Creates an instance of IShellLink }
|
|
IObject := CreateComObject(CLSID_ShellLink);
|
|
ISLink := IObject as IShellLink;
|
|
IPFile := IObject as IPersistFile;
|
|
try
|
|
ISLink.SetPath(PChar(Target));
|
|
ISLink.SetArguments(PChar(TargetArguments));
|
|
ISLink.SetWorkingDirectory(PChar(ExtractFilePath(Target)));
|
|
{ Get the desktop location }
|
|
SHGetSpecialFolderLocation(0, CSIDL_DESKTOPDIRECTORY, PIDL);
|
|
SHGetPathFromIDList(PIDL, InFolder);
|
|
|
|
LinkName := IncludeTrailingPathDelimiter(InFolder) + ShortcutName + '.lnk';
|
|
{ Get rid of any existing shortcut first }
|
|
if not SysUtils.DeleteFile(LinkName) then
|
|
AddToDebugString('Could not delete existing link ' + LinkName);
|
|
{Create the link }
|
|
IPFile.Save(PWChar(LinkName), False);
|
|
{Notify the shell}
|
|
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, PChar(LinkName), nil);
|
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH or SHCNF_FLUSH,
|
|
PChar(ExtractFileDir(LinkName)), nil);
|
|
|
|
{Menu Entry}
|
|
SHGetSpecialFolderLocation(0, CSIDL_PROGRAMS, PIDL);
|
|
SHGetPathFromIDList(PIDL, InFolder);
|
|
If Not DirectoryExistsUTF8(IncludeTrailingPathDelimiter(InFolder) + ShortcutName) then
|
|
ForceDirectoriesUTF8(IncludeTrailingPathDelimiter(InFolder) + ShortcutName);
|
|
LinkName := IncludeTrailingPathDelimiter(InFolder) + ShortcutName + DirectorySeparator + ShortcutName + '.lnk';
|
|
{ Get rid of any existing shortcut first }
|
|
if not SysUtils.DeleteFile(LinkName) then
|
|
AddToDebugString('Could not delete existing link ' + LinkName);
|
|
{Create the menu entry link }
|
|
IPFile.Save(PWChar(LinkName), False);
|
|
{Notify the shell}
|
|
SHChangeNotify(SHCNE_MKDIR, SHCNF_PATH, PChar(LinkName), nil)
|
|
|
|
finally
|
|
// Not needed: FreeAndNil(IPFile);
|
|
end;
|
|
except
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
{$ENDIF MSWINDOWS}
|
|
{$IFDEF UNIX}
|
|
function CreateDesktopShortCut(Target, TargetArguments, ShortcutName,
|
|
IconFileName, Category: string): boolean;
|
|
{
|
|
* Comprehensive debugging messages in this routine!
|
|
* So many flavours of Linux.. - if no desktop icon is created then
|
|
* call GetShortCutDebugString and log the result to a file.
|
|
IN:
|
|
Target: Filename with full path
|
|
TargetArguments: String of arguments
|
|
ShortCutName: Simple string
|
|
IconFileName: Filename with full path. If not specifies Executable will be used
|
|
Category: Simple string (see header of this unit)
|
|
OUT:
|
|
True = Success
|
|
False = Failure
|
|
Use function GetShortCutDebugString to get errors as a string
|
|
}
|
|
var
|
|
XdgDesktopStringList: TStringList;
|
|
XdgDesktopFile: string;
|
|
Aprocess: TProcess;
|
|
sPathToShare: string;
|
|
sDesktopFilename: string;
|
|
begin
|
|
// Succeed by default:
|
|
Result := True;
|
|
sDebugString := '';
|
|
// Simple failure checks
|
|
if not FileExistsUTF8(Target) then // lethal
|
|
begin
|
|
AddToDebugString('File "' + Target + '" cannot be located. Quitting.');
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
if not FileExistsUTF8(IconFileName) then // non-lethal
|
|
begin
|
|
AddToDebugString('File "' + IconFileName + '" cannot be located. Using Target.');
|
|
IconFileName := Target;
|
|
end;
|
|
if ShortCutName = '' then // lethal
|
|
begin
|
|
AddToDebugString('ShortcutName is blank. Quitting.');
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
if Category = '' then // non-lethal
|
|
begin
|
|
AddToDebugString('Category is blank. Using "Utility"');
|
|
Category := 'Utility';
|
|
end;
|
|
// Make up an 8-character filename
|
|
sDesktopFilename := DelSpace(shortcutname);
|
|
sDesktopFilename := LeftStr(sDesktopFilename, 8);
|
|
sDesktopFilename := LowerCase(sDesktopFilename);
|
|
AddToDebugString('Desktop filename = ' + sDesktopFilename);
|
|
// Note: IncludeTrailingPathDelimiter(ExpandFileNameUTF8('~')) resolves to '/root/'
|
|
|
|
// Standard path to DeskTop files
|
|
sPathToShare := '/usr/share/applications' + DirectorySeparator +
|
|
sDesktopFilename + '.desktop';
|
|
// Directory check
|
|
if not DirectoryExistsUTF8('/usr/share/applications') then // non-lethal
|
|
AddToDebugString('Cannot find directory - ' + '/usr/share/applications');
|
|
|
|
// Temp directory path
|
|
XdgDesktopFile := IncludeTrailingPathDelimiter(GetTempDir(False)) +
|
|
sDesktopFilename + '.desktop';
|
|
// Directory check
|
|
if not DirectoryExistsUTF8(GetTempDir(False)) then // lethal
|
|
begin
|
|
AddToDebugString('Failure: Invalid directory - ' + GetTempDir(False));
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
AddToDebugString('Success: XdgDesktopFile = ' + XdgDesktopFile);
|
|
AddToDebugString('Success: sPathToShare = ' + sPathToShare);
|
|
|
|
// Make up the desktop file
|
|
XdgDesktopStringList := TStringList.Create;
|
|
try
|
|
XdgDesktopStringList.Add('[Desktop Entry]');
|
|
XdgDesktopStringList.Add('Encoding=UTF-8');
|
|
XdgDesktopStringList.Add('Type=Application');
|
|
//XdgDesktopStringList.Add('NoDisplay=True');
|
|
XdgDesktopStringList.Add('Icon=' + IconFileName);
|
|
if TargetArguments <> '' then
|
|
XdgDesktopStringList.Add('Exec=' + Target + ' ' + TargetArguments)
|
|
else
|
|
XdgDesktopStringList.Add('Exec=' + Target);
|
|
XdgDesktopStringList.Add('Name=' + ShortcutName);
|
|
XdgDesktopStringList.Add('Category=' + Category);
|
|
// We're going to try and call xdg-desktop-icon
|
|
// this may fail if shortcut exists already
|
|
AProcess := TProcess.Create(nil);
|
|
try
|
|
try
|
|
if FileExistsUTF8(XdgDesktopFile) then
|
|
DeleteFile(XdgDesktopFile);
|
|
Sleep(100);
|
|
try
|
|
XdgDesktopStringList.SaveToFile(XdgDesktopFile);
|
|
except
|
|
if not FileExistsUTF8(XdgDesktopFile) then
|
|
AddToDebugString('Failure: XdgDesktopFile wasn''t saved');
|
|
end;
|
|
if FileExistsUTF8(XdgDesktopFile) then
|
|
begin
|
|
Aprocess.Executable := 'xdg-desktop-icon install';
|
|
AProcess.CurrentDirectory := ProgramDirectory;
|
|
AProcess.Parameters.Clear;
|
|
AProcess.Parameters.Add(XdgDesktopFile);
|
|
Aprocess.Execute;
|
|
Sleep(100);
|
|
AddToDebugString('xdg-desktop-icon install succeeded');
|
|
end;
|
|
except
|
|
// xdg-desktop-icon install failed.
|
|
Result := False;
|
|
AddToDebugString('Failure: Exception running "xdg-desktop-icon install"');
|
|
|
|
// OK. Try usr/share/applications
|
|
if FileExistsUTF8(sPathToShare) then
|
|
begin
|
|
if SysUtils.DeleteFile(sPathToShare) then
|
|
AddToDebugString('Successfully deleted existing ' + sPathToShare)
|
|
else
|
|
AddToDebugString('Failure: Unable to delete existing ' + sPathToShare);
|
|
end;
|
|
// Final Directory check
|
|
if not DirectoryExistsUTF8('/usr/share/applications') then // lethal
|
|
begin
|
|
AddToDebugString(
|
|
'Failure: Directory "/usr/share/applications" does not exist on this system');
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
// Save the stringlist directly to usr/share/applications
|
|
try
|
|
XdgDesktopStringList.SaveToFile(sPathToShare);
|
|
except
|
|
if not FileExistsUTF8(sPathToShare) then
|
|
begin
|
|
Result := False;
|
|
AddToDebugString('Failure: SaveToFile(' + sPathToShare + ') failed');
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
AProcess.Free;
|
|
end;
|
|
if Result = False then
|
|
try
|
|
if not (FileExistsUTF8(XdgDesktopFile)) then
|
|
AddToDebugString('Unable to locate temporary ' + XdgDesktopFile);
|
|
if (FileExistsUTF8(XdgDesktopFile)) and (not FileExistsUTF8(sPathToShare)) then
|
|
begin
|
|
// Last try to copy file to usr/share/applications
|
|
if CopyFile(XdgDesktopFile, sPathToShare) then
|
|
begin
|
|
AddToDebugString(Format('Successfully copied %s file to %s',
|
|
[XdgDesktopFile, sPathToShare]));
|
|
Result := True;
|
|
end
|
|
else
|
|
AddToDebugString(Format('Unable to copy %s file to %s',
|
|
[XdgDesktopFile, sPathToShare]));
|
|
// Temp file is no longer needed....
|
|
if not SysUtils.DeleteFile(XdgDesktopFile) then
|
|
begin
|
|
AddToDebugString('Failure: Unable to delete temporary ' + XdgDesktopFile);
|
|
end;
|
|
end;
|
|
if (FileExistsUTF8(sPathToShare)) then
|
|
begin
|
|
Result := True;
|
|
AddToDebugString('Success: Desktop file - ' + sPathToShare);
|
|
end;
|
|
finally
|
|
// Swallow, let filesystem maintenance clear it up
|
|
end;
|
|
finally
|
|
XdgDesktopStringList.Free;
|
|
end;
|
|
end;
|
|
|
|
{$ENDIF UNIX}
|
|
{$IFDEF MSWINDOWS}
|
|
function DeleteDesktopShortcut(ShortcutName: string): boolean;
|
|
var
|
|
PIDL: PItemIDList;
|
|
InFolder: array[0..MAX_PATH] of char;
|
|
LinkName: WideString;
|
|
begin
|
|
Result := False; // Assume failure; look for success
|
|
sDebugString := '';
|
|
try
|
|
{ Get the desktop location }
|
|
SHGetSpecialFolderLocation(0, CSIDL_DESKTOPDIRECTORY, PIDL);
|
|
SHGetPathFromIDList(PIDL, InFolder);
|
|
LinkName := IncludeTrailingPathDelimiter(InFolder) + ShortcutName + '.lnk';
|
|
if SysUtils.DeleteFile(LinkName) then
|
|
begin
|
|
Result := True;
|
|
AddToDebugString('DeleteDesktopShortcut Success: Deleted ' + LinkName);
|
|
end
|
|
else
|
|
AddToDebugString('DeleteDesktopShortcut Failure: Unable to delete ' + LinkName);
|
|
{ Get the menu location}
|
|
SHGetSpecialFolderLocation(0, CSIDL_PROGRAMS, PIDL);
|
|
SHGetPathFromIDList(PIDL, InFolder);
|
|
LinkName := IncludeTrailingPathDelimiter(InFolder) + ShortcutName + DirectorySeparator + ShortcutName + '.lnk';
|
|
{ Get rid of any existing shortcut first }
|
|
if SysUtils.DeleteFile(LinkName) then
|
|
begin
|
|
AddToDebugString('DeleteDesktopShortcut Success: Deleted ' + LinkName);
|
|
Result := True;
|
|
end
|
|
else
|
|
AddToDebugString('DeleteDesktopShortcut Failure: Unable to delete ' + LinkName);
|
|
If DirectoryExistsUTF8(IncludeTrailingPathDelimiter(InFolder) + ShortcutName) then
|
|
If RemoveDirUTF8(IncludeTrailingPathDelimiter(InFolder) + ShortcutName) then
|
|
AddToDebugString('DeleteDesktopShortcut Success: Deleted menu entry')
|
|
else
|
|
AddToDebugString('DeleteDesktopShortcut Failure: Unable to delete menu entry');
|
|
except
|
|
AddToDebugString('Exception deleting ' + LinkName);
|
|
// Eat the exception
|
|
end;
|
|
end;
|
|
|
|
{$ELSE}
|
|
function DeleteDesktopShortcut(ShortcutName: string): boolean;
|
|
begin
|
|
sDebugString := 'DeleteDesktopShortcut not implemented in Linux';
|
|
Result := False;
|
|
end;
|
|
|
|
{$ENDIF MSWINDOWS}
|
|
|
|
end.
|