1+ // Copyright (C) Microsoft Corporation. All rights reserved.
2+ // Use of this source code is governed by a BSD-style license that can be
3+ // found in the LICENSE file.
4+
5+ #include " stdafx.h"
6+
7+ #include " ScenarioSaveAs.h"
8+
9+ #include " App.h"
10+ #include " AppWindow.h"
11+ #include " CheckFailure.h"
12+ #include " Shlwapi.h"
13+ #include " TextInputDialog.h"
14+ #include " resource.h"
15+
16+ using namespace Microsoft ::WRL;
17+
18+ ScenarioSaveAs::ScenarioSaveAs (AppWindow* appWindow)
19+ : m_appWindow(appWindow), m_webView(appWindow->GetWebView ())
20+ {
21+ if (m_webView)
22+ {
23+ m_webView2Experimental25 = m_webView.try_query <ICoreWebView2Experimental25>();
24+ }
25+ }
26+
27+ std::initializer_list<COREWEBVIEW2_SAVE_AS_KIND> saveAsKind{
28+ COREWEBVIEW2_SAVE_AS_KIND_DEFAULT, COREWEBVIEW2_SAVE_AS_KIND_HTML_ONLY,
29+ COREWEBVIEW2_SAVE_AS_KIND_SINGLE_FILE, COREWEBVIEW2_SAVE_AS_KIND_COMPLETE};
30+ // Sync with COREWEBVIEW2_SAVE_AS_KIND.
31+ std::array<std::wstring, 4 > saveAsKindString{
32+ L" DEFAULT" , L" HTML_ONLY" , L" SIGNLE_FILE" , L" COMPLETE" };
33+ // Sync with COREWEBVIEW2_SAVE_AS_UI_RESULTS.
34+ std::array<std::wstring, 5 > saveAsUIResultString{
35+ L" SUCCESS" , L" INVALID_PATH" , L" FILE_ALREADY_EXISTS" , L" KIND_NOT_SUPPORTED" , L" CANCELLED" };
36+
37+ // ! [ToggleSilent]
38+ // Turn on/off Silent SaveAs, which won't show the system default save as dialog.
39+ // This example hides the default save as dialog and shows a customized dialog.
40+ bool ScenarioSaveAs::ToggleSilent ()
41+ {
42+ if (!m_webView2Experimental25)
43+ return false ;
44+ m_silentSaveAs = !m_silentSaveAs;
45+ if (m_silentSaveAs && m_saveAsUIShowingToken.value == 0 )
46+ {
47+ // Register a handler for the `SaveAsUIShowing` event.
48+ m_webView2Experimental25->add_SaveAsUIShowing (
49+ Callback<ICoreWebView2ExperimentalSaveAsUIShowingEventHandler>(
50+ [this ](
51+ ICoreWebView2* sender,
52+ ICoreWebView2ExperimentalSaveAsUIShowingEventArgs* args) -> HRESULT
53+ {
54+ // Hide the system default save as dialog.
55+ CHECK_FAILURE (args->put_SuppressDefaultDialog (TRUE ));
56+
57+ auto showCustomizedDialog = [this , args = wil::make_com_ptr (args)]
58+ {
59+ // Preview the content mime type, optional.
60+ wil::unique_cotaskmem_string mimeType;
61+ CHECK_FAILURE (args->get_ContentMimeType (&mimeType));
62+
63+ SaveAsDialog dialog (m_appWindow->GetMainWindow (), saveAsKind);
64+ if (dialog.confirmed )
65+ {
66+ // Set the SaveAsFilePath, Kind, AllowReplace for the event
67+ // args from this customized dialog inputs, optional. If nothing
68+ // needs to input, the event args will provide default values.
69+ CHECK_FAILURE (args->put_SaveAsFilePath (dialog.path .c_str ()));
70+ CHECK_FAILURE (args->put_Kind (dialog.selectedKind ));
71+ CHECK_FAILURE (args->put_AllowReplace (dialog.allowReplace ));
72+
73+ BOOL allowReplace;
74+ CHECK_FAILURE (args->get_AllowReplace (&allowReplace));
75+ wil::unique_cotaskmem_string path;
76+ CHECK_FAILURE (args->get_SaveAsFilePath (&path));
77+ COREWEBVIEW2_SAVE_AS_KIND selectedKind;
78+ CHECK_FAILURE (args->get_Kind (&selectedKind));
79+
80+ // Preview silent save as event args inputs, optional.
81+ MessageBox (
82+ m_appWindow->GetMainWindow (),
83+ (std::wstring (L" Content Mime Type: " ) + mimeType.get () + L" \n " +
84+ L" Fullpath: " + path.get () + L" \n " + L" Allow Replace: " +
85+ (allowReplace ? L" true" : L" false" ) + L" \n " +
86+ L" Selected Save As Kind: " + saveAsKindString[selectedKind])
87+ .c_str (),
88+ L" Silent Save As Parameters Preview" , MB_OK);
89+ }
90+ else
91+ {
92+ // Save As cancelled from this customized dialog.
93+ CHECK_FAILURE (args->put_Cancel (TRUE ));
94+ }
95+ };
96+
97+ wil::com_ptr<ICoreWebView2Deferral> deferral;
98+ CHECK_FAILURE (args->GetDeferral (&deferral));
99+
100+ m_appWindow->RunAsync (
101+ [deferral, showCustomizedDialog]()
102+ {
103+ showCustomizedDialog ();
104+ CHECK_FAILURE (deferral->Complete ());
105+ });
106+ return S_OK;
107+ })
108+ .Get (),
109+ &m_saveAsUIShowingToken);
110+ }
111+ else
112+ {
113+ // Unregister the handler for the `SaveAsUIShowing` event.
114+ m_webView2Experimental25->remove_SaveAsUIShowing (m_saveAsUIShowingToken);
115+ m_saveAsUIShowingToken.value = 0 ;
116+ }
117+ MessageBox (
118+ m_appWindow->GetMainWindow (),
119+ (m_silentSaveAs ? L" Silent Save As Enabled" : L" Silent Save As Disabled" ), L" Info" ,
120+ MB_OK);
121+ return true ;
122+ }
123+ // ! [ToggleSilent]
124+
125+ // ! [ProgrammaticSaveAs]
126+ // Call ShowSaveAsUI method to trigger the programmatic save as.
127+ bool ScenarioSaveAs::ProgrammaticSaveAs ()
128+ {
129+ if (!m_webView2Experimental25)
130+ return false ;
131+ m_webView2Experimental25->ShowSaveAsUI (
132+ Callback<ICoreWebView2ExperimentalShowSaveAsUICompletedHandler>(
133+ [this ](HRESULT errorCode, COREWEBVIEW2_SAVE_AS_UI_RESULT result) -> HRESULT
134+ {
135+ // Show ShowSaveAsUI returned result, optional.
136+ MessageBox (
137+ m_appWindow->GetMainWindow (),
138+ (L" ShowSaveAsUI " + saveAsUIResultString[result]).c_str (), L" Info" , MB_OK);
139+ return S_OK;
140+ })
141+ .Get ());
142+ return true ;
143+ }
144+ // ! [ProgrammaticSaveAs]
145+
146+ bool ScenarioSaveAs::HandleWindowMessage (
147+ HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result)
148+ {
149+ if (message == WM_COMMAND)
150+ {
151+ switch (LOWORD (wParam))
152+ {
153+ case IDM_SCENARIO_SAVE_AS_TOGGLE_SILENT:
154+ {
155+ return ToggleSilent ();
156+ }
157+ case IDM_SCENARIO_SAVE_AS_PROGRAMMATIC:
158+ {
159+ return ProgrammaticSaveAs ();
160+ }
161+ }
162+ }
163+ return false ;
164+ }
165+
166+ ScenarioSaveAs::~ScenarioSaveAs ()
167+ {
168+ if (m_webView2Experimental25)
169+ {
170+ m_webView2Experimental25->remove_SaveAsUIShowing (m_saveAsUIShowingToken);
171+ }
172+ }
173+
174+ static INT_PTR CALLBACK DlgProcStatic (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
175+ {
176+ auto self = (SaveAsDialog*)GetWindowLongPtr (hDlg, GWLP_USERDATA);
177+ switch (message)
178+ {
179+ case WM_INITDIALOG:
180+ {
181+ self = (SaveAsDialog*)lParam;
182+ SetWindowLongPtr (hDlg, GWLP_USERDATA, (LONG_PTR)self);
183+ HWND hwndList = GetDlgItem (hDlg, IDC_SAVE_AS_KIND);
184+ for (COREWEBVIEW2_SAVE_AS_KIND kind : self->kinds )
185+ {
186+ SendMessage (hwndList, CB_ADDSTRING, kind, (LPARAM)saveAsKindString[kind].c_str ());
187+ SendMessage (hwndList, CB_SETITEMDATA, kind, (LPARAM)kind);
188+ }
189+ SendMessage (hwndList, CB_SETCURSEL, 0 , 0 );
190+ return (INT_PTR)TRUE ;
191+ }
192+ case WM_COMMAND:
193+ {
194+ if (LOWORD (wParam) == IDOK)
195+ {
196+ HWND hwndList = GetDlgItem (hDlg, IDC_SAVE_AS_KIND);
197+
198+ wchar_t path[MAX_PATH] = {};
199+ int length = GetWindowTextLength (GetDlgItem (hDlg, IDC_EDIT_SAVE_AS_DIRECTORY)) +
200+ GetWindowTextLength (GetDlgItem (hDlg, IDC_EDIT_SAVE_AS_FILENAME));
201+ wchar_t directory[MAX_PATH] = {};
202+ GetDlgItemText (hDlg, IDC_EDIT_SAVE_AS_DIRECTORY, directory, length + 1 );
203+ wchar_t filename[MAX_PATH] = {};
204+ GetDlgItemText (hDlg, IDC_EDIT_SAVE_AS_FILENAME, filename, length + 1 );
205+ PathCombineW (path, directory, filename);
206+ self->path = path;
207+
208+ self->allowReplace = IsDlgButtonChecked (hDlg, IDC_CHECK_SAVE_AS_ALLOW_REPLACE);
209+
210+ int index = (int )SendMessage (hwndList, CB_GETCURSEL, 0 , 0 );
211+ self->selectedKind =
212+ (COREWEBVIEW2_SAVE_AS_KIND)SendMessage (hwndList, CB_GETITEMDATA, index, 0 );
213+
214+ self->confirmed = true ;
215+ }
216+ if (LOWORD (wParam) == IDOK || LOWORD (wParam) == IDCANCEL)
217+ {
218+ EndDialog (hDlg, LOWORD (wParam));
219+ return (INT_PTR)TRUE ;
220+ }
221+ break ;
222+ }
223+ case WM_NCDESTROY:
224+ SetWindowLongPtr (hDlg, GWLP_USERDATA, NULL );
225+ return (INT_PTR)TRUE ;
226+ }
227+ return (INT_PTR)FALSE ;
228+ }
229+
230+ SaveAsDialog::SaveAsDialog (HWND parent, std::initializer_list<COREWEBVIEW2_SAVE_AS_KIND> kinds)
231+ : kinds(kinds)
232+ {
233+ DialogBoxParam (
234+ g_hInstance, MAKEINTRESOURCE (IDD_SAVE_CONTENT_AS), parent, DlgProcStatic, (LPARAM)this );
235+ }
0 commit comments