How to implement the "Save target as" function

Dear SDL OpenExchange developers,

I would like to insert an action in the Studio UI which would first save the translation to its original format using the equivalent of "Save target as" and then apply further functions on the document.

I am not really sure on how to implement the "Save target as" function. I found the following action in the IntegrationApi: SaveDocumentTargetAsAction . However, I am not sure on how I can implement it in my code (and there are no example illustrating how to do it).

Can anyone help me or give me an indication on how I can declare/use it? it would be much appreciated. Or suggest an alternative?

Thanks in advance!

Regards,

Laurent

Parents
  • Hi Laurent,

    There is no direct save target as functions in API. But there are some workarounds which might satisfy your needs. For example you mihgt use the "Generate Target translations" task in the Project API to generate the target files. However running this task in project automation api will change the file status. You need set the file status back to the original. An alternate way is to use the EditorController.SaveTargetAs. Unfortunately this function prompts user to select the target files. To get rid of this prompt you might use code to commit the default selection. To check if this works, I made a simple test:

    I add a button (save target as) in the sample SDK project EditOperations view. And add the following code (see the code at the end of mail) to response the button click. The code just starts a new thread to look for the Save target dialogbox and to minic click on the save button. This function assumes you do not have the target files in the corresponding language folder yet. If the files already exist, it will prompt you further message. So you might change a bit the code to clear the target files first before running this function.

    Hope this helps.

    Xingzeng

    //------------------------------ The code starts here

    private void buttonSaveAsTarget_Click(object sender, EventArgs e)
    {
       var targetFiles = SaveActiveDocumentAsTarget();
     
    var targetFileListAsString = string.Join(System.Environment.NewLine, targetFiles);
      
    MessageBox.Show("Saved the current document target as the following files: " + targetFileListAsString);
    }

    // save target for the active document, return the paths of saved files
    string[] SaveActiveDocumentAsTarget()
    {
      // this task is executed in a separate thread to dismiss the file dialog
     
    var task = System.Threading.Tasks.Task.Factory.StartNew(
            () =>
            {
               const string expectedTitle = "Save Target As";    
               const string expectedCommitButtonText = "&Save";
          
    for (var i = 0; i < 20; i ++ ) // loop 20 times to make sure we can catch the dialog
               {
                  var hWnd = FindWindow(null, expectedTitle);
            
    if (hWnd != IntPtr.Zero)
                   {
                       var childs = GetChildWindows(hWnd);  
                       var saveButtonHandle = childs.FirstOrDefault(h => string.Compare(GetWindowText(h), expectedCommitButtonText) == 0);
               
    if (saveButtonHandle != IntPtr.Zero)
                        {
                          this.Invoke(new Action( () =>
                                            {
                                                SendMessage(saveButtonHandle, WM_LBUTTONDOWN, MK_LBUTTON, IntPtr.Zero);
                                                SendMessage(saveButtonHandle, WM_LBUTTONUP, MK_LBUTTON, IntPtr.Zero);
                                            }));
                           break; // done to dismiss the dialogbox, no need to do further loop
                        }
                    }
                   System.Threading.Thread.Sleep(10); // wait for 0.1 second
                }
            });
        // save the active document as target
        GetEditorController().SaveTargetAs(GetEditorController().ActiveDocument);
        var targetFiles = GetEditorController().ActiveDocument.Files.Select(
            f => System.IO.Path.Combine(System.IO.Path.GetDirectoryName(f.LocalFilePath),
                                        System.IO.Path.GetFileNameWithoutExtension(f.LocalFilePath))).ToArray();
      
    return targetFiles;
    }

    static string GetWindowText(IntPtr hWnd)
    {
      const int bufferSize = 256;
     
    var sb = new StringBuilder(bufferSize);
      GetWindowText(hWnd, sb, bufferSize);
     return sb.ToString();
    }

    static IntPtr[] GetChildWindows(IntPtr hWnd)
    {
      var ret = new List<IntPtr>();
     
    var retHandle = System.Runtime.InteropServices.GCHandle.Alloc(ret);
     
    try
        {
            EnumChildWindows(hWnd, EnumWindowCallback, System.Runtime.InteropServices.GCHandle.ToIntPtr(retHandle));
        
    return ret.ToArray();
        }
    catch (Exception ex)    {
       Console.WriteLine(ex);
     
    throw;
        }
    }

    static bool EnumWindowCallback(IntPtr hWnd, IntPtr parameter)
    {  
      var handle = System.Runtime.InteropServices.GCHandle.FromIntPtr(parameter);
     
    var list = handle.Target as List<IntPtr>;
     
    list.Add(hWnd);
      return true;
    }

    private const int WM_LBUTTONDOWN = 0x0201;
    private const int WM_LBUTTONUP = 0x0202;
    private const int MK_LBUTTON = 1;

    public delegate bool EnumWindowProc(IntPtr hwnd, IntPtr parameter);

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern IntPtr FindWindow(string className, string windowTitle);

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowProc callback, IntPtr parameter);

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern IntPtr GetActiveWindow();

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern int SendMessage(IntPtr hWnd, UInt32 msg, int wParam, IntPtr lParam);

     

