1.18.1.5.4 Floating Dialog to download file in another thread

Summary

In this tutorial, you build a dailog works as follows:

  1. Floating dialog with input/output box and two buttons.
  2. After a user input URL specify a file to download,Origin download the file in another thread so that user can use Origin to do other things.
  3. Dialog will update download status.
  4. A user can abort downloading thread.

Minimum Origin Version Required: Origin 2024b

Creating an HTML Page for Dialog

  1. Open Code Builder, create a new HTML file and save as DownloadFile.html to the folder Download Example.
  2. Copy the following code and paste it within the DownloadFile.html:
    <!doctype html>
    <html>
    	<head>
    		<meta charset="utf-8" />
    		<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    	</head>
    	<body>
    		<input id="path" style="width:350px" placeholder="input file url and click Download" onchange="On_Path()"></input>		
    		<br>
    		<input type="button" id="btnDownload" value="Download" onclick="Invoke_Download()"/>		
    		<input type="button" id="btnAbort" value="Abort" onclick="Invoke_Abort()"/>	
    		<br>
    		<input id="hint" style="width:350px;border:none" disabled="true"></div>
    	</body>
    	<script>
    		function On_Path() {
    			document.getElementById('btnDownload').disabled = false;
    			document.getElementById('hint').value = "";
    		}
    		function Invoke_Download() {
    			var Result = window.external.ExtCall("DownloadOnlineFile", document.getElementById('path').value);
    			document.getElementById('hint').value = Result;
    			document.getElementById('btnDownload').disabled = true;
    		}		
    		function Invoke_Abort() {
    			window.external.ExtCall("DownloadAbort");
    			document.getElementById('btnDownload').disabled = false;
    		}
    		function Update(str) { document.getElementById('hint').value = str; }
    	</script>    
    </html>
    Note: This code adds functionality to the HTML page you created above:
    • On_Path():Enable Download button and output message after a user input file url
    • Invoke_Download(): Respond to the event of a user hitting the Download button
    • Invoke_Abort(): Respond to the event of a user hitting the Abort button
    • Update(): Output download message

Once you finish this step, you can see the html page by opening the DownloadFile.html with a web browser.

In the functions Invoke_Download and Invoke_Abort, window.external.ExtCall is used to call Origin C. You can learn more about window.external.ExtCall from here.

Creating an Floating HTML Dialog

Now you are ready to edit the Origin C code to create an HTML dialog.

  1. In Code Builder, create a new cpp file named DownloadDlg.cpp under the folder Download Example.
  2. Include the needed headers.
    #include <Origin.h>
    #include <../OriginLab/DialogEx.h>
    #include <../OriginLab/HTMLDlg.h>
  3. Declare HTML dialog class DownloadDialog and define a pointer of DownloadDialog. This pointer is to make the HTML dialog floating.
    class DownloadDialog;
    static DownloadDialog* s_pDLDlg;
  4. Derive the HTML dialog class DownloadDialog from the class HTMLDlg, and point to the HTML page inside the method GetInitURL().
    class DownloadDialog: public HTMLDlg
    {
    protected:
    	string GetInitURL(){return GetFilePath(__FILE__) + "DownloadFile.html";}
    	string GetDialogTitle(){return "";}
    	BOOL GetDlgInitSize(int& width, int& height){ width = 400; height = 150; return true; }
    };
  5. Add Create function to invoke the modeless dialog box.
    public:
    	int Create()
    	{
    		InitMsgMap();
    		int nRet = Create(NULL, 0);
    		Visible = true;
    		return nRet;
    	}
  6. Map the dialog message inside the class DownloadDialog to specify which events will be handled and which function will be called to handle. Add event handler methods to respond to the event.
    protected:
    EVENTS_BEGIN_DERIV(HTMLDlg)
    	ON_DESTROY(OnDestroy)
    	ON_IDLE(OnIdle)
    EVENTS_END_DERIV
    	BOOL OnDestroy()
    	{
    		BOOL bRet = HTMLDlg::OnDestroy();
    		
    		delete this;
    		s_pDLDlg = NULL;
    		return bRet;
    	}
    	BOOL OnIdle()
    	{
    		string strMsg, strJS;
    		double err;
    		LT_get_var("@OCDL", &err); 
    		if( err < 0 ) 
    		{
    			if( !m_strFile.IsEmpty() )
    			{
    				switch(err)
    				{
    				case OHTTP_E_INVALID_URL:
    					strMsg = "Invalid URL"; 
    					break;
    				case OHTTP_E_USER_CANCEL: 
    					strMsg = "Abort";
    					break;
    				default:
    					strMsg.Format("err = %g", err);
    				}
    				m_strFile.Empty();
    			}
    		}
    		else if( err == 0 ) 
    		{
    			if( m_strFile.IsFile() )
    			{
    				strMsg.Format("download complete: %s", m_strFile);
    				m_strFile.Empty();
    			}			
    		}
    		else
    			strMsg.Format("downloading %.2f%%", err);
    		
    		
    		if( !strMsg.IsEmpty() )
    		{
    			strJS.Format("Update(\'%s\')", strMsg);//call Update() in html code			
    			CallJavaScript(strJS);
    		}
    		return TRUE;
    	}
    Note: OnIdle() is triggered when Origin is idle, and it will get download status and call JavaScript Update() by a member function CallJavaScript() of the DHtmlControl class.

