View Issue Details

IDProjectCategoryView StatusLast Update
0001835Double CommanderLogicpublic2019-12-22 19:57
ReporterCryHam Assigned To 
PrioritynormalSeverityfeatureReproducibilityN/A
Status newResolutionopen 
ProjectionnoneETAnone 
Product Version1.0.0 (trunk) 
Summary0001835: Make Ctrl - (Left,Right or Up) follow links to destination.
DescriptionI have many of links to executables, source, project files etc. Normally in Total Commander I can also go to the file in destination. E.g. with Ctrl-Left opening directory and focusing cursor on target on left side view, with Ctrl-Up it will do the same on new tab.

Currently in DC links to files do nothing, just open another view with current directory. Those keys work only for symbolic links to directories (which appear as normal [directories] in view).
They don't work for shortcuts (*.lnk files) and don't work for symbolic links to files (should open the containing directory and best if also put cursor on target file).
TagsNo tags attached.
Attached Files
lnk.patch (6,454 bytes)   
Index: src/platform/win/umywindows.pas
===================================================================
--- src/platform/win/umywindows.pas	(revision 8763)
+++ src/platform/win/umywindows.pas	(working copy)
@@ -137,6 +137,10 @@
 }
 procedure CreateShortcut(const Target, Shortcut: String);
 {en
+   Extracts target full file path from windows shortcut file (.lnk)
+}
+function GetShortcutTarget(const ShortcutFilename: string): string;
+{en
    Extract file attributes from find data record.
    Removes reparse point attribute if a reparse point tag is not a name surrogate.
    @param(FindData Find data record from FindFirstFile/FindNextFile function.)
@@ -860,6 +864,26 @@
   OleCheckUTF8(IPFile.Save(PWideChar(LinkName), False));
 end;
 
+function GetShortcutTarget(const ShortcutFilename: string): string;
+var
+  Psl: IShellLink;
+  Ppf: IPersistFile;
+  WideName: array[0..MAX_PATH] of WideChar;
+  pResult: array[0..MAX_PATH-1] of ansiChar;
+  Data: TWin32FindData;
+const
+  IID_IPersistFile: TGUID = (
+    D1:$0000010B; D2:$0000; D3:$0000; D4:($C0,$00,$00,$00,$00,$00,$00,$46));
+begin
+  CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IID_IShellLinkA, psl);
+  psl.QueryInterface(IID_IPersistFile, ppf);
+  MultiByteToWideChar(CP_ACP, 0, pAnsiChar(ShortcutFilename), -1, WideName, Max_Path);
+  ppf.Load(WideName, STGM_READ);
+  psl.Resolve(0, SLR_ANY_MATCH);
+  psl.GetPath(@pResult, MAX_PATH, Data, SLGP_UNCPRIORITY);
+  Result := StrPas(pResult);
+end;
+
 function ExtractFileAttributes(const FindData: TWin32FindDataW): DWORD; inline;
 begin
   // If a reparse point tag is not a name surrogate then remove reparse point attribute
Index: src/uglobs.pas
===================================================================
--- src/uglobs.pas	(revision 8763)
+++ src/uglobs.pas	(working copy)
@@ -1018,6 +1018,7 @@
       AddIfNotExists(['Alt+Right'],[],'cm_ViewHistoryNext');
       AddIfNotExists(['Alt+Shift+Enter'],[],'cm_CountDirContent');
       AddIfNotExists(['Alt+Shift+F9'],[],'cm_TestArchive');
