GetSaveFileName / IFileSaveDialog - event on user input / react to file name change? - c++

I'm currently using a GetSaveFileName dialog in my C++ application. (As I know this was superseded by the IFileDialog interface I choose not to limit this question to the deprecated API but to accept solutions for both implementations).
The challenge is to react to user input (i.e. the user typing manually into the filename input) and modify the file dialog accordingly.
As a specific example consider automatically changing the file type in the file type dropdown when the user manually enters a supported file extension.
I had hoped that the lpfnHook member of the OPENFILENAME structure could do that for me, unfortunately it seems there is no event triggered for typed characters in the filename input.

The correct solution is to create the saved file using whatever filename the user types in, and format the file's content based on the selected filter. The user should be free to change the filter as desired before closing the dialog.
There is no need to react to individual key presses. But, if you must, then when using the legacy GetSaveFileName() API, the only option is to subclass the filename Edit field directly, such as with SetWindowSubclass(). You can get the Edit field's HWND using GetDlgItem() with the edt1 or cmb13 control ID. The subclass can intercept key press messages, then send the dialog a CDM_GETFILEPATH message to get the currently typed filename, parse the extension from it, and manually update the filter combo box accordingly. You can get the combo box's HWND using GetDlgItem() with the cmb1 control ID.
However, on Vista and later, GetSaveFileName() is just a wrapper for the newer IFileSaveDialog API when ComCtl32 v6 is used, so the above is not guaranteed to work anymore. But you can try it.
If you use the newer IFileDialog API directly, it does allow you to customize the dialog with new controls via IFileDialogCustomize, but it does not allow you to subclass existing controls (it provides no access to the HWNDs of the dialog or its controls. You would have to resort to lower level hooks to access them). If you did manage to subclass the filename Edit field, you could use IFileDialog::GetFileName() to retrieve the currently typed filename, but you can't update the filter combo box directly, as IFileDialog::SetFilterTypeIndex() can't be called once the dialog is displayed (unless you can figure out a way to get its HWND). On Vista only, you could try not specifying a filter at all (or, at least, a filter of *.*), and then use a combination of IFileDialogCustomize::AddComboBox() and IFileDialogControlEvents::OnItemSelected to simulate a manual filter list, and IFileDialog::SetFilter() to specify which items appear in the dialog based on the selected filter. But, SetFilter() is deprecated and no longer works in Windows 7 and later.

Related

Add an additional control to a QFileDialog

I am using the Native file dialog, so there is no layout() but want to add an additional control to the dialog. The builtin Notepad application has a perfect example when they allow the user to select the desired encoding (see below, to the left of the "Save" button). Is it possible to add an additional control in Qt5 while still using the native dialog?
TL;DR: Completely custom components are not available with native dialogs, filters can be controlled using QFileDialog::setNameFilters.
Qt's implementation of the native windows file dialog uses ABI::Windows::Storage::Pickers. You can check the implementation out here. Depending on the type of action you perform, the implementation uses IFileOpenPicker, IFolderPicker or IFileSavePicker.
If you want to manipulate the native dialog, this class would have to provide you with the option to do so. We can find the documentation for the FileSavePicker class here.
Going through the available options, you see that there is no interface to provide any specific custom UI element. If you would only need the filter, the QFileDialog provides you with the ability to set a filter using QFileDialog::setNameFilters. If you need a completely custom control/functionality, you unfortunately have to provide your own QDialog.

using SHAutoComplete with CEdit control

I am developing an MFC application, can i use SHAutoComplete with a CEdit control? Also is there any ready made auto complete controls are available? or i need to use write all the code for creating the list box below the edit control as user types in edit control?
Just pass CEdit's m_hwnd member to SHAutoComplete. I don't think that extension warrant another class. The listbox is created by the AutoComplete object created by SHAutoComplete.
SHAutoComplete helps to autocomplete paths (system or URL).
If this is a combo box and you want to use autocomplete for suggesting string contained in the combo, you have to write a code to handle it.
There are samples you can find. One I found (working):
http://www.ucancode.net/Visual_C_MFC_COM_faq/Visual-C-Auto-completion-ComboBox-CComboBox.htm

How to customize open/save file dialogs?

I have seen some programs use custom open/save file dialogs in that manner they add some extra buttons there.
How can I do this?
Note that I want to use the basic open/save dialogs, so I dont have to re-create them, thus remain the buttons in the OS language. I only want to add some extra buttons there.
Update: As David Heffernan points out below, this answer applies to Windows platforms before Vista. From Vista onwards, you should use the
IFileDialogCustomize COM interface instead.
Basically, you have to set the OFN_ENABLETEMPLATE flag in the OPENFILENAME structure you're passing to GetOpenFileName()/GetSaveFileName(). This allows you to specify a custom dialog template in the lpTemplateName member of the same structure. This template will be used to build the dialog box. See Explorer-Style Custom Templates for all the details.
Note that if you add your own controls to the dialog box (buttons in your case), you should
also set the OFN_ENABLEHOOK flag and specify a callback function in the lpfnHook member of the OPENFILENAME structure, so you can handle the messages sent by these controls. See Explorer-Style Hook Procedures for more information.
This should help you
Customizing common dialog controls

can I set the AppUserModelID of an arbitrary process launched through a jumplist?