Adding JavaScript Callable Prototypes in Origin C

In this section, inside your dialog class DownloadDialog you add method

  • DownloadOnlineFile() , which is called in JavaScript function Invoke_Download() in order to download file when the user hit the Download key;
  • DownloadAbort() , which is called in JavaScript function Invoke_Abort() in order to abort downloading thread when the user hit the Abort key.
  1. Add the method and DECLARE_DISPATCH_MAP inside your dialog class.
    public:	
    	DECLARE_DISPATCH_MAP
    	string DownloadOnlineFile(string strURL) 
    	{
    		string strMsg, strName = GetFileName(strURL);
    		m_strFile = GetOriginPath(ORIGIN_PATH_USER) + strName;
    		BOOL bWait = FALSE, bShowMsg = TRUE; // download file in a separate thread, and message log will show message after download complete
    		double err;
    		if( okutil_http_download(strURL, m_strFile, 0, 0, FALSE, bWait, bShowMsg) )
    		{
    			LT_get_var("@OCDL", &err); 
    			strMsg.Format("previous download not completed %.2f%%", err);
    		}
    		else
    			strMsg = "downloading...";
    		return strMsg;
    	}	
    	void DownloadAbort(){ LT_set_var("@OCDL", 2); }
    private:
    	string m_strFile;
  2. Map the method of your OriginC dialog class to this HTML dialog.
    BEGIN_DISPATCH_MAP(DownloadDialog, HTMLDlg)
        DISP_FUNCTION(DownloadDialog, DownloadOnlineFile, VTS_STR, VTS_STR)
        DISP_FUNCTION(DownloadDialog, DownloadAbort, VTS_VOID, VTS_VOID)
    END_DISPATCH_MAP
    Note:
    • More details of DISP_FUNCTION can be found here.
    • More details of okutil_http_download() can be found here.
    • System variable @OCDL return download status, and @OCDL > 1 to abort the downloading thread.

Launching The Dialog

You are ready to launch the dialog.

  1. Add main function:
    int DownloadDlg(int nMsg, DWORD dwCntrl = 0, LPVOID lpData = NULL)
    {
    	bool bClose = OMSG_CLOSE==nMsg? true:false;
    	if(bClose)
    	{
    		if(s_pDLDlg)
    		{
    			Window winDlg = s_pDLDlg->GetWindow();
    			if(winDlg)
    				winDlg.SendMessage(WM_CLOSE);
    			delete s_pDLDlg;
    			s_pDLDlg = NULL;
    		}
    		return 0;
    	}
    	
    	if(!s_pDLDlg)
    	{
    		s_pDLDlg = new DownloadDialog();
    		s_pDLDlg->Create();
    	}
    	return 0;
    }
    
    void open_downloaddlg(){DownloadDlg(OMSG_OPEN);}
  2. Save all the code and build it.
  3. Run open_downloaddlg to lauch the dialog.