+      AddIfNotExists(['Ctrl+Shift+S'],[],'cm_CreateShortcut');
       AddIfNotExists([
          'Alt+1','','index=1','',
          'Alt+2','','index=2','',
Index: src/umaincommands.pas
===================================================================
--- src/umaincommands.pas	(revision 8763)
+++ src/umaincommands.pas	(working copy)
@@ -27,8 +27,9 @@
 interface
 
 uses
-  Classes, SysUtils, ActnList, uFileView, uFileViewNotebook, uFileSourceOperation,
-  uGlobs, uFileFunctions, uFormCommands, uFileSorting, uShellContextMenu, Menus, ufavoritetabs,ufile;
+  Classes, SysUtils, ActnList, uFileView, uFileViewNotebook, uFileSourceOperation, uTypes,
+  uGlobs, uFileFunctions, uFormCommands, uFileSorting,
+  uShellContextMenu, Menus, ufavoritetabs, ufile, uMyWindows;
 
 type
 
@@ -295,6 +296,7 @@
    procedure cm_RightSortBySize(const Params: array of string);
    procedure cm_RightSortByAttr(const Params: array of string);
    procedure cm_SymLink(const Params: array of string);
+   procedure cm_CreateShortcut(const {%H-}Params: array of string);
    procedure cm_CopySamePanel(const Params: array of string);
    procedure cm_DirHistory(const Params: array of string);
    procedure cm_ViewHistory(const Params: array of string);
@@ -832,10 +834,24 @@
       begin
         // Change file source, if the file under cursor can be opened as another file source.
         try
+          if LowerCase(aFile.Extension) = 'lnk' then
+          begin
+            NewPath := GetShortcutTarget(aFile.FullPath);
+            if mbDirectoryExists(NewPath) then
+              ChooseFileSource(TargetPage.FileView, NewPath)
+            else  // file
+            begin
+              ChooseFileSource(TargetPage.FileView, ExtractFilePath(NewPath));
+              TargetPage.FileView.SetActiveFile(NewPath);
+            end;
+          end
+          else
+          begin
           if not ChooseFileSource(TargetPage.FileView, SourcePage.FileView.FileSource, aFile) then
             TargetPage.FileView.AddFileSource(SourcePage.FileView.FileSource,
                                               SourcePage.FileView.CurrentPath);
           TargetPage.FileView.SetActiveFile(aFile.Name);
+          end;
         except
           on e: EFileSourceException do
             MessageDlg('Error', e.Message, mtError, [mbOK], 0);
@@ -1158,6 +1174,7 @@
 var
   aFile: TFile;
   NewPage: TFileViewPage;
+  NewPath: string;
 begin
   aFile := FrmMain.ActiveFrame.CloneActiveFile;
   if not Assigned(aFile) then
@@ -1170,6 +1187,17 @@
     else if FileIsArchive(aFile.FullPath) then
       NewPage := OpenArchive(aFile)
     else
+    if LowerCase(aFile.Extension) = 'lnk' then
+    begin
+      NewPath := GetShortcutTarget(aFile.FullPath);
+      if mbDirectoryExists(NewPath) then
+        NewPage := OpenTab(NewPath)
+      else  // file
+      begin
+        NewPage := OpenTab(ExtractFilePath(NewPath));
+        NewPage.FileView.SetActiveFile(NewPath);
+      end;
+    end else
       NewPage := OpenTab(aFile.Path);
   finally
     FreeAndNil(aFile);
@@ -3290,6 +3318,49 @@
   end;
 end;
 
+procedure TMainCommands.cm_CreateShortcut(const Params: array of string);
+var
+  sExistingFile, sLinkToCreate: String;
+  SelectedFiles: TFiles;
+begin
+  with frmMain do
+  begin
+    // Shortcut links work only for file system.
+    if not (ActiveFrame.FileSource.IsClass(TFileSystemFileSource)) then
+    begin
+      msgWarning(rsMsgErrNotSupported);
+      Exit;
+    end;
+
+    SelectedFiles := ActiveFrame.CloneSelectedOrActiveFiles;
+    try
+      if SelectedFiles.Count > 1 then
+        msgWarning(rsMsgTooManyFilesSelected)
+      else if SelectedFiles.Count = 0 then
+        msgWarning(rsMsgNoFilesSelected)
+      else
+      begin
+        sExistingFile := SelectedFiles[0].Path + SelectedFiles[0].Name;
+
+        if Length(Params) > 0 then
+          sLinkToCreate := Params[0]
+        else
+          sLinkToCreate := ActiveFrame.CurrentPath;
+
+        sLinkToCreate := sLinkToCreate + SelectedFiles[0].Name;
+        try
+          CreateShortcut(sExistingFile, sLinkToCreate);
+        finally
+          ActiveFrame.Reload;
+        end;
+      end;
+
+    finally
+      FreeAndNil(SelectedFiles);
+    end;
+  end;
+end;
+
 // Uses to change sort direction when columns header is disabled
 procedure TMainCommands.cm_ReverseOrder(const Params: array of string);
 begin
lnk.patch (6,454 bytes)   
Fixed in Revision
Operating systemWindows
WidgetsetWin32
Architecture64-bit

Activities

CryHam

2019-03-30 23:58

reporter   ~0003121

Last edited: 2019-03-30 23:59

Wow, I just implemented this.
I needed the function GetShortcutTarget from here:
http://forum.lazarus.freepascal.org/index.php?topic=1709.0

There is an issue still, it works only for paths with just ansi chars. Probably
something bad in this line:
MultiByteToWideChar(CP_ACP, 0, pAnsiChar(ShortcutFilename), -1, WideName, Max_Path);
or maybe the whole function, not sure.

I also BTW added cm_CreateShortcut, that makes .lnk to file/dir under cursor.

Probably only useful on Windows.

Patch is based on svn rev 8763. I only tested this on Windows 7.

Alexx2000

2019-03-31 00:23

administrator   ~0003123

DC already has function FileIsLinkToFolder that has correct Unicode implementation. It can be changed to work with files too or used as template.

Create shortcut command already exists in "Files" menu.

w2017

2019-12-22 19:56

reporter   ~0003357

(Tested on Ubuntu 18.04.3 LTS)

The current version DC 0.9.6 behaves like Total Commander when using Ctrl-Left/Right with files and folders, which is a good idea IMO.
(If we tab over there, the same file/folder gets the cursor position, which again matches TC.)

But DC treats links to folders just like links to files: make the other pane identical to the current pane (and position the cursor).

IMO using Ctrl-Left/Right on a link to a folder should go into the folder in the other pane like using Enter.

To go to the "cd -P" equivalent I suggest making the Symlink info in the properties dialog (marked in blue in the attachment) a clickable link and jumping to that destination when we click onto it.
(Actually for linked files the same should be possible.)

Issue History

Date Modified Username Field Change
2017-05-14 15:28 CryHam New Issue
2019-03-30 23:55 CryHam File Added: lnk.patch
2019-03-30 23:58 CryHam Note Added: 0003121
2019-03-30 23:58 CryHam Note Edited: 0003121
2019-03-30 23:59 CryHam Note Edited: 0003121
2019-03-31 00:23 Alexx2000 Note Added: 0003123
2019-12-22 19:56 w2017 Note Added: 0003357
2019-12-22 19:57 w2017 File Added: 2019-12-22 properties of link to dir.png