I have a simple console application written in C++ that acts as a stub for launching another application through it's jumplist. Purpose is to add jumplist abilities to applications that do not support this. Call it stub.exe. When running stub.exe it creates a custom jumplist using these steps (taken right form the MS samples):
create an ICustomDestinationList
ICustomDestinationList::BeginList()
create an IObjectCollection
for_each item_to_add
create an IShellLink, set its path/arguments/title/icon
add IShellLink to the IObjectCollection
get the IObjectArray interface from the IObjectCollection
call ICustomDestinationList::AddUserTasks( IObjectArray interface )
ICustomDestinationList::CommitList()
When pinning stub.exe to the taskbar and right-clicking it, the jumpilst appears and it contains all IShellLinks added. When clicking an item, it will launch the corresponding process.
Now I'd like a process launched through this jumplist have it's window(s) grouped under stub.exe's taskbar icon, instead of having it's own group. They key to get this working seems to be the AppUsermodelID. This is what I tried so far:
just for testing, create a couple of shortcuts and set the id through IPropertyStore->SetValue( PKEY_AppUserModel_ID, "id" ). Indeed, when launching these shortcuts, they will all group under the same taskbar icon.
since the shortcuts do what I want, I tried adding shortcuts to stub.exe's jumplist: no effect. The shortcuts don't even show up in the jumplist (maybe one cannot have a shortcut to a shortcut?), yet all methods return S_OK
setting the PKEY_AppUserModel_ID on each of the IShellLinks that get added to the jumplist: no effect
calling ICustomDestinationList->SetAppID(): no effect
instead of using SubTasks, tried with SHAddToRecentDocs: no effect. The recent doc list does not show up. But now things get messy. After setting the AppUserModelID on the shortcut that is responsible for the pinned taskbar item (the one in %APPDATA%/Roaming/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar), the jumplist changed: it does not show the 'Tasks' item anymore, but does show 'Recent' and the items I added using SHAddToRecentDocs. Now when clicking them I get a dialog box with a title that starts with 'd:\desktop' followed by Chinese characters. Hovering the items in the jumplist also shows Chinese characters instead of the descirption I set.
Questions:
What's with the Chinese characters in the jumplist?
How come setting the app id on the taskbar shortcut toggles between 'Tasks' and 'Recent' sections, why are they not both there?
What would be the way, if even possible, to achive what I actually want: a custom jump list of which the items launched will group under it's taskbar icon? (note that the processes I plan to laucnh their do not have their app id set currently)
not much reactions here ;]
In the meantime I managed to solve the main problem myself; it's not quite a straightforward solution but it fullfills the requirements: a program runs in the backround and installs a CBT hook. Each time an application creates a window (HookProc code = HCBT_CREATEWND), the hook checks the application's path against a map containing paths and desired application ids. If a match is found, the application id of the HWND is set. Since this occurs before the window is actually shown and is combined with the custom task list, from a user's point of view the application behaves just like one that does support a recent/pinned document list.

add custom context menu to hosted web browser control

I am hosting a web browser control, and want to provide my own context menu.
Ideally, I want to present my own context menu, that contains the original browser's context menu (with all addins etc.) as a sub menu.
If that's not possible / to tricky, I'd be ok with e.g. normally showing my context menu, and showing the original one when the user presses SHIFT.
Do I need to implement IDocHostUIHandler? if yes, how do I specify a custom context menu, how can I force the original one? How do I get the control to use my implementation?
The control is created as such (error handling omitted):
HRESULT hr=AtlAxCreateControlEx(
L"Shell.Explorer",m_wndWebCtrl.m_hWnd,
NULL,NULL,(IUnknown**)&unk,
IID_IWebBrowser2, NULL); // (IPersistStreamInit*)this);
hr = AtlAdviseSinkMap( this, true);
IUnknownPtr unk;
AtlAxGetControl(m_wndWebCtrl.m_hWnd, &unk);
IWebBrowser2Ptr browser2 = unk;
Yes, you do need to implement IDocHostUIHandler.
Ok, i guess you could intercept right-clicks, keystrokes, and that other message that'll normally display a context menu... But that's probably gonna break badly sooner or later; at very least, i'd expect it to break accessibility.
Once you've intercepted IDocHostUIHandler::ShowContextMenu(), you have the option of returning S_OK to squelch the built-in menu after showing your own. You can use the normal Win32 menu routines for this purpose, a custom control, or even fancy HTML if that's what does it for you. Per the documentation, enough context is provided to allow you to determine what element context is requested for, and what the default context menu would be.
Unfortunately, I know of no way to get a handle to the built-in menu. You could probably fake it by showing your context menu and then returning S_FALSE if the user chose the "original" option, but even then there's no way to attach the resulting menu to an existing popup menu (which really should be gone by the time you return anyway if you're running the modal-loop common to such popups). It is possible to add options to the built-in menus.
You should be able to use GetKeyboardState() to determine the state of the shift key when the menu was requested.
Assuming you only want a subset of the normal browser functionality anyway, you might be better served by just re-implementing the options you want (back, forward, print) and invoking the appropriate command if the user chooses them. Alternately, if you only want normal menus in a very specific scenario (for instance: editing commands in a textarea), use the ShowContextMenu() arguments to identify this and only then return S_FALSE to trigger the default. I've had pretty good luck with this latter technique; after all, they are supposed to be context menus...
I am adding the following information how to "inject" your own DocHostUI implementation into the browser control:
The default way is to implement IOleClientSite, and support the IDocHostUIHandler on the implementing object.
There is a simpler way: In the DocumentComplete handler, query the IHTMLDocumennt for ICustomDoc, which allows you to supply your interface from a standalone object. This allows you to keep whatever IOleClientSite implementation your environment provides.
More details can be found here.