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
25 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(browseui
);
42 #define CANCEL_MSG_LINE 2
44 /* Note: to avoid a deadlock we don't want to send messages to the dialog
45 * with the critical section held. Instead we only mark what fields should be
46 * updated and the dialog proc does the update */
47 #define UPDATE_PROGRESS 0x1
48 #define UPDATE_TITLE 0x2
49 #define UPDATE_LINE1 0x4
50 #define UPDATE_LINE2 (UPDATE_LINE1<<1)
51 #define UPDATE_LINE3 (UPDATE_LINE2<<2)
54 #define WM_DLG_UPDATE (WM_APP+1) /* set to the dialog when it should update */
55 #define WM_DLG_DESTROY (WM_APP+2) /* DestroyWindow must be called from the owning thread */
57 typedef struct tagProgressDialog
{
58 IProgressDialog IProgressDialog_iface
;
59 IOleWindow IOleWindow_iface
;
69 ULONGLONG ullCompleted
;
71 HWND hwndDisabledParent
; /* For modal dialog: the parent that need to be re-enabled when the dialog ends */
73 LPWSTR remainingMsg
[2];
77 static inline ProgressDialog
*impl_from_IProgressDialog(IProgressDialog
*iface
)
79 return CONTAINING_RECORD(iface
, ProgressDialog
, IProgressDialog_iface
);
82 static inline ProgressDialog
*impl_from_IOleWindow(IOleWindow
*iface
)
84 return CONTAINING_RECORD(iface
, ProgressDialog
, IOleWindow_iface
);
87 static void set_buffer(LPWSTR
*buffer
, LPCWSTR string
)
90 *buffer
= wcsdup(string
? string
: L
"");
100 static LPWSTR
load_string(HINSTANCE hInstance
, UINT uiResourceId
)
105 LoadStringW(hInstance
, uiResourceId
, string
, ARRAY_SIZE(string
));
106 ret
= malloc((lstrlenW(string
) + 1) * sizeof(WCHAR
));
107 lstrcpyW(ret
, string
);
111 static void set_progress_marquee(ProgressDialog
*This
)
113 HWND hProgress
= GetDlgItem(This
->hwnd
, IDC_PROGRESS_BAR
);
114 SetWindowLongW(hProgress
, GWL_STYLE
,
115 GetWindowLongW(hProgress
, GWL_STYLE
)|PBS_MARQUEE
);
118 static void update_dialog(ProgressDialog
*This
, DWORD dwUpdate
)
120 if (dwUpdate
& UPDATE_TITLE
)
121 SetWindowTextW(This
->hwnd
, This
->title
);
123 if (dwUpdate
& UPDATE_LINE1
)
124 SetDlgItemTextW(This
->hwnd
, IDC_TEXT_LINE
, (This
->isCancelled
? L
"" : This
->lines
[0]));
125 if (dwUpdate
& UPDATE_LINE2
)
126 SetDlgItemTextW(This
->hwnd
, IDC_TEXT_LINE
+1, (This
->isCancelled
? L
"" : This
->lines
[1]));
127 if (dwUpdate
& UPDATE_LINE3
)
128 SetDlgItemTextW(This
->hwnd
, IDC_TEXT_LINE
+2, (This
->isCancelled
? This
->cancelMsg
: This
->lines
[2]));
130 if (dwUpdate
& UPDATE_PROGRESS
)
132 ULONGLONG ullTotal
= This
->ullTotal
;
133 ULONGLONG ullCompleted
= This
->ullCompleted
;
135 /* progress bar requires 32-bit coordinates */
136 while (ullTotal
>> 32)
142 SendDlgItemMessageW(This
->hwnd
, IDC_PROGRESS_BAR
, PBM_SETRANGE32
, 0, (DWORD
)ullTotal
);
143 SendDlgItemMessageW(This
->hwnd
, IDC_PROGRESS_BAR
, PBM_SETPOS
, (DWORD
)ullCompleted
, 0);
147 static void load_time_strings(ProgressDialog
*This
)
151 for (i
= 0; i
< 2; i
++)
153 if (!This
->remainingMsg
[i
])
154 This
->remainingMsg
[i
] = load_string(BROWSEUI_hinstance
, IDS_REMAINING1
+ i
);
156 for (i
= 0; i
< 3; i
++)
158 if (!This
->timeMsg
[i
])
159 This
->timeMsg
[i
] = load_string(BROWSEUI_hinstance
, IDS_SECONDS
+ i
);
163 static void end_dialog(ProgressDialog
*This
)
165 SendMessageW(This
->hwnd
, WM_DLG_DESTROY
, 0, 0);
166 /* native doesn't re-enable the window? */
167 if (This
->hwndDisabledParent
)
168 EnableWindow(This
->hwndDisabledParent
, TRUE
);
172 static INT_PTR CALLBACK
dialog_proc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
174 ProgressDialog
*This
= (ProgressDialog
*)GetWindowLongPtrW(hwnd
, DWLP_USER
);
180 struct create_params
*params
= (struct create_params
*)lParam
;
182 /* Note: until we set the hEvent, the object is protected by
183 * the critical section held by StartProgress */
184 SetWindowLongPtrW(hwnd
, DWLP_USER
, (LONG_PTR
)params
->This
);
188 if (This
->dwFlags
& PROGDLG_NOPROGRESSBAR
)
189 ShowWindow(GetDlgItem(hwnd
, IDC_PROGRESS_BAR
), SW_HIDE
);
190 if (This
->dwFlags
& PROGDLG_NOCANCEL
)
191 ShowWindow(GetDlgItem(hwnd
, IDCANCEL
), SW_HIDE
);
192 if (This
->dwFlags
& PROGDLG_MARQUEEPROGRESS
)
193 set_progress_marquee(This
);
194 if (This
->dwFlags
& PROGDLG_NOMINIMIZE
)
195 SetWindowLongW(hwnd
, GWL_STYLE
, GetWindowLongW(hwnd
, GWL_STYLE
) & (~WS_MINIMIZEBOX
));
197 update_dialog(This
, 0xffffffff);
199 This
->isCancelled
= FALSE
;
200 SetEvent(params
->hEvent
);
205 EnterCriticalSection(&This
->cs
);
206 update_dialog(This
, This
->dwUpdate
);
208 LeaveCriticalSection(&This
->cs
);
213 PostThreadMessageW(GetCurrentThreadId(), WM_NULL
, 0, 0); /* wake up the GetMessage */
218 if (msg
== WM_CLOSE
|| wParam
== IDCANCEL
)
220 EnterCriticalSection(&This
->cs
);
221 This
->isCancelled
= TRUE
;
223 if (!This
->cancelMsg
)
224 This
->cancelMsg
= load_string(BROWSEUI_hinstance
, IDS_CANCELLING
);
226 set_progress_marquee(This
);
227 EnableWindow(GetDlgItem(This
->hwnd
, IDCANCEL
), FALSE
);
228 update_dialog(This
, UPDATE_LINE1
|UPDATE_LINE2
|UPDATE_LINE3
);
229 LeaveCriticalSection(&This
->cs
);
236 static DWORD WINAPI
dialog_thread(LPVOID lpParameter
)
238 /* Note: until we set the hEvent in WM_INITDIALOG, the ProgressDialog object
239 * is protected by the critical section held by StartProgress */
240 struct create_params
*params
= lpParameter
;
241 ProgressDialog
*This
= params
->This
;
245 hwnd
= CreateDialogParamW(BROWSEUI_hinstance
, MAKEINTRESOURCEW(IDD_PROGRESS_DLG
),
246 params
->hwndParent
, dialog_proc
, (LPARAM
)params
);
248 while (GetMessageW(&msg
, NULL
, 0, 0) > 0)
252 if(!IsDialogMessageW(hwnd
, &msg
))
254 TranslateMessage(&msg
);
255 DispatchMessageW(&msg
);
259 IProgressDialog_Release(&This
->IProgressDialog_iface
);
263 static void ProgressDialog_Destructor(ProgressDialog
*This
)
266 TRACE("destroying %p\n", This
);
269 for (i
= 0; i
< ARRAY_SIZE(This
->lines
); i
++)
270 free(This
->lines
[i
]);
271 free(This
->cancelMsg
);
273 for (i
= 0; i
< ARRAY_SIZE(This
->remainingMsg
); i
++)
274 free(This
->remainingMsg
[i
]);
275 for (i
= 0; i
< ARRAY_SIZE(This
->timeMsg
); i
++)
276 free(This
->timeMsg
[i
]);
277 This
->cs
.DebugInfo
->Spare
[0] = 0;
278 DeleteCriticalSection(&This
->cs
);
280 InterlockedDecrement(&BROWSEUI_refCount
);
283 static HRESULT WINAPI
ProgressDialog_QueryInterface(IProgressDialog
*iface
, REFIID iid
, LPVOID
*ppvOut
)
285 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
287 TRACE("(%p, %s, %p)\n", iface
, debugstr_guid(iid
), ppvOut
);
292 if (IsEqualIID(iid
, &IID_IUnknown
) || IsEqualIID(iid
, &IID_IProgressDialog
))
296 else if (IsEqualIID(iid
, &IID_IOleWindow
))
298 *ppvOut
= &This
->IOleWindow_iface
;
303 IProgressDialog_AddRef(iface
);
307 WARN("unsupported interface: %s\n", debugstr_guid(iid
));
308 return E_NOINTERFACE
;
311 static ULONG WINAPI
ProgressDialog_AddRef(IProgressDialog
*iface
)
313 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
314 return InterlockedIncrement(&This
->refCount
);
317 static ULONG WINAPI
ProgressDialog_Release(IProgressDialog
*iface
)
319 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
322 ret
= InterlockedDecrement(&This
->refCount
);
324 ProgressDialog_Destructor(This
);
328 static HRESULT WINAPI
ProgressDialog_StartProgressDialog(IProgressDialog
*iface
, HWND hwndParent
, IUnknown
*punkEnableModeless
, DWORD dwFlags
, LPCVOID reserved
)
330 static const INITCOMMONCONTROLSEX init
= { sizeof(init
), ICC_ANIMATE_CLASS
};
331 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
332 struct create_params params
;
335 TRACE("(%p, %p, %lx, %p)\n", iface
, punkEnableModeless
, dwFlags
, reserved
);
336 if (punkEnableModeless
|| reserved
)
337 FIXME("Reserved parameters not null (%p, %p)\n", punkEnableModeless
, reserved
);
338 if (dwFlags
& PROGDLG_NOTIME
)
339 FIXME("Flags PROGDLG_NOTIME not supported\n");
341 InitCommonControlsEx( &init
);
343 EnterCriticalSection(&This
->cs
);
347 LeaveCriticalSection(&This
->cs
);
348 return S_OK
; /* as on XP */
350 This
->dwFlags
= dwFlags
;
353 params
.hwndParent
= hwndParent
;
354 params
.hEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
356 /* thread holds one reference to ensure clean shutdown */
357 IProgressDialog_AddRef(&This
->IProgressDialog_iface
);
359 hThread
= CreateThread(NULL
, 0, dialog_thread
, ¶ms
, 0, NULL
);
360 WaitForSingleObject(params
.hEvent
, INFINITE
);
361 CloseHandle(params
.hEvent
);
362 CloseHandle(hThread
);
364 This
->hwndDisabledParent
= NULL
;
365 if (hwndParent
&& (dwFlags
& PROGDLG_MODAL
))
367 HWND hwndDisable
= GetAncestor(hwndParent
, GA_ROOT
);
368 if (EnableWindow(hwndDisable
, FALSE
))
369 This
->hwndDisabledParent
= hwndDisable
;
372 if (dwFlags
& PROGDLG_AUTOTIME
)
373 load_time_strings(This
);
375 This
->startTime
= GetTickCount64();
376 LeaveCriticalSection(&This
->cs
);
381 static HRESULT WINAPI
ProgressDialog_StopProgressDialog(IProgressDialog
*iface
)
383 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
385 EnterCriticalSection(&This
->cs
);
388 LeaveCriticalSection(&This
->cs
);
393 static HRESULT WINAPI
ProgressDialog_SetTitle(IProgressDialog
*iface
, LPCWSTR pwzTitle
)
395 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
398 TRACE("(%p, %s)\n", This
, wine_dbgstr_w(pwzTitle
));
400 EnterCriticalSection(&This
->cs
);
401 set_buffer(&This
->title
, pwzTitle
);
402 This
->dwUpdate
|= UPDATE_TITLE
;
404 LeaveCriticalSection(&This
->cs
);
407 SendMessageW(hwnd
, WM_DLG_UPDATE
, 0, 0);
412 static HRESULT WINAPI
ProgressDialog_SetAnimation(IProgressDialog
*iface
, HINSTANCE hInstance
, UINT uiResourceId
)
414 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
416 TRACE("(%p, %p, %u)\n", iface
, hInstance
, uiResourceId
);
418 if (IS_INTRESOURCE(uiResourceId
))
420 if (!SendDlgItemMessageW(This
->hwnd
, IDC_ANIMATION
, ACM_OPENW
, (WPARAM
)hInstance
, uiResourceId
))
421 WARN("Failed to load animation\n");
427 static BOOL WINAPI
ProgressDialog_HasUserCancelled(IProgressDialog
*iface
)
429 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
430 return This
->isCancelled
;
433 static void update_time_remaining(ProgressDialog
*This
, ULONGLONG ullCompleted
, ULONGLONG ullTotal
)
435 unsigned int remaining
, remainder
= 0;
441 if (!This
->startTime
|| !ullCompleted
|| !ullTotal
)
444 elapsed
= GetTickCount64() - This
->startTime
;
445 remaining
= (elapsed
* ullTotal
/ ullCompleted
- elapsed
) / 1000;
447 for (i
= 0; remaining
>= 60 && i
< 2; i
++)
449 remainder
= remaining
% 60;
454 args
[1] = (DWORD_PTR
)This
->timeMsg
[i
];
456 args
[3] = (DWORD_PTR
)This
->timeMsg
[i
-1];
458 if (i
> 0 && remaining
< 2 && remainder
!= 0)
459 FormatMessageW(FORMAT_MESSAGE_FROM_STRING
|FORMAT_MESSAGE_ARGUMENT_ARRAY
,
460 This
->remainingMsg
[1], 0, 0, line
, ARRAY_SIZE(line
), (__ms_va_list
*)args
);
462 FormatMessageW(FORMAT_MESSAGE_FROM_STRING
|FORMAT_MESSAGE_ARGUMENT_ARRAY
,
463 This
->remainingMsg
[0], 0, 0, line
, ARRAY_SIZE(line
), (__ms_va_list
*)args
);
465 set_buffer(&This
->lines
[2], line
);
466 This
->dwUpdate
|= UPDATE_LINE3
;
469 static HRESULT WINAPI
ProgressDialog_SetProgress64(IProgressDialog
*iface
, ULONGLONG ullCompleted
, ULONGLONG ullTotal
)
471 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
474 TRACE("(%p, 0x%s, 0x%s)\n", This
, wine_dbgstr_longlong(ullCompleted
), wine_dbgstr_longlong(ullTotal
));
476 EnterCriticalSection(&This
->cs
);
477 This
->ullTotal
= ullTotal
;
478 This
->ullCompleted
= ullCompleted
;
479 This
->dwUpdate
|= UPDATE_PROGRESS
;
481 if (This
->dwFlags
& PROGDLG_AUTOTIME
)
482 update_time_remaining(This
, ullCompleted
, ullTotal
);
483 LeaveCriticalSection(&This
->cs
);
486 SendMessageW(hwnd
, WM_DLG_UPDATE
, 0, 0);
488 return S_OK
; /* Windows sometimes returns S_FALSE */
491 static HRESULT WINAPI
ProgressDialog_SetProgress(IProgressDialog
*iface
, DWORD dwCompleted
, DWORD dwTotal
)
493 return IProgressDialog_SetProgress64(iface
, dwCompleted
, dwTotal
);
496 static HRESULT WINAPI
ProgressDialog_SetLine(IProgressDialog
*iface
, DWORD dwLineNum
, LPCWSTR pwzLine
, BOOL bPath
, LPCVOID reserved
)
498 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
501 TRACE("(%p, %ld, %s, %d)\n", This
, dwLineNum
, wine_dbgstr_w(pwzLine
), bPath
);
504 FIXME("reserved pointer not null (%p)\n", reserved
);
507 if (dwLineNum
>= 3) /* Windows seems to do something like that */
510 EnterCriticalSection(&This
->cs
);
511 set_buffer(&This
->lines
[dwLineNum
], pwzLine
);
512 This
->dwUpdate
|= UPDATE_LINE1
<< dwLineNum
;
513 hwnd
= (This
->isCancelled
? NULL
: This
->hwnd
); /* no sense to send the message if window cancelled */
514 LeaveCriticalSection(&This
->cs
);
517 SendMessageW(hwnd
, WM_DLG_UPDATE
, 0, 0);
522 static HRESULT WINAPI
ProgressDialog_SetCancelMsg(IProgressDialog
*iface
, LPCWSTR pwzMsg
, LPCVOID reserved
)
524 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
527 TRACE("(%p, %s)\n", This
, wine_dbgstr_w(pwzMsg
));
530 FIXME("reserved pointer not null (%p)\n", reserved
);
532 EnterCriticalSection(&This
->cs
);
533 set_buffer(&This
->cancelMsg
, pwzMsg
);
534 This
->dwUpdate
|= UPDATE_LINE1
<< CANCEL_MSG_LINE
;
535 hwnd
= (This
->isCancelled
? This
->hwnd
: NULL
); /* no sense to send the message if window not cancelled */
536 LeaveCriticalSection(&This
->cs
);
539 SendMessageW(hwnd
, WM_DLG_UPDATE
, 0, 0);
544 static HRESULT WINAPI
ProgressDialog_Timer(IProgressDialog
*iface
, DWORD dwTimerAction
, LPCVOID reserved
)
546 ProgressDialog
*This
= impl_from_IProgressDialog(iface
);
548 FIXME("(%p, %ld, %p) - stub\n", This
, dwTimerAction
, reserved
);
551 FIXME("Reserved field not NULL but %p\n", reserved
);
556 static const IProgressDialogVtbl ProgressDialogVtbl
=
558 ProgressDialog_QueryInterface
,
559 ProgressDialog_AddRef
,
560 ProgressDialog_Release
,
562 ProgressDialog_StartProgressDialog
,
563 ProgressDialog_StopProgressDialog
,
564 ProgressDialog_SetTitle
,
565 ProgressDialog_SetAnimation
,
566 ProgressDialog_HasUserCancelled
,
567 ProgressDialog_SetProgress
,
568 ProgressDialog_SetProgress64
,
569 ProgressDialog_SetLine
,
570 ProgressDialog_SetCancelMsg
,
574 static HRESULT WINAPI
OleWindow_QueryInterface(IOleWindow
*iface
, REFIID iid
, LPVOID
*ppvOut
)
576 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
577 return ProgressDialog_QueryInterface(&This
->IProgressDialog_iface
, iid
, ppvOut
);
580 static ULONG WINAPI
OleWindow_AddRef(IOleWindow
*iface
)
582 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
583 return ProgressDialog_AddRef(&This
->IProgressDialog_iface
);
586 static ULONG WINAPI
OleWindow_Release(IOleWindow
*iface
)
588 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
589 return ProgressDialog_Release(&This
->IProgressDialog_iface
);
592 static HRESULT WINAPI
OleWindow_GetWindow(IOleWindow
* iface
, HWND
* phwnd
)
594 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
596 TRACE("(%p, %p)\n", This
, phwnd
);
597 EnterCriticalSection(&This
->cs
);
599 LeaveCriticalSection(&This
->cs
);
603 static HRESULT WINAPI
OleWindow_ContextSensitiveHelp(IOleWindow
* iface
, BOOL fEnterMode
)
605 ProgressDialog
*This
= impl_from_IOleWindow(iface
);
607 FIXME("(%p, %d): stub\n", This
, fEnterMode
);
611 static const IOleWindowVtbl OleWindowVtbl
=
613 OleWindow_QueryInterface
,
617 OleWindow_ContextSensitiveHelp
621 HRESULT
ProgressDialog_Constructor(IUnknown
*pUnkOuter
, IUnknown
**ppOut
)
623 ProgressDialog
*This
;
625 return CLASS_E_NOAGGREGATION
;
627 if (!(This
= calloc(1, sizeof(*This
))))
628 return E_OUTOFMEMORY
;
630 This
->IProgressDialog_iface
.lpVtbl
= &ProgressDialogVtbl
;
631 This
->IOleWindow_iface
.lpVtbl
= &OleWindowVtbl
;
633 InitializeCriticalSection(&This
->cs
);
634 This
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": ProgressDialog.cs");
636 TRACE("returning %p\n", This
);
637 *ppOut
= (IUnknown
*)&This
->IProgressDialog_iface
;
638 InterlockedIncrement(&BROWSEUI_refCount
);