Reply
  • Hi Laurent,

    There is no direct save target as functions in API. But there are some workarounds which might satisfy your needs. For example you mihgt use the "Generate Target translations" task in the Project API to generate the target files. However running this task in project automation api will change the file status. You need set the file status back to the original. An alternate way is to use the EditorController.SaveTargetAs. Unfortunately this function prompts user to select the target files. To get rid of this prompt you might use code to commit the default selection. To check if this works, I made a simple test:

    I add a button (save target as) in the sample SDK project EditOperations view. And add the following code (see the code at the end of mail) to response the button click. The code just starts a new thread to look for the Save target dialogbox and to minic click on the save button. This function assumes you do not have the target files in the corresponding language folder yet. If the files already exist, it will prompt you further message. So you might change a bit the code to clear the target files first before running this function.

    Hope this helps.

    Xingzeng

    //------------------------------ The code starts here

    private void buttonSaveAsTarget_Click(object sender, EventArgs e)
    {
       var targetFiles = SaveActiveDocumentAsTarget();
     
    var targetFileListAsString = string.Join(System.Environment.NewLine, targetFiles);
      
    MessageBox.Show("Saved the current document target as the following files: " + targetFileListAsString);
    }

    // save target for the active document, return the paths of saved files
    string[] SaveActiveDocumentAsTarget()
    {
      // this task is executed in a separate thread to dismiss the file dialog
     
    var task = System.Threading.Tasks.Task.Factory.StartNew(
            () =>
            {
               const string expectedTitle = "Save Target As";    
               const string expectedCommitButtonText = "&Save";
          
    for (var i = 0; i < 20; i ++ ) // loop 20 times to make sure we can catch the dialog
               {
                  var hWnd = FindWindow(null, expectedTitle);
            
    if (hWnd != IntPtr.Zero)
                   {
                       var childs = GetChildWindows(hWnd);  
                       var saveButtonHandle = childs.FirstOrDefault(h => string.Compare(GetWindowText(h), expectedCommitButtonText) == 0);
               
    if (saveButtonHandle != IntPtr.Zero)
                        {
                          this.Invoke(new Action( () =>
                                            {
                                                SendMessage(saveButtonHandle, WM_LBUTTONDOWN, MK_LBUTTON, IntPtr.Zero);
                                                SendMessage(saveButtonHandle, WM_LBUTTONUP, MK_LBUTTON, IntPtr.Zero);
                                            }));
                           break; // done to dismiss the dialogbox, no need to do further loop
                        }
                    }
                   System.Threading.Thread.Sleep(10); // wait for 0.1 second
                }
            });
        // save the active document as target
        GetEditorController().SaveTargetAs(GetEditorController().ActiveDocument);
        var targetFiles = GetEditorController().ActiveDocument.Files.Select(
            f => System.IO.Path.Combine(System.IO.Path.GetDirectoryName(f.LocalFilePath),
                                        System.IO.Path.GetFileNameWithoutExtension(f.LocalFilePath))).ToArray();
      
    return targetFiles;
    }

    static string GetWindowText(IntPtr hWnd)
    {
      const int bufferSize = 256;
     
    var sb = new StringBuilder(bufferSize);
      GetWindowText(hWnd, sb, bufferSize);
     return sb.ToString();
    }

    static IntPtr[] GetChildWindows(IntPtr hWnd)
    {
      var ret = new List<IntPtr>();
     
    var retHandle = System.Runtime.InteropServices.GCHandle.Alloc(ret);
     
    try
        {
            EnumChildWindows(hWnd, EnumWindowCallback, System.Runtime.InteropServices.GCHandle.ToIntPtr(retHandle));
        
    return ret.ToArray();
        }
    catch (Exception ex)    {
       Console.WriteLine(ex);
     
    throw;
        }
    }

    static bool EnumWindowCallback(IntPtr hWnd, IntPtr parameter)
    {  
      var handle = System.Runtime.InteropServices.GCHandle.FromIntPtr(parameter);
     
    var list = handle.Target as List<IntPtr>;
     
    list.Add(hWnd);
      return true;
    }

    private const int WM_LBUTTONDOWN = 0x0201;
    private const int WM_LBUTTONUP = 0x0202;
    private const int MK_LBUTTON = 1;

    public delegate bool EnumWindowProc(IntPtr hwnd, IntPtr parameter);

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern IntPtr FindWindow(string className, string windowTitle);

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowProc callback, IntPtr parameter);

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern IntPtr GetActiveWindow();

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    private static extern int SendMessage(IntPtr hWnd, UInt32 msg, int wParam, IntPtr lParam);

     

Children