4 * Copyright 2007 Mikolaj Zalewski
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/debug.h"
39 #include "wine/heap.h"
40 #include "wine/unicode.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(browseui
);
47 #define CANCEL_MSG_LINE 2
49 /* Note: to avoid a deadlock we don't want to send messages to the dialog
50 * with the critical section held. Instead we only mark what fields should be
51 * updated and the dialog proc does the update */
52 #define UPDATE_PROGRESS 0x1
53 #define UPDATE_TITLE 0x2
54 #define UPDATE_LINE1 0x4
55 #define UPDATE_LINE2 (UPDATE_LINE1<<1)
56 #define UPDATE_LINE3 (UPDATE_LINE2<<2)
59 #define WM_DLG_UPDATE (WM_APP+1) /* set to the dialog when it should update */
60 #define WM_DLG_DESTROY (WM_APP+2) /* DestroyWindow must be called from the owning thread */
62 typedef struct tagProgressDialog
{
63 IProgressDialog IProgressDialog_iface
;
64 IOleWindow IOleWindow_iface
;
74 ULONGLONG ullCompleted
;
76 HWND hwndDisabledParent
; /* For modal dialog: the parent that need to be re-enabled when the dialog ends */
79 static inline ProgressDialog
*impl_from_IProgressDialog(IProgressDialog
*iface
)
81 return CONTAINING_RECORD(iface
, ProgressDialog
, IProgressDialog_iface
);
84 static inline ProgressDialog
*impl_from_IOleWindow(IOleWindow
*iface
)
86 return CONTAINING_RECORD(iface
, ProgressDialog
, IOleWindow_iface
);
89 static void set_buffer(LPWSTR
*buffer
, LPCWSTR string
)
91 static const WCHAR empty_string
[] = {0};
96 string
= empty_string
;
97 CoGetMalloc(MEMCTX_TASK
, &malloc
);
99 cb
= (strlenW(string
) + 1)*sizeof(WCHAR
);
100 if (*buffer
== NULL
|| cb
> IMalloc_GetSize(malloc
, *buffer
))
101 *buffer
= IMalloc_Realloc(malloc
, *buffer
, cb
);
102 memcpy(*buffer
, string
, cb
);
107 ProgressDialog
*This
;
112 static LPWSTR
load_string(HINSTANCE hInstance
, UINT uiResourceId
)
117 LoadStringW(hInstance
, uiResourceId
, string
, sizeof(string
)/sizeof(string
[0]));
118 ret
= HeapAlloc(GetProcessHeap(), 0, (strlenW(string
) + 1) * sizeof(WCHAR
));
119 strcpyW(ret
, string
);
123 static void set_progress_marquee(ProgressDialog
*This
)
125 HWND hProgress
= GetDlgItem(This
->hwnd
, IDC_PROGRESS_BAR
);
126 SetWindowLongW(hProgress
, GWL_STYLE
,
127 GetWindowLongW(hProgress
, GWL_STYLE
)|PBS_MARQUEE
);
130 static void update_dialog(ProgressDialog
*This
, DWORD dwUpdate
)
134 if (dwUpdate
& UPDATE_TITLE
)
135 SetWindowTextW(This
->hwnd
, This
->title
);
137 if (dwUpdate
& UPDATE_LINE1
)
138 SetDlgItemTextW(This
->hwnd
, IDC_TEXT_LINE
, (This
->isCancelled
? empty
: This
->lines
[0]));
139 if (dwUpdate
& UPDATE_LINE2
)
140 SetDlgItemTextW(This
->hwnd
, IDC_TEXT_LINE
+1, (This
->isCancelled
? empty
: This
->lines
[1]));
141 if (dwUpdate
& UPDATE_LINE3
)
142 SetDlgItemTextW(This
->hwnd
, IDC_TEXT_LINE
+2, (This
->isCancelled
? This
->cancelMsg
: This
->lines
[2]));
144 if (dwUpdate
& UPDATE_PROGRESS
)
146 ULONGLONG ullTotal
= This
->ullTotal
;
147 ULONGLONG ullCompleted
= This
->ullCompleted
;
149 /* progress bar requires 32-bit coordinates */
150 while (ullTotal
>> 32)
156 SendDlgItemMessageW(This
->hwnd
, IDC_PROGRESS_BAR
, PBM_SETRANGE32
, 0, (DWORD
)ullTotal
);
157 SendDlgItemMessageW(This
->hwnd
, IDC_PROGRESS_BAR
, PBM_SETPOS
, (DWORD
)ullCompleted
, 0);
161 static void end_dialog(ProgressDialog
*This
)
163 SendMessageW(This
->hwnd
, WM_DLG_DESTROY
, 0, 0);
164 /* native doesn't re-enable the window? */
165 if (This
->hwndDisabledParent
)
166 EnableWindow(This
->hwndDisabledParent
, TRUE
);
170 static INT_PTR CALLBACK
dialog_proc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
172 ProgressDialog
*This
= (ProgressDialog
*)GetWindowLongPtrW(hwnd
, DWLP_USER
);
178 struct create_params
*params
= (struct create_params
*)lParam
;
180 /* Note: until we set the hEvent, the object is protected by
181 * the critical section held by StartProgress */
182 SetWindowLongPtrW(hwnd
, DWLP_USER
, (LONG_PTR
)params
->This
);
186 if (This
->dwFlags
& PROGDLG_NOPROGRESSBAR
)
187 ShowWindow(GetDlgItem(hwnd
, IDC_PROGRESS_BAR
), SW_HIDE
);
188 if (This
->dwFlags
& PROGDLG_NOCANCEL
)
189 ShowWindow(GetDlgItem(hwnd
, IDCANCEL
), SW_HIDE
);
190 if (This
->dwFlags
& PROGDLG_MARQUEEPROGRESS
)
191 set_progress_marquee(This
);
192 if (This
->dwFlags
& PROGDLG_NOMINIMIZE
)
193 SetWindowLongW(hwnd
, GWL_STYLE
, GetWindowLongW(hwnd
, GWL_STYLE
) & (~WS_MINIMIZEBOX
));
195 update_dialog(This
, 0xffffffff);
197 This
->isCancelled
= FALSE
;
198 SetEvent(params
->hEvent
);
203 EnterCriticalSection(&This
->cs
);
204 update_dialog(This
, This
->dwUpdate
);
206 LeaveCriticalSection(&This
->cs
);
211 PostThreadMessageW(GetCurrentThreadId(), WM_NULL
, 0, 0); /* wake up the GetMessage */
216 if (msg
== WM_CLOSE
|| wParam
== IDCANCEL
)
218 EnterCriticalSection(&This
->cs
);
219 This
->isCancelled
= TRUE
;
221 if (!This
->cancelMsg
)
222 This
->cancelMsg
= load_string(BROWSEUI_hinstance
, IDS_CANCELLING
);
224 set_progress_marquee(This
);
225 EnableWindow(GetDlgItem(This
->hwnd
, IDCANCEL
), FALSE
);
226 update_dialog(This
, UPDATE_LINE1
|UPDATE_LINE2
|UPDATE_LINE3
);
227 LeaveCriticalSection(&This
->cs
);
234 static DWORD WINAPI
dialog_thread(LPVOID lpParameter
)
236 /* Note: until we set the hEvent in WM_INITDIALOG, the ProgressDialog object
237 * is protected by the critical section held by StartProgress */
238 struct create_params
*params
= lpParameter
;
239 ProgressDialog
*This
= params
->This
;
243 hwnd
= CreateDialogParamW(BROWSEUI_hinstance
, MAKEINTRESOURCEW(IDD_PROGRESS_DLG
),
244 params
->hwndParent
, dialog_proc
, (LPARAM
)params
);
246 while (GetMessageW(&msg
, NULL
, 0, 0) > 0)
250 if(!IsDialogMessageW(hwnd
, &msg
))
252 TranslateMessage(&msg
);
253 DispatchMessageW(&msg
);
257 IProgressDialog_Release(&This
->IProgressDialog_iface
);
261 static void ProgressDialog_Destructor(ProgressDialog
*This
)
263 TRACE("destroying %p\n", This
);
266 heap_free(This
->lines
[0]);
267 heap_free(This
->lines
[1]);
268 heap_free(This
->lines
[2]);
269 heap_free(This
->cancelMsg
);
270 heap_free(This
->title
);
271 This
->cs
.DebugInfo
->Spare
[0] = 0;
272 DeleteCriticalSection(&This
->cs
);
274 InterlockedDecrement(&BROWSEUI_refCount
);
277 static HRESULT WINAPI
ProgressDialog_QueryInterface(IProgressDialog
*iface
, REFIID iid
, LPVOID
*ppvOut
)
279 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
281 TRACE("(%p, %s, %p)\n", iface
, debugstr_guid(iid
), ppvOut
);
286 if (IsEqualIID(iid
, &IID_IUnknown
) || IsEqualIID(iid
, &IID_IProgressDialog
))
290 else if (IsEqualIID(iid
, &IID_IOleWindow
))
292 *ppvOut
= &This
->IOleWindow_iface
;
297 IProgressDialog_AddRef(iface
);
301 WARN("unsupported interface: %s\n", debugstr_guid(iid
));
302 return E_NOINTERFACE
;
305 static ULONG WINAPI
ProgressDialog_AddRef(IProgressDialog
*iface
)
307 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
308 return InterlockedIncrement(&This
->refCount
);
311 static ULONG WINAPI
ProgressDialog_Release(IProgressDialog
*iface
)
313 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
316 ret
= InterlockedDecrement(&This
->refCount
);
318 ProgressDialog_Destructor(This
);
322 static HRESULT WINAPI
ProgressDialog_StartProgressDialog(IProgressDialog
*iface
, HWND hwndParent
, IUnknown
*punkEnableModeless
, DWORD dwFlags
, LPCVOID reserved
)
324 static const INITCOMMONCONTROLSEX init
= { sizeof(init
), ICC_ANIMATE_CLASS
};
325 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
326 struct create_params params
;
329 TRACE("(%p, %p, %x, %p)\n", iface
, punkEnableModeless
, dwFlags
, reserved
);
330 if (punkEnableModeless
|| reserved
)
331 FIXME("Reserved parameters not null (%p, %p)\n", punkEnableModeless
, reserved
);
332 if (dwFlags
& PROGDLG_AUTOTIME
)
333 FIXME("Flags PROGDLG_AUTOTIME not supported\n");
334 if (dwFlags
& PROGDLG_NOTIME
)
335 FIXME("Flags PROGDLG_NOTIME not supported\n");
337 InitCommonControlsEx( &init
);
339 EnterCriticalSection(&This
->cs
);
343 LeaveCriticalSection(&This
->cs
);
344 return S_OK
; /* as on XP */
346 This
->dwFlags
= dwFlags
;
349 params
.hwndParent
= hwndParent
;
350 params
.hEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
352 /* thread holds one reference to ensure clean shutdown */
353 IProgressDialog_AddRef(&This
->IProgressDialog_iface
);
355 hThread
= CreateThread(NULL
, 0, dialog_thread
, ¶ms
, 0, NULL
);
356 WaitForSingleObject(params
.hEvent
, INFINITE
);
357 CloseHandle(params
.hEvent
);
358 CloseHandle(hThread
);
360 This
->hwndDisabledParent
= NULL
;
361 if (hwndParent
&& (dwFlags
& PROGDLG_MODAL
))
363 HWND hwndDisable
= GetAncestor(hwndParent
, GA_ROOT
);
364 if (EnableWindow(hwndDisable
, FALSE
))
365 This
->hwndDisabledParent
= hwndDisable
;
368 LeaveCriticalSection(&This
->cs
);
373 static HRESULT WINAPI
ProgressDialog_StopProgressDialog(IProgressDialog
*iface
)
375 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
377 EnterCriticalSection(&This
->cs
);
380 LeaveCriticalSection(&This
->cs
);
385 static HRESULT WINAPI
ProgressDialog_SetTitle(IProgressDialog
*iface
, LPCWSTR pwzTitle
)
387 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
390 TRACE("(%p, %s)\n", This
, wine_dbgstr_w(pwzTitle
));
392 EnterCriticalSection(&This
->cs
);
393 set_buffer(&This
->title
, pwzTitle
);
394 This
->dwUpdate
|= UPDATE_TITLE
;
396 LeaveCriticalSection(&This
->cs
);
399 SendMessageW(hwnd
, WM_DLG_UPDATE
, 0, 0);
404 static HRESULT WINAPI
ProgressDialog_SetAnimation(IProgressDialog
*iface
, HINSTANCE hInstance
, UINT uiResourceId
)
406 FIXME("(%p, %p, %d) - stub\n", iface
, hInstance
, uiResourceId
);
410 static BOOL WINAPI
ProgressDialog_HasUserCancelled(IProgressDialog
*iface
)
412 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
413 return This
->isCancelled
;
416 static HRESULT WINAPI
ProgressDialog_SetProgress64(IProgressDialog
*iface
, ULONGLONG ullCompleted
, ULONGLONG ullTotal
)
418 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
421 TRACE("(%p, 0x%s, 0x%s)\n", This
, wine_dbgstr_longlong(ullCompleted
), wine_dbgstr_longlong(ullTotal
));
423 EnterCriticalSection(&This
->cs
);
424 This
->ullTotal
= ullTotal
;
425 This
->ullCompleted
= ullCompleted
;
426 This
->dwUpdate
|= UPDATE_PROGRESS
;
428 LeaveCriticalSection(&This
->cs
);
431 SendMessageW(hwnd
, WM_DLG_UPDATE
, 0, 0);
433 return S_OK
; /* Windows sometimes returns S_FALSE */
436 static HRESULT WINAPI
ProgressDialog_SetProgress(IProgressDialog
*iface
, DWORD dwCompleted
, DWORD dwTotal
)
438 return IProgressDialog_SetProgress64(iface
, dwCompleted
, dwTotal
);
441 static HRESULT WINAPI
ProgressDialog_SetLine(IProgressDialog
*iface
, DWORD dwLineNum
, LPCWSTR pwzLine
, BOOL bPath
, LPCVOID reserved
)
443 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
446 TRACE("(%p, %d, %s, %d)\n", This
, dwLineNum
, wine_dbgstr_w(pwzLine
), bPath
);
449 FIXME("reserved pointer not null (%p)\n", reserved
);
452 if (dwLineNum
>= 3) /* Windows seems to do something like that */
455 EnterCriticalSection(&This
->cs
);
456 set_buffer(&This
->lines
[dwLineNum
], pwzLine
);
457 This
->dwUpdate
|= UPDATE_LINE1
<< dwLineNum
;
458 hwnd
= (This
->isCancelled
? NULL
: This
->hwnd
); /* no sense to send the message if window cancelled */
459 LeaveCriticalSection(&This
->cs
);
462 SendMessageW(hwnd
, WM_DLG_UPDATE
, 0, 0);
467 static HRESULT WINAPI
ProgressDialog_SetCancelMsg(IProgressDialog
*iface
, LPCWSTR pwzMsg
, LPCVOID reserved
)
469 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
472 TRACE("(%p, %s)\n", This
, wine_dbgstr_w(pwzMsg
));
475 FIXME("reserved pointer not null (%p)\n", reserved
);
477 EnterCriticalSection(&This
->cs
);
478 set_buffer(&This
->cancelMsg
, pwzMsg
);
479 This
->dwUpdate
|= UPDATE_LINE1
<< CANCEL_MSG_LINE
;
480 hwnd
= (This
->isCancelled
? This
->hwnd
: NULL
); /* no sense to send the message if window not cancelled */
481 LeaveCriticalSection(&This
->cs
);
484 SendMessageW(hwnd
, WM_DLG_UPDATE
, 0, 0);
489 static HRESULT WINAPI
ProgressDialog_Timer(IProgressDialog
*iface
, DWORD dwTimerAction
, LPCVOID reserved
)
491 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
493 FIXME("(%p, %d, %p) - stub\n", This
, dwTimerAction
, reserved
);
496 FIXME("Reserved field not NULL but %p\n", reserved
);
501 static const IProgressDialogVtbl ProgressDialogVtbl
=
503 ProgressDialog_QueryInterface
,
504 ProgressDialog_AddRef
,
505 ProgressDialog_Release
,
507 ProgressDialog_StartProgressDialog
,
508 ProgressDialog_StopProgressDialog
,
509 ProgressDialog_SetTitle
,
510 ProgressDialog_SetAnimation
,
511 ProgressDialog_HasUserCancelled
,
512 ProgressDialog_SetProgress
,
513 ProgressDialog_SetProgress64
,
514 ProgressDialog_SetLine
,
515 ProgressDialog_SetCancelMsg
,
519 static HRESULT WINAPI
OleWindow_QueryInterface(IOleWindow
*iface
, REFIID iid
, LPVOID
*ppvOut
)
521 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
522 return ProgressDialog_QueryInterface(&This
->IProgressDialog_iface
, iid
, ppvOut
);
525 static ULONG WINAPI
OleWindow_AddRef(IOleWindow
*iface
)
527 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
528 return ProgressDialog_AddRef(&This
->IProgressDialog_iface
);
531 static ULONG WINAPI
OleWindow_Release(IOleWindow
*iface
)
533 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
534 return ProgressDialog_Release(&This
->IProgressDialog_iface
);
537 static HRESULT WINAPI
OleWindow_GetWindow(IOleWindow
* iface
, HWND
* phwnd
)
539 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
541 TRACE("(%p, %p)\n", This
, phwnd
);
542 EnterCriticalSection(&This
->cs
);
544 LeaveCriticalSection(&This
->cs
);
548 static HRESULT WINAPI
OleWindow_ContextSensitiveHelp(IOleWindow
* iface
, BOOL fEnterMode
)
550 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
552 FIXME("(%p, %d): stub\n", This
, fEnterMode
);
556 static const IOleWindowVtbl OleWindowVtbl
=
558 OleWindow_QueryInterface
,
562 OleWindow_ContextSensitiveHelp
566 HRESULT
ProgressDialog_Constructor(IUnknown
*pUnkOuter
, IUnknown
**ppOut
)
568 ProgressDialog
*This
;
570 return CLASS_E_NOAGGREGATION
;
572 This
= heap_alloc_zero(sizeof(ProgressDialog
));
574 return E_OUTOFMEMORY
;
576 This
->IProgressDialog_iface
.lpVtbl
= &ProgressDialogVtbl
;
577 This
->IOleWindow_iface
.lpVtbl
= &OleWindowVtbl
;
579 InitializeCriticalSection(&This
->cs
);
580 This
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": ProgressDialog.cs");
582 TRACE("returning %p\n", This
);
583 *ppOut
= (IUnknown
*)&This
->IProgressDialog_iface
;
584 InterlockedIncrement(&BROWSEUI_refCount
);