I am looking for a way to protect powerpoint presentations via Automation using Delphi.
In Word, I can issue this command:
If (WordDocument.ProtectionType = wdNoProtection)
Then WordDocument.Protect (3, VarTrue, VarProtectPass);
In Excel, I can issue this command:
{ If the file was NOT protected, then protect it }
For SheetIndex := 1 To ExcelWorkbook.Sheets.Count Do Begin
VarSheet := SheetIndex;
{ Connect to the work sheet }
ExcelWorksheet.ConnectTo (ExcelWorkbook.Worksheets.Item [VarSheet] As _Worksheet);
ExcelWorksheet.Protect ()...
The problem is that I cannot find a way to do this using the OfficeXP.pas components. We are running Office 2010 on various systems. Any ideas?
What I need is to open powerpoint up with the presentation,
1) For MenuItem1, Disallow any editting by the user. Also, disallow them to re-save it somewhere else.
2) For MenuItem2, Allow the user to edit the presentation.
One approach to this would be via an add-in that traps events:
Trap the PresentationBeforeSave event and if need be, cancel the save.
Trap the WindowSelectionChange event, test to see what's selected (it's passed by the event) and in most cases, DE-select the selection. If the user can't select something, they can't edit/change it.
The event handling routines can be enabled/disabled based on state variables; you might trap the PresentationOpen event, test to see if the new presentation is one of your "protected" ones and if so, set boolIsProtected = True; your other event handlers could test this variable and stop processing the event if False.
Related
I am using CEF4Delphi from https://github.com/salvadordf/CEF4Delphi
I am editing PopupBrowser2 example. There is Chromium1 component. I have added event, that is supposed to notify when keyboard is requested:
procedure TForm1.Chromium1VirtualKeyboardRequested(Sender: TObject; const browser: ICefBrowser;
input_mode: TCefTextInpuMode);
begin
caption := 'kbd';
end;
Sadly, when example loads google page, I click on search edit box and event is not called. How can I make event to be called?
TChromium.OnVirtualKeyboardRequested is one of the events of ICefRenderHandler and that handler is only used by browsers in off-screen mode (OSR mode).
The PopupBrowser2 demo uses browsers in normal mode (a.k.a. "windowed mode") which means that none of the ICefRenderHandler events will be triggered.
With PopupBrowser2 you will have to use a different event called GlobalCEFApp.OnFocusedNodeChanged
Check the node.name with the HTML tag names that should show the virtual keyboard like "input" or "textarea" inside GlobalCEFApp.OnFocusedNodeChanged.
That event is executed in the render process and you will have to send a process message to the main browser process to show the keyboard.
The DOMVisitor demo shows you how to send that process message with the some parameters if you need them :
https://github.com/salvadordf/CEF4Delphi/blob/132edb2e8895d998d3e3810982c95b9f845d78f8/demos/Delphi_VCL/DOMVisitor/uDOMVisitor.pas#L305
The browser process will receive that message in the TChromium.OnProcessMessageReceived event as you can see here :
https://github.com/salvadordf/CEF4Delphi/blob/132edb2e8895d998d3e3810982c95b9f845d78f8/demos/Delphi_VCL/DOMVisitor/uDOMVisitor.pas#L432
Notice that TCefProcessMessageRef is created by the "New" function with a name. When you implement TChromium.OnProcessMessageReceived in your application you'll have to compare that message.name has the same name value before handling it.
I have a Delphi VCL form application (for Windows) with a TabControl component, where tabs are created at run time. Images property is linked to a ImageList, and I am using the OnGetImageIndex event to set the image index for each tab, based on a status information. At certain point, when status change, I need to update this images (indexes). My first idea is to call:
TabControl1.Invalidate;
But it doesn't work. I also tried another approachs without success:
TabControl1.Repaint;
RedrawWindow(TabControl1.Handle, nil, 0, RDW_ERASE or
RDW_INVALIDATE or RDW_ALLCHILDREN);
Please, how can I force OnGetImageIndex for each tab? Thanks!
You can call the protected UpdateTabImages method which sends a TCM_SETITEM for each tab.
type
TAccessTabControl = class(TTabControl);
...
TAccessTabControl(TabControl1).UpdateTabImages;
I have a workflow that adds a button "Open Link" and a field on the record called "URL" that contains a hyperlink to an attachment in NetSuite. I want to add a workflow action script that opens this url in a different page. I have added the script and the workflow action to the workflow. My script:
function openURL() {
var url = nlapiGetFieldValue('custbody_url');
window.open(url);
}
I get this script error after clicking the button: "TypeError: Cannot find function open in object [object Object].
How can I change my script so it opens the URL in the field?
(This function works when I try it in the console)
Thanks!
Do you want it to work when the record is being viewed or edited? They have slightly different scripts. I'm going to assume you want the button to work when the record is being viewed, but I'll write it so it works even when the document is being edited as well.
The hard part about the way Netsuite has set it up is that it requires two scripts, a user event script, and a client script. The way #michoel suggests may work too... I've never inserted the script by text before personally though.
I'll try that sometime today perhaps.
Here's a user event you could use (haven't tested it myself though, so you should run it through a test before deploying it to everyone).
function userEvent_beforeLoad(type, form, request)
{
/*
Add the specified client script to the document that is being shown
It looks it up by id, so you'll want to make sure the id is correct
*/
form.setScript("customscript_my_client_script");
/*
Add a button to the page which calls the openURL() method from a client script
*/
form.addButton("custpage_open_url", "Open URL", "openURL()");
}
Use this as the Suitescript file for a User Event script. Set the Before Load function in the Script Page to userEvent_beforeLoad. Make sure to deploy it to the record you want it to run on.
Here's the client script to go with it.
function openURL()
{
/*
nlapiGetFieldValue() gets the url client side in a changeable field, which nlapiLookupField (which looks it up server side) can't do
if your url is hidden/unchanging or you only care about view mode, you can just get rid of the below and use nlapiLookupField() instead
*/
var url = nlapiGetFieldValue('custbody_url');
/*
nlapiGetFieldValue() doesn't work in view mode (it returns null), so we need to use nlapiLookupField() instead
if you only care about edit mode, you don't need to use nlapiLookupField so you can ignore this
*/
if(url == null)
{
var myType = nlapiGetRecordType();
var myId = nlapiGetRecordId();
url = nlapiLookupField(myType, myId,'custbody_url');
}
//opening up the url
window.open(url);
}
Add it as a Client Script, but don't make any deployments (the User Event Script will attach it to the form for you). Make sure this script has the id customscript_my_client_script (or whatever script id you used in the user event script in form.setScript()) or else this won't work.
Another thing to keep in mind is that each record can only have one script appended to it using form.setScript() (I think?) so you may want to title the user event script and client script something related to the form you are deploying it on. Using form.setScript is equivalent to setting the script value when you are in the Customize Form menu.
If you can get #michoel's answer working, that may end up being better because you're keeping the logic all in one script which (from my point of view) makes it easier to manage your Suitescripts.
The problem you are running into is that Workflow Action Scripts execute on the server side, so you are not able to perform client side actions like opening up a new tab. I would suggest using a User Event Script which can "inject" client code into the button onclick function.
function beforeLoad(type, form) {
var script = "window.open(nlapiGetFieldValue('custbody_url'))";
form.addButton('custpage_custom_button', 'Open URL', script);
}
I use EmbeddedWB in edit mode and need to insert tab (4 * ) when user presses TAB key. I've trapped OnKeyDown event and did the following:
if (Key = VK_TAB) then
begin
EditDesignerMsg.InsertHTML(' ');
EditDesignerMsg.EmbeddedWB.SetFocusToDoc;
end;
The problem is that this moves focus from the control to another control as usual with TAB in Windows. I want to keep the focus within the web browser control and only move away to previous control if the user presses Shift + TAB.
How can this be done?
Thanks to TLama, I've managed to do this by intercepting CM_DIALOGKEY message and applying the message handler which inserts specified HTML code at that point and then eats the message by setting AMessage.Result := 1;. More details how to implement this message handler can be found here:
Intercept TAB key and suppress it
I have a Delphi application A, which I need to control from a .NET application B.
Among other things, I need to automate this process:
User selects item X from a combo box.
Application A notices the change and reacts by displaying a certain panel.
This works fine, if I do it manually.
But when the application B selects a combo box value, no panel is displayed.
This is the problem.
Potential cause of it:
When I select a combo box item, a certain windows message is fired. Some Delphi routine reacts to this message.
When I select a combo box item programmatically, the only message I send is CB_SETCURSEL and the Delphi app seems to ignore it.
Hence I assume that I can fix the problem, if I
get to know what windows messages are used as a basis for notification about combo box value changes (e. g. OnChange) and
send that windows message from C# application.
Therefore my question: What are the windows messages, on whose occurrence OnChange (and other events that notify the Delphi application on changed combo box selection) are fired?
Update 1: Started to implement the solution proposed by David Heffernan
private const int CB_SETCURSEL = 0x14E;
private const int WM_COMMAND = 0x0111;
private const int CBN_SELCHANGE = 0x001;
private const int CN_COMMAND = 0xBD11;
private int MakeWParam(int l, int h)
{
return (l & 0xFFFF) | (h << 16);
}
...
IntPtr comboBoxHandle = new IntPtr(comboBox.Current.NativeWindowHandle);
SendMessage(comboBoxHandle, CB_SETCURSEL, (Int32)myIndexInComboBox, 0);
SendMessage(comboBoxHandle, CN_COMMAND, MakeWParam(0, CBN_SELCHANGE), 0);
At the moment, it doesn't work.
Update 2:
I noticed a very strange thing.
If I invoke CB_SETCURSEL only, the desired item is selected in the combo box.
If I invoke CB_SETCURSEL and then (after 5 seconds) CN_COMMAND, then nothing is selected in the combo box.
This means - CB_SECURSEL selects the item and CN_COMMAND undoes it.
Update 3: Styles of the combo box according to Spy++:
WS_CHILDWINDOW
WS_VISIBLE
WS_CLIPSIBLINGS
00000243
Extended styles:
WS_EX_LEFT
WS_EX_LTRREADING
WS_EX_RIGHTSCROLLBAR
Class styles:
CS_VREDRAW
CS_HREDRAW
CS_DBLCLKS
Update 4: When I select the combo box item manually, I see following messages in the Spy++ output:
<00177> 0195085E S message:0xBD33 [Custom:WM_APP+15667] wParam:6801164A lParam:0195085E
<00178> 0195085E R message:0xBD33 [Custom:WM_APP+15667] lResult:4610165A
Unfortunately, I couldn't find documentation for this message.
Update 5: I noticed that the reaction to combo box selection change does occur, but only after a relatively long time (30 seconds to 1 minute). When I do the same thing manually, the reaction occurs instantaneously.
Potential cause of this behaviour: The thread of the .NET application makes the thread of the Delphi application wait for it. Note that the UI interaction code in the .NET app is executed in a separate thread (not the UI thread).
You should follow the CB_SETCURSEL message by sending the combo box a WM_COMMAND message with NotifyCode equal to CBN_SELCHANGE. It's the CBN_SELCHANGE that triggers the OnChange event.
In Delphi the code would look like this:
SendMessage(ComboHandle, CB_SETCURSEL, NewSelectionIndex, 0);
SendMessage(ComboHandle, WM_COMMAND, MakeWParam(0, CBN_SELCHANGE), ComboHandle);
Or you could use the CN_COMMAND message instead which would perhaps be a little more direct:
SendMessage(ComboHandle, CB_SETCURSEL, NewSelectionIndex, 0);
SendMessage(ComboHandle, CN_COMMAND, MakeWParam(0, CBN_SELCHANGE), 0);
You'll want to translate that into whichever .net language you are using, but I'm sure that's easy for you.
When the user selects a ComboBox item by hand, the control receives a CBN_SELCHANGE notification, which then triggers the TComboBox.OnChange event. When you select a ComboBox item programmably, no CBN_SELCHANGE notification is sent. This is documented behavior:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb775821.aspx
The CBN_SELCHANGE notification code is not sent when the current selection is set using the CB_SETCURSEL message.