Return true from wxProgressDialog::Show() on success We accidentally returned false after successfully showing the native dialog, which didn't make sense.
Remove unnecessary explicit call to wxThread::Create() Run() creates the thread if necessary, so there is no need to do it here.
Remove untranslated error message never shown in practice Messages shown by wxLogError() are user-visible and so need to be translated, but it doesn't seem to be worth doing it for the message about failing to start a thread which should be never given -- and if the system is so low on resources that it can't create new threads, showing this message would probably fail anyhow. Just replace it with wxLogDebug().
Fall back to generic dialog if native wxProgressDialog fails Even if this is not supposed to happen, fall back to the generic progress dialog instead of just hanging if showing the native dialog fails, for whatever reason. This commit is best viewed with Git --color-moved option.
Fix showing wxProgressDialog with wxPD_CAN_SKIP style This was broken by 02cff90601 (Change return type of wxGetTranslation, 2026-02-15) which resulted in passing invalid pointer to the already destroyed temporary wxString "Skip" to the native dialog -- which detected this and failed, resulting in a hang because our code didn't handle its error correctly. Error handling was improved in the preceding commits, but now fix the real underlying bug by ensuring the string lives long enough. Closes #26287.
Fix crash when opening link in new window in wxWebViewWebKit The parameters of "ready-to-show" signal handler were deleted, by the handler itself, after a first call to it, but at least with the current version of webkit2gtk (2.50.4) this handler can be called more than once and it crashed when it happened. Fix this by disconnecting it as soon as it is called, which fixes the crash and doesn't seem to have any drawbacks. Note that we might want to find a better way of passing these parameters as currently they are leaked if the handler is not called at all, which might happen under some unusual circumstances. But for now just fix the crash. Closes #26298.
Add wxEVT_WEBVIEW_BROWSING_DATA_CLEARED to the documentation Closes #26299.
Fix static build regression in wxGTK due to wxMoveToTrash() Move this function to core library and only define it under the platforms that implement it. This commit is best view using Git --color-moved option. Closes #26290.
Allow matrix parameter of wxAffineMatrix2D::Get() to be null too Previously, only the second parameter, in which translation vector was returned, could be null. Make it possible to specify null for the first parameter if it is not needed too now. Still check that at least one parameter is not null, as it doesn't make sense to call the function otherwise. Update documentation to reflect the new behaviour. Closes #26303.
Merge branch 'msw-fix-progress-dialog-skip' Fix showing wxProgressDialog with wxPD_CAN_SKIP in wxMSW. See #26293.
| ... | ... | @@ -440,7 +440,9 @@ WXDLLIMPEXP_BASE bool wxRemoveFile(const wxString& file); |
| 440 | 440 | #define wxHAS_MOVE_TO_TRASH
|
| 441 | 441 | #endif
|
| 442 | 442 | |
| 443 | -WXDLLIMPEXP_BASE bool wxMoveToTrash(const wxString& path);
|
|
| 443 | +#ifdef wxHAS_MOVE_TO_TRASH
|
|
| 444 | + WXDLLIMPEXP_CORE bool wxMoveToTrash(const wxString& path);
|
|
| 445 | +#endif // wxHAS_MOVE_TO_TRASH
|
|
| 444 | 446 | |
| 445 | 447 | // Rename file
|
| 446 | 448 | WXDLLIMPEXP_BASE bool wxRenameFile(const wxString& oldpath, const wxString& newpath, bool overwrite = true);
|
| ... | ... | @@ -72,6 +72,12 @@ private: |
| 72 | 72 | // Get the task dialog geometry when using the native dialog.
|
| 73 | 73 | wxRect GetTaskDialogRect() const;
|
| 74 | 74 | |
| 75 | + // Returns true if the dialog is currently using the native task dialog.
|
|
| 76 | + bool UsingNativeTaskDialog() const { return m_sharedData != nullptr; }
|
|
| 77 | + |
|
| 78 | + // Show native dialog for the first time.
|
|
| 79 | + bool InitAndShowNative();
|
|
| 80 | + |
|
| 75 | 81 | |
| 76 | 82 | wxProgressDialogTaskRunner *m_taskDialogRunner;
|
| 77 | 83 |
| ... | ... | @@ -28,9 +28,11 @@ public: |
| 28 | 28 | /**
|
| 29 | 29 | Get the component values of the matrix.
|
| 30 | 30 | |
| 31 | + At least one of the parameters must be non-NULL.
|
|
| 32 | + |
|
| 31 | 33 | @param mat2D
|
| 32 | - The rotational components of the matrix (upper 2 x 2), must be
|
|
| 33 | - non-null.
|
|
| 34 | + The rotational components of the matrix (upper 2 x 2),
|
|
| 35 | + may be @NULL.
|
|
| 34 | 36 | @param tr
|
| 35 | 37 | The translational components of the matrix, may be @NULL.
|
| 36 | 38 | */
|
| ... | ... | @@ -356,8 +356,7 @@ bool wxRemoveFile(const wxString& file); |
| 356 | 356 | facility.
|
| 357 | 357 | |
| 358 | 358 | Preprocessor symbol @c wxHAS_MOVE_TO_TRASH is defined if this function is
|
| 359 | - supported on the current platform. If it is not defined, the function still
|
|
| 360 | - exists but always returns @false without doing anything else.
|
|
| 359 | + available on the current platform.
|
|
| 361 | 360 | |
| 362 | 361 | Implementation details:
|
| 363 | 362 | - Under Windows, this uses @c SHFileOperation with @c FOF_ALLOWUNDO.
|
| ... | ... | @@ -2183,3 +2183,4 @@ wxEventType wxEVT_WEBVIEW_FULLSCREEN_CHANGED; |
| 2183 | 2183 | wxEventType wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED;
|
| 2184 | 2184 | wxEventType wxEVT_WEBVIEW_SCRIPT_RESULT;
|
| 2185 | 2185 | wxEventType wxEVT_WEBVIEW_WINDOW_CLOSE_REQUESTED;
|
| 2186 | +wxEventType wxEVT_WEBVIEW_BROWSING_DATA_CLEARED; |
| ... | ... | @@ -29,10 +29,16 @@ void wxAffineMatrix2D::Set(const wxMatrix2D &mat2D, const wxPoint2DDouble &tr) |
| 29 | 29 | // gets the component values of the matrix
|
| 30 | 30 | void wxAffineMatrix2D::Get(wxMatrix2D *mat2D, wxPoint2DDouble *tr) const
|
| 31 | 31 | {
|
| 32 | - mat2D->m_11 = m_11;
|
|
| 33 | - mat2D->m_12 = m_12;
|
|
| 34 | - mat2D->m_21 = m_21;
|
|
| 35 | - mat2D->m_22 = m_22;
|
|
| 32 | + wxASSERT_MSG( mat2D || tr,
|
|
| 33 | + wxT("at least one parameter must be non-null") );
|
|
| 34 | + |
|
| 35 | + if( mat2D )
|
|
| 36 | + {
|
|
| 37 | + mat2D->m_11 = m_11;
|
|
| 38 | + mat2D->m_12 = m_12;
|
|
| 39 | + mat2D->m_21 = m_21;
|
|
| 40 | + mat2D->m_22 = m_22;
|
|
| 41 | + }
|
|
| 36 | 42 | |
| 37 | 43 | if ( tr )
|
| 38 | 44 | {
|
| ... | ... | @@ -641,15 +641,6 @@ bool wxRemoveFile(const wxString& file) |
| 641 | 641 | return res == 0;
|
| 642 | 642 | }
|
| 643 | 643 | |
| 644 | -#ifndef wxHAS_MOVE_TO_TRASH
|
|
| 645 | - |
|
| 646 | -bool wxMoveToTrash(const wxString& WXUNUSED(path))
|
|
| 647 | -{
|
|
| 648 | - return false;
|
|
| 649 | -}
|
|
| 650 | - |
|
| 651 | -#endif // !wxHAS_MOVE_TO_TRASH
|
|
| 652 | - |
|
| 653 | 644 | bool wxMkdir(const wxString& dir, int perm)
|
| 654 | 645 | {
|
| 655 | 646 | #if defined(__WXMAC__) && !defined(__UNIX__)
|
| ... | ... | @@ -423,11 +423,16 @@ class wxReadyToShowParams |
| 423 | 423 | public:
|
| 424 | 424 | wxWebViewWebKit* childWebView;
|
| 425 | 425 | wxWebViewWebKit* parentWebView;
|
| 426 | + gulong handlerId = 0;
|
|
| 426 | 427 | };
|
| 427 | 428 | |
| 428 | 429 | static void wxgtk_webview_webkit_ready_to_show (WebKitWebView *web_view,
|
| 429 | 430 | wxReadyToShowParams *params)
|
| 430 | 431 | {
|
| 432 | + // This handler must not be called more than once as it deletes params, so
|
|
| 433 | + // disconnect it immediately.
|
|
| 434 | + g_signal_handler_disconnect(web_view, params->handlerId);
|
|
| 435 | + |
|
| 431 | 436 | wxWebViewWindowFeaturesWebKit features(params->childWebView, web_view);
|
| 432 | 437 | wxWebViewEvent event(wxEVT_WEBVIEW_NEWWINDOW_FEATURES,
|
| 433 | 438 | params->parentWebView->GetId(),
|
| ... | ... | @@ -867,9 +872,8 @@ wxWebViewWebKit::wxWebViewWebKit(WebKitWebView* parentWebView, wxWebViewWebKit* |
| 867 | 872 | wxReadyToShowParams* params = new wxReadyToShowParams();
|
| 868 | 873 | params->childWebView = this;
|
| 869 | 874 | params->parentWebView = parentWebViewCtrl;
|
| 870 | - |
|
| 871 | - g_signal_connect(m_web_view, "ready-to-show",
|
|
| 872 | - G_CALLBACK(wxgtk_webview_webkit_ready_to_show), params);
|
|
| 875 | + params->handlerId = g_signal_connect(m_web_view, "ready-to-show",
|
|
| 876 | + G_CALLBACK(wxgtk_webview_webkit_ready_to_show), params);
|
|
| 873 | 877 | }
|
| 874 | 878 | |
| 875 | 879 | wxWebViewWebKit::wxWebViewWebKit(const wxWebViewConfiguration &config):
|
| ... | ... | @@ -59,6 +59,14 @@ const int wxSPDD_WINDOW_MOVED = 0x2000; |
| 59 | 59 | |
| 60 | 60 | const int Id_SkipBtn = wxID_HIGHEST + 1;
|
| 61 | 61 | |
| 62 | +// Enum indicating whether the native dialog was initialized.
|
|
| 63 | +enum class wxProgressDialogStatus
|
|
| 64 | +{
|
|
| 65 | + Initializing,
|
|
| 66 | + Initialized,
|
|
| 67 | + Failed
|
|
| 68 | +};
|
|
| 69 | + |
|
| 62 | 70 | } // anonymous namespace
|
| 63 | 71 | |
| 64 | 72 | // ============================================================================
|
| ... | ... | @@ -82,6 +90,10 @@ public: |
| 82 | 90 | |
| 83 | 91 | wxCriticalSection m_cs;
|
| 84 | 92 | |
| 93 | + // This field is set to either Initialized or Failed by the task dialog
|
|
| 94 | + // runner thread to indicate whether the dialog was successfully created.
|
|
| 95 | + wxProgressDialogStatus m_status = wxProgressDialogStatus::Initializing;
|
|
| 96 | + |
|
| 85 | 97 | wxWindow *m_parent; // Parent window only used to center us over it.
|
| 86 | 98 | HWND m_hwnd; // Task dialog handler
|
| 87 | 99 | long m_style; // wxProgressDialog style
|
| ... | ... | @@ -90,6 +102,7 @@ public: |
| 90 | 102 | wxString m_title;
|
| 91 | 103 | wxString m_message;
|
| 92 | 104 | wxString m_expandedInformation;
|
| 105 | + wxString m_labelSkip;
|
|
| 93 | 106 | wxString m_labelCancel; // Privately used by callback.
|
| 94 | 107 | unsigned long m_timeStop;
|
| 95 | 108 | wxIcon m_iconSmall;
|
| ... | ... | @@ -395,6 +408,8 @@ wxProgressDialog::wxProgressDialog( const wxString& title, |
| 395 | 408 | m_message(message),
|
| 396 | 409 | m_title(title)
|
| 397 | 410 | {
|
| 411 | + // Note that here we're testing whether we can use the native task dialog
|
|
| 412 | + // and not if we are already using it, as UsingNativeTaskDialog() does.
|
|
| 398 | 413 | if ( HasNativeTaskDialog() )
|
| 399 | 414 | {
|
| 400 | 415 | SetTopParent(parent);
|
| ... | ... | @@ -402,10 +417,29 @@ wxProgressDialog::wxProgressDialog( const wxString& title, |
| 402 | 417 | SetMaximum(maximum);
|
| 403 | 418 | |
| 404 | 419 | EnsureActiveEventLoopExists();
|
| 405 | - Show();
|
|
| 406 | - DisableOtherWindows();
|
|
| 420 | + if ( InitAndShowNative() )
|
|
| 421 | + {
|
|
| 422 | + DisableOtherWindows();
|
|
| 423 | + return;
|
|
| 424 | + }
|
|
| 407 | 425 | |
| 408 | - return;
|
|
| 426 | + // Showing native dialog failed, fall back to the generic one.
|
|
| 427 | + if ( m_taskDialogRunner )
|
|
| 428 | + {
|
|
| 429 | + // This points to the data owned by m_taskDialogRunner, so don't
|
|
| 430 | + // keep it dangling after deleting the thread.
|
|
| 431 | + //
|
|
| 432 | + // This is also used by UsingNativeTaskDialog().
|
|
| 433 | + m_sharedData = nullptr;
|
|
| 434 | + |
|
| 435 | + // It's critical to use wxTHREAD_WAIT_YIELD here to prevent
|
|
| 436 | + // deadlocks as the task dialog runner thread might be waiting for
|
|
| 437 | + // the main thread to process some messages.
|
|
| 438 | + m_taskDialogRunner->Wait(wxTHREAD_WAIT_YIELD);
|
|
| 439 | + |
|
| 440 | + delete m_taskDialogRunner;
|
|
| 441 | + m_taskDialogRunner = nullptr;
|
|
| 442 | + }
|
|
| 409 | 443 | }
|
| 410 | 444 | |
| 411 | 445 | Create(title, message, maximum, parent, style);
|
| ... | ... | @@ -469,7 +503,7 @@ wxProgressDialog::~wxProgressDialog() |
| 469 | 503 | |
| 470 | 504 | bool wxProgressDialog::Update(int value, const wxString& newmsg, bool *skip)
|
| 471 | 505 | {
|
| 472 | - if ( HasNativeTaskDialog() )
|
|
| 506 | + if ( UsingNativeTaskDialog() )
|
|
| 473 | 507 | {
|
| 474 | 508 | if ( !DoNativeBeforeUpdate(skip) )
|
| 475 | 509 | {
|
| ... | ... | @@ -542,7 +576,7 @@ bool wxProgressDialog::Update(int value, const wxString& newmsg, bool *skip) |
| 542 | 576 | |
| 543 | 577 | bool wxProgressDialog::Pulse(const wxString& newmsg, bool *skip)
|
| 544 | 578 | {
|
| 545 | - if ( HasNativeTaskDialog() )
|
|
| 579 | + if ( UsingNativeTaskDialog() )
|
|
| 546 | 580 | {
|
| 547 | 581 | if ( !DoNativeBeforeUpdate(skip) )
|
| 548 | 582 | {
|
| ... | ... | @@ -577,7 +611,7 @@ bool wxProgressDialog::Pulse(const wxString& newmsg, bool *skip) |
| 577 | 611 | |
| 578 | 612 | void wxProgressDialog::DispatchEvents()
|
| 579 | 613 | {
|
| 580 | - // No need for HasNativeTaskDialog() check, we're only called when this is
|
|
| 614 | + // No need for UsingNativeTaskDialog() check, we're only called when this is
|
|
| 581 | 615 | // the case.
|
| 582 | 616 | |
| 583 | 617 | // We don't need to dispatch the user input events as the task dialog
|
| ... | ... | @@ -613,7 +647,7 @@ void wxProgressDialog::Resume() |
| 613 | 647 | {
|
| 614 | 648 | wxGenericProgressDialog::Resume();
|
| 615 | 649 | |
| 616 | - if ( HasNativeTaskDialog() )
|
|
| 650 | + if ( UsingNativeTaskDialog() )
|
|
| 617 | 651 | {
|
| 618 | 652 | HWND hwnd;
|
| 619 | 653 | |
| ... | ... | @@ -648,7 +682,7 @@ void wxProgressDialog::Resume() |
| 648 | 682 | |
| 649 | 683 | WXWidget wxProgressDialog::GetHandle() const
|
| 650 | 684 | {
|
| 651 | - if ( HasNativeTaskDialog() )
|
|
| 685 | + if ( UsingNativeTaskDialog() )
|
|
| 652 | 686 | {
|
| 653 | 687 | wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
| 654 | 688 | return m_sharedData->m_hwnd;
|
| ... | ... | @@ -659,7 +693,7 @@ WXWidget wxProgressDialog::GetHandle() const |
| 659 | 693 | |
| 660 | 694 | int wxProgressDialog::GetValue() const
|
| 661 | 695 | {
|
| 662 | - if ( HasNativeTaskDialog() )
|
|
| 696 | + if ( UsingNativeTaskDialog() )
|
|
| 663 | 697 | {
|
| 664 | 698 | wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
| 665 | 699 | return m_sharedData->m_value;
|
| ... | ... | @@ -670,7 +704,7 @@ int wxProgressDialog::GetValue() const |
| 670 | 704 | |
| 671 | 705 | wxString wxProgressDialog::GetMessage() const
|
| 672 | 706 | {
|
| 673 | - if ( HasNativeTaskDialog() )
|
|
| 707 | + if ( UsingNativeTaskDialog() )
|
|
| 674 | 708 | return m_message;
|
| 675 | 709 | |
| 676 | 710 | return wxGenericProgressDialog::GetMessage();
|
| ... | ... | @@ -678,7 +712,7 @@ wxString wxProgressDialog::GetMessage() const |
| 678 | 712 | |
| 679 | 713 | void wxProgressDialog::SetRange(int maximum)
|
| 680 | 714 | {
|
| 681 | - if ( HasNativeTaskDialog() )
|
|
| 715 | + if ( UsingNativeTaskDialog() )
|
|
| 682 | 716 | {
|
| 683 | 717 | SetMaximum(maximum);
|
| 684 | 718 | |
| ... | ... | @@ -695,7 +729,7 @@ void wxProgressDialog::SetRange(int maximum) |
| 695 | 729 | |
| 696 | 730 | bool wxProgressDialog::WasSkipped() const
|
| 697 | 731 | {
|
| 698 | - if ( HasNativeTaskDialog() )
|
|
| 732 | + if ( UsingNativeTaskDialog() )
|
|
| 699 | 733 | {
|
| 700 | 734 | if ( !m_sharedData )
|
| 701 | 735 | {
|
| ... | ... | @@ -712,7 +746,7 @@ bool wxProgressDialog::WasSkipped() const |
| 712 | 746 | |
| 713 | 747 | bool wxProgressDialog::WasCancelled() const
|
| 714 | 748 | {
|
| 715 | - if ( HasNativeTaskDialog() )
|
|
| 749 | + if ( UsingNativeTaskDialog() )
|
|
| 716 | 750 | {
|
| 717 | 751 | wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
| 718 | 752 | return m_sharedData->m_state == Canceled;
|
| ... | ... | @@ -723,7 +757,7 @@ bool wxProgressDialog::WasCancelled() const |
| 723 | 757 | |
| 724 | 758 | void wxProgressDialog::SetTitle(const wxString& title)
|
| 725 | 759 | {
|
| 726 | - if ( HasNativeTaskDialog() )
|
|
| 760 | + if ( UsingNativeTaskDialog() )
|
|
| 727 | 761 | {
|
| 728 | 762 | m_title = title;
|
| 729 | 763 | |
| ... | ... | @@ -740,7 +774,7 @@ void wxProgressDialog::SetTitle(const wxString& title) |
| 740 | 774 | |
| 741 | 775 | wxString wxProgressDialog::GetTitle() const
|
| 742 | 776 | {
|
| 743 | - if ( HasNativeTaskDialog() )
|
|
| 777 | + if ( UsingNativeTaskDialog() )
|
|
| 744 | 778 | return m_title;
|
| 745 | 779 | |
| 746 | 780 | return wxGenericProgressDialog::GetTitle();
|
| ... | ... | @@ -748,7 +782,7 @@ wxString wxProgressDialog::GetTitle() const |
| 748 | 782 | |
| 749 | 783 | void wxProgressDialog::SetIcons(const wxIconBundle& icons)
|
| 750 | 784 | {
|
| 751 | - if ( HasNativeTaskDialog() )
|
|
| 785 | + if ( UsingNativeTaskDialog() )
|
|
| 752 | 786 | {
|
| 753 | 787 | m_icons = icons; // We can't just call to parent's SetIcons()
|
| 754 | 788 | // (wxGenericProgressDialog::SetIcons == wxTopLevelWindowMSW::SetIcons)
|
| ... | ... | @@ -780,7 +814,7 @@ void wxProgressDialog::SetIcons(const wxIconBundle& icons) |
| 780 | 814 | |
| 781 | 815 | void wxProgressDialog::DoMoveWindow(int x, int y, int width, int height)
|
| 782 | 816 | {
|
| 783 | - if ( HasNativeTaskDialog() )
|
|
| 817 | + if ( UsingNativeTaskDialog() )
|
|
| 784 | 818 | {
|
| 785 | 819 | if ( m_sharedData )
|
| 786 | 820 | {
|
| ... | ... | @@ -810,7 +844,7 @@ wxRect wxProgressDialog::GetTaskDialogRect() const |
| 810 | 844 | |
| 811 | 845 | void wxProgressDialog::DoGetPosition(int *x, int *y) const
|
| 812 | 846 | {
|
| 813 | - if ( HasNativeTaskDialog() )
|
|
| 847 | + if ( UsingNativeTaskDialog() )
|
|
| 814 | 848 | {
|
| 815 | 849 | const wxRect r = GetTaskDialogRect();
|
| 816 | 850 | if (x)
|
| ... | ... | @@ -826,7 +860,7 @@ void wxProgressDialog::DoGetPosition(int *x, int *y) const |
| 826 | 860 | |
| 827 | 861 | void wxProgressDialog::DoGetSize(int *width, int *height) const
|
| 828 | 862 | {
|
| 829 | - if ( HasNativeTaskDialog() )
|
|
| 863 | + if ( UsingNativeTaskDialog() )
|
|
| 830 | 864 | {
|
| 831 | 865 | const wxRect r = GetTaskDialogRect();
|
| 832 | 866 | if ( width )
|
| ... | ... | @@ -842,7 +876,7 @@ void wxProgressDialog::DoGetSize(int *width, int *height) const |
| 842 | 876 | |
| 843 | 877 | void wxProgressDialog::Fit()
|
| 844 | 878 | {
|
| 845 | - if ( HasNativeTaskDialog() )
|
|
| 879 | + if ( UsingNativeTaskDialog() )
|
|
| 846 | 880 | {
|
| 847 | 881 | wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
| 848 | 882 | |
| ... | ... | @@ -856,74 +890,90 @@ void wxProgressDialog::Fit() |
| 856 | 890 | wxGenericProgressDialog::Fit();
|
| 857 | 891 | }
|
| 858 | 892 | |
| 859 | -bool wxProgressDialog::Show(bool show)
|
|
| 893 | +bool wxProgressDialog::InitAndShowNative()
|
|
| 860 | 894 | {
|
| 861 | - if ( HasNativeTaskDialog() )
|
|
| 895 | + // We're showing the dialog for the first time, create the thread that
|
|
| 896 | + // will manage it.
|
|
| 897 | + m_taskDialogRunner = new wxProgressDialogTaskRunner;
|
|
| 898 | + m_sharedData = m_taskDialogRunner->GetSharedDataObject();
|
|
| 899 | + |
|
| 900 | + // Initialize shared data.
|
|
| 901 | + m_sharedData->m_title = m_title;
|
|
| 902 | + m_sharedData->m_message = m_message;
|
|
| 903 | + m_sharedData->m_range = m_maximum;
|
|
| 904 | + m_sharedData->m_state = Uncancelable;
|
|
| 905 | + m_sharedData->m_style = GetPDStyle();
|
|
| 906 | + m_sharedData->m_parent = GetTopParent();
|
|
| 907 | + |
|
| 908 | + if ( HasPDFlag(wxPD_CAN_ABORT) )
|
|
| 862 | 909 | {
|
| 863 | - // The dialog can't be hidden at all and showing it again after it had
|
|
| 864 | - // been shown before doesn't do anything.
|
|
| 865 | - if ( !show || m_taskDialogRunner )
|
|
| 866 | - return false;
|
|
| 910 | + m_sharedData->m_state = Continue;
|
|
| 911 | + m_sharedData->m_labelCancel = _("Cancel");
|
|
| 912 | + }
|
|
| 913 | + else // Dialog can't be cancelled.
|
|
| 914 | + {
|
|
| 915 | + // We still must have at least a single button in the dialog so
|
|
| 916 | + // just don't call it "Cancel" in this case.
|
|
| 917 | + m_sharedData->m_labelCancel = _("Close");
|
|
| 918 | + }
|
|
| 867 | 919 | |
| 868 | - // We're showing the dialog for the first time, create the thread that
|
|
| 869 | - // will manage it.
|
|
| 870 | - m_taskDialogRunner = new wxProgressDialogTaskRunner;
|
|
| 871 | - m_sharedData = m_taskDialogRunner->GetSharedDataObject();
|
|
| 920 | + if ( HasPDFlag(wxPD_ELAPSED_TIME |
|
|
| 921 | + wxPD_ESTIMATED_TIME |
|
|
| 922 | + wxPD_REMAINING_TIME) )
|
|
| 923 | + {
|
|
| 924 | + // Set the expanded information field from the beginning to avoid
|
|
| 925 | + // having to re-layout the dialog later when it changes.
|
|
| 926 | + UpdateExpandedInformation(0);
|
|
| 927 | + }
|
|
| 872 | 928 | |
| 873 | - // Initialize shared data.
|
|
| 874 | - m_sharedData->m_title = m_title;
|
|
| 875 | - m_sharedData->m_message = m_message;
|
|
| 876 | - m_sharedData->m_range = m_maximum;
|
|
| 877 | - m_sharedData->m_state = Uncancelable;
|
|
| 878 | - m_sharedData->m_style = GetPDStyle();
|
|
| 879 | - m_sharedData->m_parent = GetTopParent();
|
|
| 929 | + // Do launch the thread.
|
|
| 930 | + if ( m_taskDialogRunner->Run() != wxTHREAD_NO_ERROR )
|
|
| 931 | + {
|
|
| 932 | + // This should never happen and it's useless to have a user-visible
|
|
| 933 | + // message (that would need to be translated) for it.
|
|
| 934 | + wxLogDebug("Unexpectedly failed to launch the task dialog thread");
|
|
| 935 | + return false;
|
|
| 936 | + }
|
|
| 880 | 937 | |
| 881 | - if ( HasPDFlag(wxPD_CAN_ABORT) )
|
|
| 882 | - {
|
|
| 883 | - m_sharedData->m_state = Continue;
|
|
| 884 | - m_sharedData->m_labelCancel = _("Cancel");
|
|
| 885 | - }
|
|
| 886 | - else // Dialog can't be cancelled.
|
|
| 887 | - {
|
|
| 888 | - // We still must have at least a single button in the dialog so
|
|
| 889 | - // just don't call it "Cancel" in this case.
|
|
| 890 | - m_sharedData->m_labelCancel = _("Close");
|
|
| 891 | - }
|
|
| 938 | + // Wait until the dialog is shown as the program may need some time
|
|
| 939 | + // before it calls Update() and we want to show something to the user
|
|
| 940 | + // in the meanwhile.
|
|
| 941 | + auto status = wxProgressDialogStatus::Failed;
|
|
| 942 | + while ( wxEventLoop::GetActive()->Dispatch() )
|
|
| 943 | + {
|
|
| 944 | + wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
|
| 945 | + status = m_sharedData->m_status;
|
|
| 946 | + if ( status != wxProgressDialogStatus::Initializing )
|
|
| 947 | + break;
|
|
| 948 | + }
|
|
| 892 | 949 | |
| 893 | - if ( HasPDFlag(wxPD_ELAPSED_TIME |
|
|
| 894 | - wxPD_ESTIMATED_TIME |
|
|
| 895 | - wxPD_REMAINING_TIME) )
|
|
| 896 | - {
|
|
| 897 | - // Set the expanded information field from the beginning to avoid
|
|
| 898 | - // having to re-layout the dialog later when it changes.
|
|
| 899 | - UpdateExpandedInformation(0);
|
|
| 900 | - }
|
|
| 950 | + switch ( m_sharedData->m_status )
|
|
| 951 | + {
|
|
| 952 | + case wxProgressDialogStatus::Initialized:
|
|
| 953 | + break;
|
|
| 901 | 954 | |
| 902 | - // Do launch the thread.
|
|
| 903 | - if ( m_taskDialogRunner->Create() != wxTHREAD_NO_ERROR )
|
|
| 904 | - {
|
|
| 905 | - wxLogError( "Unable to create thread!" );
|
|
| 906 | - return false;
|
|
| 907 | - }
|
|
| 955 | + case wxProgressDialogStatus::Initializing:
|
|
| 956 | + wxFAIL_MSG("Unreachable");
|
|
| 957 | + wxFALLTHROUGH;
|
|
| 908 | 958 | |
| 909 | - if ( m_taskDialogRunner->Run() != wxTHREAD_NO_ERROR )
|
|
| 910 | - {
|
|
| 911 | - wxLogError( "Unable to start thread!" );
|
|
| 959 | + case wxProgressDialogStatus::Failed:
|
|
| 912 | 960 | return false;
|
| 913 | - }
|
|
| 961 | + }
|
|
| 914 | 962 | |
| 915 | - // Wait until the dialog is shown as the program may need some time
|
|
| 916 | - // before it calls Update() and we want to show something to the user
|
|
| 917 | - // in the meanwhile.
|
|
| 918 | - while ( wxEventLoop::GetActive()->Dispatch() )
|
|
| 919 | - {
|
|
| 920 | - wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
|
| 921 | - if ( m_sharedData->m_hwnd )
|
|
| 922 | - break;
|
|
| 923 | - }
|
|
| 963 | + return true;
|
|
| 964 | +}
|
|
| 924 | 965 | |
| 925 | - // Do not show the underlying dialog.
|
|
| 926 | - return false;
|
|
| 966 | +bool wxProgressDialog::Show(bool show)
|
|
| 967 | +{
|
|
| 968 | + if ( UsingNativeTaskDialog() )
|
|
| 969 | + {
|
|
| 970 | + // The dialog can't be hidden at all and showing it again after it had
|
|
| 971 | + // been shown before doesn't do anything.
|
|
| 972 | + if ( !show || m_taskDialogRunner )
|
|
| 973 | + return false;
|
|
| 974 | + |
|
| 975 | + // We don't need to do anything to show it.
|
|
| 976 | + return true;
|
|
| 927 | 977 | }
|
| 928 | 978 | |
| 929 | 979 | return wxGenericProgressDialog::Show( show );
|
| ... | ... | @@ -1022,7 +1072,11 @@ void* wxProgressDialogTaskRunner::Entry() |
| 1022 | 1072 | tdc.lpCallbackData = (LONG_PTR) &m_sharedData;
|
| 1023 | 1073 | |
| 1024 | 1074 | if ( m_sharedData.m_style & wxPD_CAN_SKIP )
|
| 1025 | - wxTdc.AddTaskDialogButton( tdc, Id_SkipBtn, 0, _("Skip") );
|
|
| 1075 | + {
|
|
| 1076 | + m_sharedData.m_labelSkip = _("Skip");
|
|
| 1077 | + wxTdc.AddTaskDialogButton( tdc, Id_SkipBtn, 0,
|
|
| 1078 | + m_sharedData.m_labelSkip );
|
|
| 1079 | + }
|
|
| 1026 | 1080 | |
| 1027 | 1081 | tdc.dwFlags |= TDF_CALLBACK_TIMER | TDF_SHOW_PROGRESS_BAR;
|
| 1028 | 1082 | |
| ... | ... | @@ -1040,15 +1094,26 @@ void* wxProgressDialogTaskRunner::Entry() |
| 1040 | 1094 | }
|
| 1041 | 1095 | }
|
| 1042 | 1096 | |
| 1097 | + HRESULT hr;
|
|
| 1043 | 1098 | TaskDialogIndirect_t taskDialogIndirect = GetTaskDialogIndirectFunc();
|
| 1044 | - if ( !taskDialogIndirect )
|
|
| 1045 | - return nullptr;
|
|
| 1099 | + if ( taskDialogIndirect )
|
|
| 1100 | + {
|
|
| 1101 | + int msAns;
|
|
| 1102 | + hr = taskDialogIndirect(&tdc, &msAns, nullptr, nullptr);
|
|
| 1103 | + }
|
|
| 1104 | + else
|
|
| 1105 | + {
|
|
| 1106 | + hr = E_UNEXPECTED;
|
|
| 1107 | + }
|
|
| 1046 | 1108 | |
| 1047 | - int msAns;
|
|
| 1048 | - HRESULT hr = taskDialogIndirect(&tdc, &msAns, nullptr, nullptr);
|
|
| 1049 | 1109 | if ( FAILED(hr) )
|
| 1110 | + {
|
|
| 1050 | 1111 | wxLogApiError( "TaskDialogIndirect", hr );
|
| 1051 | 1112 | |
| 1113 | + wxCriticalSectionLocker locker(m_sharedData.m_cs);
|
|
| 1114 | + m_sharedData.m_status = wxProgressDialogStatus::Failed;
|
|
| 1115 | + }
|
|
| 1116 | + |
|
| 1052 | 1117 | // If the main thread is waiting for us to exit inside the event loop in
|
| 1053 | 1118 | // Update(), wake it up so that it checks our status again.
|
| 1054 | 1119 | wxWakeUpIdle();
|
| ... | ... | @@ -1079,6 +1144,9 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc |
| 1079 | 1144 | switch ( uNotification )
|
| 1080 | 1145 | {
|
| 1081 | 1146 | case TDN_CREATED:
|
| 1147 | + // Let the main thread know that the dialog can be used.
|
|
| 1148 | + sharedData->m_status = wxProgressDialogStatus::Initialized;
|
|
| 1149 | + |
|
| 1082 | 1150 | // Store the HWND for the main thread use.
|
| 1083 | 1151 | sharedData->m_hwnd = hwnd;
|
| 1084 | 1152 |
| ... | ... | @@ -1715,35 +1715,6 @@ wxCreateHiddenWindow(LPCTSTR *pclassname, LPCTSTR classname, WNDPROC wndproc) |
| 1715 | 1715 | return hwnd;
|
| 1716 | 1716 | }
|
| 1717 | 1717 | |
| 1718 | -bool wxMoveToTrash(const wxString& path)
|
|
| 1719 | -{
|
|
| 1720 | - // SHFileOperation needs double null termination string
|
|
| 1721 | - // but without separator at the end of the path
|
|
| 1722 | - wxString pathStr(path);
|
|
| 1723 | - if ( pathStr.Last() == wxFILE_SEP_PATH )
|
|
| 1724 | - pathStr.RemoveLast();
|
|
| 1725 | - pathStr += wxT('\0');
|
|
| 1726 | - |
|
| 1727 | - SHFILEOPSTRUCT fileop;
|
|
| 1728 | - wxZeroMemory(fileop);
|
|
| 1729 | - fileop.wFunc = FO_DELETE;
|
|
| 1730 | - fileop.pFrom = pathStr.t_str();
|
|
| 1731 | - fileop.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
|
|
| 1732 | - |
|
| 1733 | - const int ret = SHFileOperation(&fileop);
|
|
| 1734 | - if ( ret != 0 || fileop.fAnyOperationsAborted )
|
|
| 1735 | - {
|
|
| 1736 | - // Note that the return value from SHFileOperation() is not a standard
|
|
| 1737 | - // Win32 error code, so we can't use wxLogSysError() here.
|
|
| 1738 | - wxLogError(_("'%s' couldn't be moved to trash: error 0x%08x"),
|
|
| 1739 | - path,
|
|
| 1740 | - ret);
|
|
| 1741 | - return false;
|
|
| 1742 | - }
|
|
| 1743 | - |
|
| 1744 | - return true;
|
|
| 1745 | -}
|
|
| 1746 | - |
|
| 1747 | 1718 | int wxCMPFUNC_CONV wxCmpNatural(const wxString& s1, const wxString& s2)
|
| 1748 | 1719 | {
|
| 1749 | 1720 | return StrCmpLogicalW(s1.wc_str(), s2.wc_str());
|
| ... | ... | @@ -14,6 +14,7 @@ |
| 14 | 14 | #include "wx/utils.h"
|
| 15 | 15 | #endif //WX_PRECOMP
|
| 16 | 16 | |
| 17 | +#include "wx/filefn.h"
|
|
| 17 | 18 | #include "wx/private/launchbrowser.h"
|
| 18 | 19 | #include "wx/msw/private.h" // includes <windows.h>
|
| 19 | 20 | #include "wx/msw/private/dpiaware.h"
|
| ... | ... | @@ -178,3 +179,32 @@ bool wxMSWIsOnSecureScreen() |
| 178 | 179 | // level.
|
| 179 | 180 | return wcscmp(name, L"Winlogon") == 0;
|
| 180 | 181 | }
|
| 182 | + |
|
| 183 | +bool wxMoveToTrash(const wxString& path)
|
|
| 184 | +{
|
|
| 185 | + // SHFileOperation needs double null termination string
|
|
| 186 | + // but without separator at the end of the path
|
|
| 187 | + wxString pathStr(path);
|
|
| 188 | + if ( pathStr.Last() == wxFILE_SEP_PATH )
|
|
| 189 | + pathStr.RemoveLast();
|
|
| 190 | + pathStr += wxT('\0');
|
|
| 191 | + |
|
| 192 | + SHFILEOPSTRUCT fileop;
|
|
| 193 | + wxZeroMemory(fileop);
|
|
| 194 | + fileop.wFunc = FO_DELETE;
|
|
| 195 | + fileop.pFrom = pathStr.t_str();
|
|
| 196 | + fileop.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
|
|
| 197 | + |
|
| 198 | + const int ret = SHFileOperation(&fileop);
|
|
| 199 | + if ( ret != 0 || fileop.fAnyOperationsAborted )
|
|
| 200 | + {
|
|
| 201 | + // Note that the return value from SHFileOperation() is not a standard
|
|
| 202 | + // Win32 error code, so we can't use wxLogSysError() here.
|
|
| 203 | + wxLogError(_("'%s' couldn't be moved to trash: error 0x%08x"),
|
|
| 204 | + path,
|
|
| 205 | + ret);
|
|
| 206 | + return false;
|
|
| 207 | + }
|
|
| 208 | + |
|
| 209 | + return true;
|
|
| 210 | +} |
| ... | ... | @@ -12,6 +12,8 @@ |
| 12 | 12 | #ifndef WX_PRECOMP
|
| 13 | 13 | #include "wx/object.h"
|
| 14 | 14 | #include "wx/math.h"
|
| 15 | +#include "wx/intl.h"
|
|
| 16 | +#include "wx/log.h"
|
|
| 15 | 17 | #endif
|
| 16 | 18 | |
| 17 | 19 | #if wxOSX_USE_COCOA_OR_CARBON
|
| ... | ... | @@ -694,4 +696,31 @@ wxOSXEffectiveAppearanceSetter::~wxOSXEffectiveAppearanceSetter() |
| 694 | 696 | #endif
|
| 695 | 697 | }
|
| 696 | 698 | |
| 699 | +#ifdef __WXDARWIN_OSX__
|
|
| 700 | + |
|
| 701 | +// Move file or directory to macOS Trash using NSFileManager.
|
|
| 702 | +bool wxMoveToTrash(const wxString& path)
|
|
| 703 | +{
|
|
| 704 | + wxCFStringRef cfPath(path);
|
|
| 705 | + NSURL *fileURL = [NSURL fileURLWithPath:cfPath.AsNSString()];
|
|
| 706 | + if ( fileURL == nil )
|
|
| 707 | + return false;
|
|
| 708 | + |
|
| 709 | + NSError *error = nil;
|
|
| 710 | + BOOL ok = [[NSFileManager defaultManager] trashItemAtURL:fileURL
|
|
| 711 | + resultingItemURL:nil
|
|
| 712 | + error:&error];
|
|
| 713 | + if ( !ok )
|
|
| 714 | + {
|
|
| 715 | + wxLogError(_("'%s' couldn't be moved to trash: %s"),
|
|
| 716 | + path,
|
|
| 717 | + error ? wxCFStringRef::AsString([error localizedDescription])
|
|
| 718 | + : _("unknown error"));
|
|
| 719 | + }
|
|
| 720 | + |
|
| 721 | + return ok;
|
|
| 722 | +}
|
|
| 723 | + |
|
| 724 | +#endif // __WXDARWIN_OSX__
|
|
| 725 | + |
|
| 697 | 726 | #endif |
| ... | ... | @@ -388,27 +388,4 @@ bool wxMacInitCocoa() |
| 388 | 388 | return cocoaLoaded;
|
| 389 | 389 | }
|
| 390 | 390 | |
| 391 | -// Move file or directory to macOS Trash using NSFileManager.
|
|
| 392 | -bool wxMoveToTrash(const wxString& path)
|
|
| 393 | -{
|
|
| 394 | - wxCFStringRef cfPath(path);
|
|
| 395 | - NSURL *fileURL = [NSURL fileURLWithPath:cfPath.AsNSString()];
|
|
| 396 | - if ( fileURL == nil )
|
|
| 397 | - return false;
|
|
| 398 | - |
|
| 399 | - NSError *error = nil;
|
|
| 400 | - BOOL ok = [[NSFileManager defaultManager] trashItemAtURL:fileURL
|
|
| 401 | - resultingItemURL:nil
|
|
| 402 | - error:&error];
|
|
| 403 | - if ( !ok )
|
|
| 404 | - {
|
|
| 405 | - wxLogError(_("'%s' couldn't be moved to trash: %s"),
|
|
| 406 | - path,
|
|
| 407 | - error ? wxCFStringRef::AsString([error localizedDescription])
|
|
| 408 | - : _("unknown error"));
|
|
| 409 | - }
|
|
| 410 | - |
|
| 411 | - return ok;
|
|
| 412 | -}
|
|
| 413 | - |
|
| 414 | 391 | #endif // __WXDARWIN_OSX__ |
—
View it on GitLab.
You're receiving this email because of your account on gitlab.com. Manage all notifications · Help