From 267c6f2ac71f92999e969232431ba04678e7437e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 07:54:39 +0200 Subject: Adding upstream version 4:24.2.0. Signed-off-by: Daniel Baumann --- cui/source/dialogs/AdditionsDialog.cxx | 868 ++++++++++ cui/source/dialogs/DiagramDialog.cxx | 155 ++ cui/source/dialogs/FontFeaturesDialog.cxx | 274 +++ cui/source/dialogs/GraphicTestsDialog.cxx | 115 ++ cui/source/dialogs/ImageViewerDialog.cxx | 24 + cui/source/dialogs/QrCodeGenDialog.cxx | 433 +++++ cui/source/dialogs/SignSignatureLineDialog.cxx | 258 +++ cui/source/dialogs/SignatureLineDialog.cxx | 194 +++ cui/source/dialogs/SignatureLineDialogBase.cxx | 40 + cui/source/dialogs/SpellAttrib.hxx | 118 ++ cui/source/dialogs/SpellDialog.cxx | 2179 ++++++++++++++++++++++++ cui/source/dialogs/about.cxx | 273 +++ cui/source/dialogs/colorpicker.cxx | 1361 +++++++++++++++ cui/source/dialogs/cuicharmap.cxx | 890 ++++++++++ cui/source/dialogs/cuifmsearch.cxx | 764 +++++++++ cui/source/dialogs/cuigaldlg.cxx | 1009 +++++++++++ cui/source/dialogs/cuigrfflt.cxx | 469 +++++ cui/source/dialogs/cuihyperdlg.cxx | 307 ++++ cui/source/dialogs/cuiimapwnd.cxx | 57 + cui/source/dialogs/cuitbxform.cxx | 31 + cui/source/dialogs/dlgname.cxx | 284 +++ cui/source/dialogs/fileextcheckdlg.cxx | 55 + cui/source/dialogs/hangulhanjadlg.cxx | 1508 ++++++++++++++++ cui/source/dialogs/hldocntp.cxx | 449 +++++ cui/source/dialogs/hldoctp.cxx | 317 ++++ cui/source/dialogs/hlinettp.cxx | 255 +++ cui/source/dialogs/hlmailtp.cxx | 220 +++ cui/source/dialogs/hlmarkwn.cxx | 532 ++++++ cui/source/dialogs/hltpbase.cxx | 571 +++++++ cui/source/dialogs/hyphen.cxx | 474 ++++++ cui/source/dialogs/iconcdlg.cxx | 313 ++++ cui/source/dialogs/insdlg.cxx | 633 +++++++ cui/source/dialogs/insrc.cxx | 59 + cui/source/dialogs/linkdlg.cxx | 644 +++++++ cui/source/dialogs/multipat.cxx | 316 ++++ cui/source/dialogs/newtabledlg.cxx | 39 + cui/source/dialogs/passwdomdlg.cxx | 273 +++ cui/source/dialogs/pastedlg.cxx | 339 ++++ cui/source/dialogs/postdlg.cxx | 162 ++ cui/source/dialogs/screenshotannotationdlg.cxx | 578 +++++++ cui/source/dialogs/scriptdlg.cxx | 1330 +++++++++++++++ cui/source/dialogs/sdrcelldlg.cxx | 106 ++ cui/source/dialogs/showcols.cxx | 109 ++ cui/source/dialogs/signature-line-draw.svg | 36 + cui/source/dialogs/signature-line.svg | 30 + cui/source/dialogs/splitcelldlg.cxx | 114 ++ cui/source/dialogs/srchxtra.cxx | 232 +++ cui/source/dialogs/thesdlg.cxx | 352 ++++ cui/source/dialogs/tipofthedaydlg.cxx | 266 +++ cui/source/dialogs/toolbarmodedlg.cxx | 210 +++ cui/source/dialogs/widgettestdlg.cxx | 57 + cui/source/dialogs/zoom.cxx | 397 +++++ 52 files changed, 21079 insertions(+) create mode 100644 cui/source/dialogs/AdditionsDialog.cxx create mode 100644 cui/source/dialogs/DiagramDialog.cxx create mode 100644 cui/source/dialogs/FontFeaturesDialog.cxx create mode 100644 cui/source/dialogs/GraphicTestsDialog.cxx create mode 100644 cui/source/dialogs/ImageViewerDialog.cxx create mode 100644 cui/source/dialogs/QrCodeGenDialog.cxx create mode 100644 cui/source/dialogs/SignSignatureLineDialog.cxx create mode 100644 cui/source/dialogs/SignatureLineDialog.cxx create mode 100644 cui/source/dialogs/SignatureLineDialogBase.cxx create mode 100644 cui/source/dialogs/SpellAttrib.hxx create mode 100644 cui/source/dialogs/SpellDialog.cxx create mode 100644 cui/source/dialogs/about.cxx create mode 100644 cui/source/dialogs/colorpicker.cxx create mode 100644 cui/source/dialogs/cuicharmap.cxx create mode 100644 cui/source/dialogs/cuifmsearch.cxx create mode 100644 cui/source/dialogs/cuigaldlg.cxx create mode 100644 cui/source/dialogs/cuigrfflt.cxx create mode 100644 cui/source/dialogs/cuihyperdlg.cxx create mode 100644 cui/source/dialogs/cuiimapwnd.cxx create mode 100644 cui/source/dialogs/cuitbxform.cxx create mode 100644 cui/source/dialogs/dlgname.cxx create mode 100644 cui/source/dialogs/fileextcheckdlg.cxx create mode 100644 cui/source/dialogs/hangulhanjadlg.cxx create mode 100644 cui/source/dialogs/hldocntp.cxx create mode 100644 cui/source/dialogs/hldoctp.cxx create mode 100644 cui/source/dialogs/hlinettp.cxx create mode 100644 cui/source/dialogs/hlmailtp.cxx create mode 100644 cui/source/dialogs/hlmarkwn.cxx create mode 100644 cui/source/dialogs/hltpbase.cxx create mode 100644 cui/source/dialogs/hyphen.cxx create mode 100644 cui/source/dialogs/iconcdlg.cxx create mode 100644 cui/source/dialogs/insdlg.cxx create mode 100644 cui/source/dialogs/insrc.cxx create mode 100644 cui/source/dialogs/linkdlg.cxx create mode 100644 cui/source/dialogs/multipat.cxx create mode 100644 cui/source/dialogs/newtabledlg.cxx create mode 100644 cui/source/dialogs/passwdomdlg.cxx create mode 100644 cui/source/dialogs/pastedlg.cxx create mode 100644 cui/source/dialogs/postdlg.cxx create mode 100644 cui/source/dialogs/screenshotannotationdlg.cxx create mode 100644 cui/source/dialogs/scriptdlg.cxx create mode 100644 cui/source/dialogs/sdrcelldlg.cxx create mode 100644 cui/source/dialogs/showcols.cxx create mode 100644 cui/source/dialogs/signature-line-draw.svg create mode 100644 cui/source/dialogs/signature-line.svg create mode 100644 cui/source/dialogs/splitcelldlg.cxx create mode 100644 cui/source/dialogs/srchxtra.cxx create mode 100644 cui/source/dialogs/thesdlg.cxx create mode 100644 cui/source/dialogs/tipofthedaydlg.cxx create mode 100644 cui/source/dialogs/toolbarmodedlg.cxx create mode 100644 cui/source/dialogs/widgettestdlg.cxx create mode 100644 cui/source/dialogs/zoom.cxx (limited to 'cui/source/dialogs') diff --git a/cui/source/dialogs/AdditionsDialog.cxx b/cui/source/dialogs/AdditionsDialog.cxx new file mode 100644 index 0000000000..f0dedf626a --- /dev/null +++ b/cui/source/dialogs/AdditionsDialog.cxx @@ -0,0 +1,868 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#define PAGE_SIZE 30 + +using namespace css; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Sequence; + +using namespace com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; + +namespace +{ +// Gets the content of the given URL and returns as a standard string +std::string ucbGet(const OUString& rURL) +{ + try + { + auto const s = utl::UcbStreamHelper::CreateStream(rURL, StreamMode::STD_READ); + if (!s) + { + SAL_WARN("cui.dialogs", "CreateStream <" << rURL << "> failed"); + return {}; + } + std::string response_body; + do + { + char buf[4096]; + auto const n = s->ReadBytes(buf, sizeof buf); + response_body.append(buf, n); + } while (s->good()); + if (s->bad()) + { + SAL_WARN("cui.dialogs", "Reading <" << rURL << "> failed with " << s->GetError()); + return {}; + } + return response_body; + } + catch (css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "Download failed"); + return {}; + } +} + +// Downloads and saves the file at the given rURL to a local path (sFolderURL/fileName) +void ucbDownload(const OUString& rURL, const OUString& sFolderURL, const OUString& fileName) +{ + try + { + ucbhelper::Content(sFolderURL, {}, comphelper::getProcessComponentContext()) + .transferContent(ucbhelper::Content(rURL, {}, comphelper::getProcessComponentContext()), + ucbhelper::InsertOperation::Copy, fileName, + css::ucb::NameClash::OVERWRITE); + } + catch (css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "Download failed"); + } +} + +void parseResponse(const std::string& rResponse, std::vector& aAdditions) +{ + orcus::json::document_tree aJsonDoc; + orcus::json_config aConfig; + + if (rResponse.empty()) + return; + + try + { + aJsonDoc.load(rResponse, aConfig); + } + catch (const orcus::parse_error&) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "Invalid JSON file from the extensions API"); + return; + } + + auto aDocumentRoot = aJsonDoc.get_document_root(); + if (aDocumentRoot.type() != orcus::json::node_t::object) + { + SAL_WARN("cui.dialogs", "invalid root entries: " << rResponse); + return; + } + + auto resultsArray = aDocumentRoot.child("extension"); + + for (size_t i = 0; i < resultsArray.child_count(); ++i) + { + auto arrayElement = resultsArray.child(i); + + try + { + AdditionInfo aNewAddition = { + OStringToOUString(arrayElement.child("id").string_value(), RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("name").string_value(), RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("author").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("url").string_value(), RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("screenshotURL").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("extensionIntroduction").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("extensionDescription").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString( + arrayElement.child("releases").child(0).child("compatibility").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString( + arrayElement.child("releases").child(0).child("releaseName").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString( + arrayElement.child("releases").child(0).child("license").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("commentNumber").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("commentURL").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("rating").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString(arrayElement.child("downloadNumber").string_value(), + RTL_TEXTENCODING_UTF8), + OStringToOUString( + arrayElement.child("releases").child(0).child("downloadURL").string_value(), + RTL_TEXTENCODING_UTF8) + }; + + aAdditions.push_back(aNewAddition); + } + catch (orcus::json::document_error& e) + { + // This usually happens when one of the values is null (type() == orcus::json::node_t::null) + // TODO: Allow null values in additions. + SAL_WARN("cui.dialogs", "Additions JSON parse error: " << e.what()); + } + } +} + +bool getPreviewFile(const AdditionInfo& aAdditionInfo, OUString& sPreviewFile) +{ + uno::Reference xFileAccess + = ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext()); + + // copy the images to the user's additions folder + OUString userFolder = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER + "/" SAL_CONFIGFILE("bootstrap") "::UserInstallation}"; + rtl::Bootstrap::expandMacros(userFolder); + userFolder += "/user/additions/" + aAdditionInfo.sExtensionID + "/"; + + OUString aPreviewFile(INetURLObject(aAdditionInfo.sScreenshotURL).getName()); + OUString aPreviewURL = aAdditionInfo.sScreenshotURL; + + try + { + osl::Directory::createPath(userFolder); + + if (!xFileAccess->exists(userFolder + aPreviewFile)) + ucbDownload(aPreviewURL, userFolder, aPreviewFile); + } + catch (const uno::Exception&) + { + return false; + } + sPreviewFile = userFolder + aPreviewFile; + return true; +} + +void LoadImage(std::u16string_view rPreviewFile, std::shared_ptr pCurrentItem) +{ + const sal_Int8 Margin = 6; + + SolarMutexGuard aGuard; + + GraphicFilter aFilter; + Graphic aGraphic; + + INetURLObject aURLObj(rPreviewFile); + + // for VCL to be able to create bitmaps / do visual changes in the thread + aFilter.ImportGraphic(aGraphic, aURLObj); + BitmapEx aBmp = aGraphic.GetBitmapEx(); + Size aBmpSize = aBmp.GetSizePixel(); + Size aThumbSize(pCurrentItem->m_xImageScreenshot->get_size_request()); + if (!aBmp.IsEmpty()) + { + double aScale; + if (aBmpSize.Width() > aThumbSize.Width() - 2 * Margin) + { + aScale = static_cast(aBmpSize.Width()) / (aThumbSize.Width() - 2 * Margin); + aBmp.Scale(Size(aBmpSize.Width() / aScale, aBmpSize.Height() / aScale)); + } + else if (aBmpSize.Height() > aThumbSize.Height() - 2 * Margin) + { + aScale = static_cast(aBmpSize.Height()) / (aThumbSize.Height() - 2 * Margin); + aBmp.Scale(Size(aBmpSize.Width() / aScale, aBmpSize.Height() / aScale)); + }; + aBmpSize = aBmp.GetSizePixel(); + } + + ScopedVclPtr xVirDev = pCurrentItem->m_xImageScreenshot->create_virtual_device(); + xVirDev->SetOutputSizePixel(aThumbSize); + //white background since images come with a white border + xVirDev->SetBackground(Wallpaper(COL_WHITE)); + xVirDev->Erase(); + xVirDev->DrawBitmapEx(Point(aThumbSize.Width() / 2 - aBmpSize.Width() / 2, Margin), aBmp); + pCurrentItem->m_xImageScreenshot->set_image(xVirDev.get()); + xVirDev.disposeAndClear(); +} + +} // End of the anonymous namespace + +SearchAndParseThread::SearchAndParseThread(AdditionsDialog* pDialog, const bool isFirstLoading) + : Thread("cuiAdditionsSearchThread") + , m_pAdditionsDialog(pDialog) + , m_bExecute(true) + , m_bIsFirstLoading(isFirstLoading) +{ + // if we are running a UITest, e.g. UITest_sw_options then + // don't attempt to downloading anything + static const bool bUITest = getenv("LIBO_TEST_UNIT"); + + m_bUITest = bUITest; +} + +SearchAndParseThread::~SearchAndParseThread() {} + +void SearchAndParseThread::Append(AdditionInfo& additionInfo) +{ + if (!m_bExecute) + return; + OUString aPreviewFile; + bool bResult + = !m_bUITest && getPreviewFile(additionInfo, aPreviewFile); // info vector json data + + if (!bResult) + { + SAL_INFO("cui.dialogs", "Couldn't get the preview file. Skipping: " << aPreviewFile); + return; + } + + SolarMutexGuard aGuard; + + auto newItem = std::make_shared(m_pAdditionsDialog->m_xContentGrid.get(), + m_pAdditionsDialog, additionInfo); + m_pAdditionsDialog->m_aAdditionsItems.push_back(newItem); + std::shared_ptr aCurrentItem = m_pAdditionsDialog->m_aAdditionsItems.back(); + + LoadImage(aPreviewFile, aCurrentItem); + m_pAdditionsDialog->m_nCurrentListItemCount++; + + if (m_pAdditionsDialog->m_nCurrentListItemCount == m_pAdditionsDialog->m_nMaxItemCount) + { + if (m_pAdditionsDialog->m_nCurrentListItemCount + != m_pAdditionsDialog->m_aAllExtensionsVector.size()) + aCurrentItem->m_xButtonShowMore->set_visible(true); + } +} + +void SearchAndParseThread::Search() +{ + m_pAdditionsDialog->m_searchOptions.searchString + = m_pAdditionsDialog->m_xEntrySearch->get_text(); + utl::TextSearch textSearch(m_pAdditionsDialog->m_searchOptions); + + size_t nIteration = 0; + for (auto& rInfo : m_pAdditionsDialog->m_aAllExtensionsVector) + { + if (m_pAdditionsDialog->m_nCurrentListItemCount == m_pAdditionsDialog->m_nMaxItemCount) + break; + + OUString sExtensionName = rInfo.sName; + OUString sExtensionDescription = rInfo.sDescription; + + if (!m_pAdditionsDialog->m_xEntrySearch->get_text().isEmpty() + && !textSearch.searchForward(sExtensionName) + && !textSearch.searchForward(sExtensionDescription)) + { + continue; + } + else + { + if (nIteration >= m_pAdditionsDialog->m_nCurrentListItemCount) + Append(rInfo); + nIteration++; + } + } + CheckInstalledExtensions(); +} + +void SearchAndParseThread::CheckInstalledExtensions() +{ + const uno::Sequence>> xAllPackages + = m_pAdditionsDialog->getInstalledExtensions(); + + if (!xAllPackages.hasElements()) + return; + + OUString currentExtensionName; + + for (auto& package : xAllPackages) + { + for (auto& extensionVersion : package) + { + if (extensionVersion.is()) + { + currentExtensionName = extensionVersion->getName(); + if (currentExtensionName.isEmpty()) + continue; + + m_pAdditionsDialog->m_searchOptions.searchString = currentExtensionName; + utl::TextSearch textSearch(m_pAdditionsDialog->m_searchOptions); + + for (auto& rInfo : m_pAdditionsDialog->m_aAdditionsItems) + { + OUString sExtensionDownloadURL = rInfo->m_sDownloadURL; + + if (!textSearch.searchForward(sExtensionDownloadURL)) + { + continue; + } + else + { + SolarMutexGuard aGuard; + rInfo->m_xButtonInstall->set_sensitive(false); + rInfo->m_xButtonInstall->set_label( + CuiResId(RID_CUISTR_ADDITIONS_INSTALLEDBUTTON)); + } + } + } + } + } +} + +void SearchAndParseThread::execute() +{ + OUString sProgress; + if (m_bIsFirstLoading) + sProgress = CuiResId(RID_CUISTR_ADDITIONS_LOADING); + else + sProgress = CuiResId(RID_CUISTR_ADDITIONS_SEARCHING); + + m_pAdditionsDialog->SetProgress( + sProgress); // Loading or searching according to being first call or not + + if (m_bIsFirstLoading) + { + std::string sResponse = !m_bUITest ? ucbGet(m_pAdditionsDialog->m_sURL) : ""; + parseResponse(sResponse, m_pAdditionsDialog->m_aAllExtensionsVector); + std::sort(m_pAdditionsDialog->m_aAllExtensionsVector.begin(), + m_pAdditionsDialog->m_aAllExtensionsVector.end(), + AdditionsDialog::sortByDownload); + Search(); + } + else // Searching + { + Search(); + } + + if (!m_bExecute) + return; + + SolarMutexGuard aGuard; + sProgress.clear(); + m_pAdditionsDialog->SetProgress(sProgress); +} + +AdditionsDialog::AdditionsDialog(weld::Window* pParent, const OUString& sAdditionsTag) + : GenericDialogController(pParent, "cui/ui/additionsdialog.ui", "AdditionsDialog") + , m_aSearchDataTimer("AdditionsDialog SearchDataTimer") + , m_xEntrySearch(m_xBuilder->weld_entry("entrySearch")) + , m_xButtonClose(m_xBuilder->weld_button("buttonClose")) + , m_xContentWindow(m_xBuilder->weld_scrolled_window("contentWindow")) + , m_xContentGrid(m_xBuilder->weld_container("contentGrid")) + , m_xLabelProgress(m_xBuilder->weld_label("labelProgress")) + , m_xGearBtn(m_xBuilder->weld_menu_button("buttonGear")) +{ + m_xGearBtn->connect_selected(LINK(this, AdditionsDialog, GearHdl)); + m_xGearBtn->set_item_active("gear_sort_voting", true); + + m_aSearchDataTimer.SetInvokeHandler(LINK(this, AdditionsDialog, ImplUpdateDataHdl)); + m_aSearchDataTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT); + + m_xEntrySearch->connect_changed(LINK(this, AdditionsDialog, SearchUpdateHdl)); + m_xEntrySearch->connect_focus_out(LINK(this, AdditionsDialog, FocusOut_Impl)); + m_xButtonClose->connect_clicked(LINK(this, AdditionsDialog, CloseButtonHdl)); + + m_sTag = sAdditionsTag; + m_nMaxItemCount = PAGE_SIZE; // Dialog initialization item count + m_nCurrentListItemCount = 0; // First, there is no item on the list. + + OUString titlePrefix = CuiResId(RID_CUISTR_ADDITIONS_DIALOG_TITLE_PREFIX); + if (!m_sTag.isEmpty()) + { // tdf#142564 localize extension category names + OUString sDialogTitle = ""; + if (sAdditionsTag == "Templates") + { + sDialogTitle = CuiResId(RID_CUISTR_ADDITIONS_TEMPLATES); + } + else if (sAdditionsTag == "Dictionary") + { + sDialogTitle = CuiResId(RID_CUISTR_ADDITIONS_DICTIONARY); + } + else if (sAdditionsTag == "Gallery") + { + sDialogTitle = CuiResId(RID_CUISTR_ADDITIONS_GALLERY); + } + else if (sAdditionsTag == "Icons") + { + sDialogTitle = CuiResId(RID_CUISTR_ADDITIONS_ICONS); + } + else if (sAdditionsTag == "Color Palette") + { + sDialogTitle = CuiResId(RID_CUISTR_ADDITIONS_PALETTES); + } + this->set_title(sDialogTitle); + } + else + { + this->set_title(titlePrefix); + m_sTag = "allextensions"; // Means empty parameter + } + + OUString sEncodedURLPart = INetURLObject::encode(m_sTag, INetURLObject::PART_PCHAR, + INetURLObject::EncodeMechanism::All); + + //FIXME: Temporary URL - v0 is not using actual api + OUString rURL = "https://extensions.libreoffice.org/api/v0/" + sEncodedURLPart + ".json"; + m_sURL = rURL; + + m_xExtensionManager + = deployment::ExtensionManager::get(::comphelper::getProcessComponentContext()); + + //Initialize search util + m_searchOptions.AlgorithmType2 = css::util::SearchAlgorithms2::ABSOLUTE; + m_searchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE; + m_searchOptions.searchFlag |= (css::util::SearchFlags::REG_NOT_BEGINOFLINE + | css::util::SearchFlags::REG_NOT_ENDOFLINE); + m_pSearchThread = new SearchAndParseThread(this, true); + m_pSearchThread->launch(); +} + +AdditionsDialog::~AdditionsDialog() +{ + if (m_pSearchThread.is()) + { + m_pSearchThread->StopExecution(); + // Release the solar mutex, so the thread is not affected by the race + // when it's after the m_bExecute check but before taking the solar + // mutex. + SolarMutexReleaser aReleaser; + m_pSearchThread->join(); + } +} + +uno::Sequence>> +AdditionsDialog::getInstalledExtensions() +{ + uno::Sequence>> xAllPackages; + + try + { + xAllPackages = m_xExtensionManager->getAllExtensions( + uno::Reference(), uno::Reference()); + } + catch (const deployment::DeploymentException&) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", ""); + } + catch (const ucb::CommandFailedException&) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", ""); + } + catch (const ucb::CommandAbortedException&) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", ""); + } + catch (const lang::IllegalArgumentException& e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException(e.Message, e.Context, anyEx); + } + return xAllPackages; +} + +void AdditionsDialog::SetProgress(const OUString& rProgress) +{ + if (rProgress.isEmpty()) + { + m_xLabelProgress->hide(); + m_xButtonClose->set_sensitive(true); + } + else + { + SolarMutexGuard aGuard; + m_xLabelProgress->show(); + m_xLabelProgress->set_label(rProgress); + m_xDialog->resize_to_request(); //TODO + } +} + +void AdditionsDialog::ClearList() +{ + // for VCL to be able to destroy bitmaps + SolarMutexGuard aGuard; + + for (auto& item : this->m_aAdditionsItems) + { + item->m_xContainer->hide(); + } + this->m_aAdditionsItems.clear(); +} + +void AdditionsDialog::RefreshUI() +{ + if (m_pSearchThread.is()) + m_pSearchThread->StopExecution(); + ClearList(); + m_nCurrentListItemCount = 0; + m_nMaxItemCount = PAGE_SIZE; + m_pSearchThread = new SearchAndParseThread(this, false); + m_pSearchThread->launch(); +} + +bool AdditionsDialog::sortByRating(const AdditionInfo& a, const AdditionInfo& b) +{ + return a.sRating.toDouble() > b.sRating.toDouble(); +} + +bool AdditionsDialog::sortByComment(const AdditionInfo& a, const AdditionInfo& b) +{ + return a.sCommentNumber.toUInt32() > b.sCommentNumber.toUInt32(); +} + +bool AdditionsDialog::sortByDownload(const AdditionInfo& a, const AdditionInfo& b) +{ + return a.sDownloadNumber.toUInt32() > b.sDownloadNumber.toUInt32(); +} + +AdditionsItem::AdditionsItem(weld::Widget* pParent, AdditionsDialog* pParentDialog, + const AdditionInfo& additionInfo) + : m_xBuilder(Application::CreateBuilder(pParent, "cui/ui/additionsfragment.ui")) + , m_xContainer(m_xBuilder->weld_widget("additionsEntry")) + , m_xImageScreenshot(m_xBuilder->weld_image("imageScreenshot")) + , m_xButtonInstall(m_xBuilder->weld_button("buttonInstall")) + , m_xLinkButtonWebsite(m_xBuilder->weld_link_button("btnWebsite")) + , m_xLabelName(m_xBuilder->weld_label("lbName")) + , m_xLabelAuthor(m_xBuilder->weld_label("labelAuthor")) + , m_xLabelDescription(m_xBuilder->weld_label("labelDescription")) + , m_xLabelLicense(m_xBuilder->weld_label("lbLicenseText")) + , m_xLabelVersion(m_xBuilder->weld_label("lbVersionText")) + , m_xLinkButtonComments(m_xBuilder->weld_link_button("linkButtonComments")) + , m_xImageVoting1(m_xBuilder->weld_image("imageVoting1")) + , m_xImageVoting2(m_xBuilder->weld_image("imageVoting2")) + , m_xImageVoting3(m_xBuilder->weld_image("imageVoting3")) + , m_xImageVoting4(m_xBuilder->weld_image("imageVoting4")) + , m_xImageVoting5(m_xBuilder->weld_image("imageVoting5")) + , m_xLabelDownloadNumber(m_xBuilder->weld_label("labelDownloadNumber")) + , m_xButtonShowMore(m_xBuilder->weld_button("buttonShowMore")) + , m_pParentDialog(pParentDialog) + , m_sDownloadURL("") + , m_sExtensionID("") +{ + SolarMutexGuard aGuard; + + // AdditionsItem set location + m_xContainer->set_grid_left_attach(0); + m_xContainer->set_grid_top_attach(pParentDialog->m_aAdditionsItems.size()); + + // Set maximum length of the extension title + OUString sExtensionName; + const sal_Int32 maxExtensionNameLength = 30; + + if (additionInfo.sName.getLength() > maxExtensionNameLength) + { + std::u16string_view sShortName = additionInfo.sName.subView(0, maxExtensionNameLength - 3); + sExtensionName = OUString::Concat(sShortName) + "..."; + } + else + { + sExtensionName = additionInfo.sName; + } + + m_xLabelName->set_label(sExtensionName); + + double aExtensionRating = additionInfo.sRating.toDouble(); + switch (std::isnan(aExtensionRating) ? 0 : int(std::clamp(aExtensionRating, 0.0, 5.0))) + { + case 5: + m_xImageVoting5->set_from_icon_name(RID_SVXBMP_STARS_FULL); + [[fallthrough]]; + case 4: + m_xImageVoting4->set_from_icon_name(RID_SVXBMP_STARS_FULL); + [[fallthrough]]; + case 3: + m_xImageVoting3->set_from_icon_name(RID_SVXBMP_STARS_FULL); + [[fallthrough]]; + case 2: + m_xImageVoting2->set_from_icon_name(RID_SVXBMP_STARS_FULL); + [[fallthrough]]; + case 1: + m_xImageVoting1->set_from_icon_name(RID_SVXBMP_STARS_FULL); + break; + } + + m_xLinkButtonWebsite->set_uri(additionInfo.sExtensionURL); + m_xLabelDescription->set_label(additionInfo.sIntroduction); + + if (!additionInfo.sAuthorName.equalsIgnoreAsciiCase("null")) + m_xLabelAuthor->set_label(additionInfo.sAuthorName); + + m_xButtonInstall->set_label(CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON)); + m_xLabelLicense->set_label(additionInfo.sLicense); + m_xLabelVersion->set_label(">=" + additionInfo.sCompatibleVersion); + m_xLinkButtonComments->set_label(additionInfo.sCommentNumber); + m_xLinkButtonComments->set_uri(additionInfo.sCommentURL); + m_xLabelDownloadNumber->set_label(additionInfo.sDownloadNumber); + m_pParentDialog = pParentDialog; + m_sDownloadURL = additionInfo.sDownloadURL; + m_sExtensionID = additionInfo.sExtensionID; + + m_xButtonShowMore->connect_clicked(LINK(this, AdditionsItem, ShowMoreHdl)); + m_xButtonInstall->connect_clicked(LINK(this, AdditionsItem, InstallHdl)); +} + +bool AdditionsItem::getExtensionFile(OUString& sExtensionFile) +{ + uno::Reference xFileAccess + = ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext()); + + // copy the extensions' files to the user's additions folder + OUString userFolder = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER + "/" SAL_CONFIGFILE("bootstrap") "::UserInstallation}"; + rtl::Bootstrap::expandMacros(userFolder); + userFolder += "/user/additions/" + m_sExtensionID + "/"; + + OUString aExtensionsFile(INetURLObject(m_sDownloadURL).getName()); + OUString aExtensionsURL = m_sDownloadURL; + + try + { + osl::Directory::createPath(userFolder); + + if (!xFileAccess->exists(userFolder + aExtensionsFile)) + ucbDownload(aExtensionsURL, userFolder, aExtensionsFile); + } + catch (const uno::Exception&) + { + return false; + } + sExtensionFile = userFolder + aExtensionsFile; + return true; +} + +IMPL_LINK_NOARG(AdditionsDialog, ImplUpdateDataHdl, Timer*, void) { RefreshUI(); } + +IMPL_LINK_NOARG(AdditionsDialog, SearchUpdateHdl, weld::Entry&, void) +{ + m_aSearchDataTimer.Start(); +} + +IMPL_LINK_NOARG(AdditionsDialog, FocusOut_Impl, weld::Widget&, void) +{ + if (m_aSearchDataTimer.IsActive()) + { + m_aSearchDataTimer.Stop(); + m_aSearchDataTimer.Invoke(); + } +} + +IMPL_LINK_NOARG(AdditionsDialog, CloseButtonHdl, weld::Button&, void) +{ + if (m_pSearchThread.is()) + m_pSearchThread->StopExecution(); + this->response(RET_CLOSE); +} + +IMPL_LINK_NOARG(AdditionsItem, ShowMoreHdl, weld::Button&, void) +{ + this->m_xButtonShowMore->set_visible(false); + m_pParentDialog->m_nMaxItemCount += PAGE_SIZE; + if (m_pParentDialog->m_pSearchThread.is()) + m_pParentDialog->m_pSearchThread->StopExecution(); + m_pParentDialog->m_pSearchThread = new SearchAndParseThread(m_pParentDialog, false); + m_pParentDialog->m_pSearchThread->launch(); +} + +IMPL_LINK_NOARG(AdditionsItem, InstallHdl, weld::Button&, void) +{ + m_xButtonInstall->set_label(CuiResId(RID_CUISTR_ADDITIONS_INSTALLING)); + m_xButtonInstall->set_sensitive(false); + OUString aExtensionFile; + bool bResult = getExtensionFile(aExtensionFile); // info vector json data + + if (!bResult) + { + m_xButtonInstall->set_label(CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON)); + m_xButtonInstall->set_sensitive(true); + + SAL_INFO("cui.dialogs", "Couldn't get the extension file."); + return; + } + + rtl::Reference pCmdEnv = new TmpRepositoryCommandEnv(); + uno::Reference xAbortChannel; + try + { + m_pParentDialog->m_xExtensionManager->addExtension( + aExtensionFile, uno::Sequence(), "user", xAbortChannel, pCmdEnv); + m_xButtonInstall->set_label(CuiResId(RID_CUISTR_ADDITIONS_INSTALLEDBUTTON)); + } + catch (const ucb::CommandFailedException) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", ""); + m_xButtonInstall->set_label(CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON)); + m_xButtonInstall->set_sensitive(true); + } + catch (const ucb::CommandAbortedException) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", ""); + m_xButtonInstall->set_label(CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON)); + m_xButtonInstall->set_sensitive(true); + } + catch (const deployment::DeploymentException) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", ""); + m_xButtonInstall->set_label(CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON)); + m_xButtonInstall->set_sensitive(true); + } + catch (const lang::IllegalArgumentException) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", ""); + m_xButtonInstall->set_label(CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON)); + m_xButtonInstall->set_sensitive(true); + } + catch (const css::uno::Exception) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", ""); + m_xButtonInstall->set_label(CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON)); + m_xButtonInstall->set_sensitive(true); + } +} + +// TmpRepositoryCommandEnv + +TmpRepositoryCommandEnv::TmpRepositoryCommandEnv() {} + +TmpRepositoryCommandEnv::~TmpRepositoryCommandEnv() {} +// XCommandEnvironment + +uno::Reference TmpRepositoryCommandEnv::getInteractionHandler() +{ + return this; +} + +uno::Reference TmpRepositoryCommandEnv::getProgressHandler() { return this; } + +// XInteractionHandler +void TmpRepositoryCommandEnv::handle(uno::Reference const& xRequest) +{ + OSL_ASSERT(xRequest->getRequest().getValueTypeClass() == uno::TypeClass_EXCEPTION); + + bool approve = true; + + // select: + uno::Sequence> conts(xRequest->getContinuations()); + Reference const* pConts = conts.getConstArray(); + sal_Int32 len = conts.getLength(); + for (sal_Int32 pos = 0; pos < len; ++pos) + { + if (approve) + { + uno::Reference xInteractionApprove(pConts[pos], + uno::UNO_QUERY); + if (xInteractionApprove.is()) + { + xInteractionApprove->select(); + // don't query again for ongoing continuations: + approve = false; + } + } + } +} + +// XProgressHandler +void TmpRepositoryCommandEnv::push(uno::Any const& /*Status*/) {} + +void TmpRepositoryCommandEnv::update(uno::Any const& /*Status */) {} + +void TmpRepositoryCommandEnv::pop() {} + +IMPL_LINK(AdditionsDialog, GearHdl, const OUString&, rIdent, void) +{ + if (rIdent == "gear_sort_voting") + { + std::sort(m_aAllExtensionsVector.begin(), m_aAllExtensionsVector.end(), sortByRating); + } + else if (rIdent == "gear_sort_comments") + { + std::sort(m_aAllExtensionsVector.begin(), m_aAllExtensionsVector.end(), sortByComment); + } + else if (rIdent == "gear_sort_downloads") + { + std::sort(m_aAllExtensionsVector.begin(), m_aAllExtensionsVector.end(), sortByDownload); + } + // After the sorting, UI will be refreshed to update extension list. + RefreshUI(); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/DiagramDialog.cxx b/cui/source/dialogs/DiagramDialog.cxx new file mode 100644 index 0000000000..b63d6cb6e3 --- /dev/null +++ b/cui/source/dialogs/DiagramDialog.cxx @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* +* This file is part of the LibreOffice project. +* +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +DiagramDialog::DiagramDialog(weld::Window* pWindow, SdrObjGroup& rDiagram) + : GenericDialogController(pWindow, "cui/ui/diagramdialog.ui", "DiagramDialog") + , m_rDiagram(rDiagram) + , m_nUndos(0) + , mpBtnCancel(m_xBuilder->weld_button("btnCancel")) + , mpBtnAdd(m_xBuilder->weld_button("btnAdd")) + , mpBtnRemove(m_xBuilder->weld_button("btnRemove")) + , mpTreeDiagram(m_xBuilder->weld_tree_view("treeDiagram")) + , mpTextAdd(m_xBuilder->weld_text_view("textAdd")) +{ + mpBtnCancel->connect_clicked(LINK(this, DiagramDialog, OnAddCancel)); + mpBtnAdd->connect_clicked(LINK(this, DiagramDialog, OnAddClick)); + mpBtnRemove->connect_clicked(LINK(this, DiagramDialog, OnRemoveClick)); + + populateTree(nullptr, OUString()); + + // expand all items + weld::TreeView* pTreeDiagram = mpTreeDiagram.get(); + pTreeDiagram->all_foreach([pTreeDiagram](weld::TreeIter& rEntry) { + pTreeDiagram->expand_row(rEntry); + return false; + }); +} + +IMPL_LINK_NOARG(DiagramDialog, OnAddCancel, weld::Button&, void) +{ + // If the user cancels the dialog, undo all changes done so far. It may + // even be feasible to then delete the redo-stack, since it stays + // available (?) - but it does no harm either... + while (0 != m_nUndos) + { + comphelper::dispatchCommand(".uno:Undo", {}); + m_nUndos--; + } + + m_xDialog->response(RET_CANCEL); +} + +IMPL_LINK_NOARG(DiagramDialog, OnAddClick, weld::Button&, void) +{ + if (!m_rDiagram.isDiagram()) + return; + + OUString sText = mpTextAdd->get_text(); + const std::shared_ptr< svx::diagram::IDiagramHelper >& pDiagramHelper(m_rDiagram.getDiagramHelper()); + + if (pDiagramHelper && !sText.isEmpty()) + { + SdrModel& rDrawModel(m_rDiagram.getSdrModelFromSdrObject()); + const bool bUndo(rDrawModel.IsUndoEnabled()); + svx::diagram::DiagramDataStatePtr aStartState; + + if (bUndo) + { + // rescue all start state Diagram-defining data + aStartState = pDiagramHelper->extractDiagramDataState(); + } + + OUString sNodeId = pDiagramHelper->addNode(sText); + + if (bUndo) + { + // create undo action. That will internally secure the + // current Diagram-defining data as end state + rDrawModel.AddUndo( + rDrawModel.GetSdrUndoFactory().CreateUndoDiagramModelData(m_rDiagram, aStartState)); + m_nUndos++; + } + + std::unique_ptr pEntry(mpTreeDiagram->make_iterator()); + mpTreeDiagram->insert(nullptr, -1, &sText, &sNodeId, nullptr, nullptr, false, pEntry.get()); + mpTreeDiagram->select(*pEntry); + comphelper::dispatchCommand(".uno:RegenerateDiagram", {}); + } +} + +IMPL_LINK_NOARG(DiagramDialog, OnRemoveClick, weld::Button&, void) +{ + if (!m_rDiagram.isDiagram()) + return; + + std::unique_ptr pEntry(mpTreeDiagram->make_iterator()); + const std::shared_ptr< svx::diagram::IDiagramHelper >& pDiagramHelper(m_rDiagram.getDiagramHelper()); + + if (pDiagramHelper && mpTreeDiagram->get_selected(pEntry.get())) + { + SdrModel& rDrawModel(m_rDiagram.getSdrModelFromSdrObject()); + const bool bUndo(rDrawModel.IsUndoEnabled()); + svx::diagram::DiagramDataStatePtr aStartState; + + if (bUndo) + { + // rescue all start state Diagram-defining data + aStartState = pDiagramHelper->extractDiagramDataState(); + } + + if (pDiagramHelper->removeNode(mpTreeDiagram->get_id(*pEntry))) + { + if (bUndo) + { + // create undo action. That will internally secure the + // current Diagram-defining data as end state + rDrawModel.AddUndo(rDrawModel.GetSdrUndoFactory().CreateUndoDiagramModelData( + m_rDiagram, aStartState)); + m_nUndos++; + } + + mpTreeDiagram->remove(*pEntry); + comphelper::dispatchCommand(".uno:RegenerateDiagram", {}); + } + } +} + +void DiagramDialog::populateTree(const weld::TreeIter* pParent, const OUString& rParentId) +{ + if (!m_rDiagram.isDiagram()) + return; + + const std::shared_ptr< svx::diagram::IDiagramHelper >& pDiagramHelper(m_rDiagram.getDiagramHelper()); + + if (!pDiagramHelper) + return; + + auto aItems = pDiagramHelper->getChildren(rParentId); + for (auto& aItem : aItems) + { + std::unique_ptr pEntry(mpTreeDiagram->make_iterator()); + mpTreeDiagram->insert(pParent, -1, &aItem.second, &aItem.first, nullptr, nullptr, false, + pEntry.get()); + populateTree(pEntry.get(), aItem.first); + } +} + +DiagramDialog::~DiagramDialog() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/FontFeaturesDialog.cxx b/cui/source/dialogs/FontFeaturesDialog.cxx new file mode 100644 index 0000000000..e9aba0a6e4 --- /dev/null +++ b/cui/source/dialogs/FontFeaturesDialog.cxx @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include +#include +#include +#include +#include +#include + +using namespace css; + +namespace cui +{ +FontFeaturesDialog::FontFeaturesDialog(weld::Window* pParent, OUString aFontName) + : GenericDialogController(pParent, "cui/ui/fontfeaturesdialog.ui", "FontFeaturesDialog") + , m_sFontName(std::move(aFontName)) + , m_xContentWindow(m_xBuilder->weld_scrolled_window("contentWindow")) + , m_xContentBox(m_xBuilder->weld_container("contentBox")) + , m_xContentGrid(m_xBuilder->weld_container("contentGrid")) + , m_xStylisticSetsBox(m_xBuilder->weld_container("stylisticSetsBox")) + , m_xStylisticSetsGrid(m_xBuilder->weld_container("stylisticSetsGrid")) + , m_xCharacterVariantsBox(m_xBuilder->weld_container("characterVariantsBox")) + , m_xCharacterVariantsGrid(m_xBuilder->weld_container("characterVariantsGrid")) + , m_xPreviewWindow(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreviewWindow)) +{ + initialize(); +} + +FontFeaturesDialog::~FontFeaturesDialog() {} + +static sal_Int32 makeEnumComboBox(weld::ComboBox& rNameBox, + vcl::font::FeatureDefinition const& rFeatureDefinition, + uint32_t nDefault) +{ + sal_Int32 nRes = 0; + int count = 0; + for (vcl::font::FeatureParameter const& rParameter : rFeatureDefinition.getEnumParameters()) + { + rNameBox.append(OUString::number(rParameter.getCode()), rParameter.getDescription()); + if (rParameter.getCode() == nDefault) + nRes = count; + ++count; + } + return nRes; +} + +void FontFeaturesDialog::initialize() +{ + ScopedVclPtrInstance aVDev(*Application::GetDefaultDevice(), + DeviceFormat::WITH_ALPHA); + std::vector rFontFeatures = getFontFeatureList(m_sFontName, *aVDev); + + std::unordered_set aDoneFeatures; + std::vector rFilteredFontFeatures; + + for (vcl::font::Feature const& rFontFeature : rFontFeatures) + { + sal_uInt32 nFontFeatureCode = rFontFeature.m_nCode; + if (!aDoneFeatures.insert(nFontFeatureCode).second) + continue; + rFilteredFontFeatures.push_back(rFontFeature); + } + + int nRowHeight = fillGrid(rFilteredFontFeatures); + + auto nFeaturesHeight = m_xContentBox->get_preferred_size().Height() + + m_xStylisticSetsBox->get_preferred_size().Height() + + m_xCharacterVariantsBox->get_preferred_size().Height(); + m_xContentWindow->set_size_request( + -1, std::min(std::max(m_xContentWindow->get_preferred_size().Height(), nFeaturesHeight), + static_cast(300L))); + + if (nRowHeight) + { + // tdf#141333 use row height + the 6 px spacing of contentGrid + m_xContentWindow->vadjustment_set_step_increment(nRowHeight + 6); + } + + updateFontPreview(); +} + +int FontFeaturesDialog::fillGrid(std::vector const& rFontFeatures) +{ + int nRowHeight(0); + + vcl::font::FeatureParser aParser(m_sFontName); + auto aExistingFeatures = aParser.getFeaturesMap(); + + sal_Int32 nIdx, nStylisticSets(0), nCharacterVariants(0), nOtherFeatures(0); + for (vcl::font::Feature const& rFontFeature : rFontFeatures) + { + sal_uInt32 nFontFeatureCode = rFontFeature.m_nCode; + + vcl::font::FeatureDefinition aDefinition; + if (rFontFeature.m_aDefinition) + aDefinition = rFontFeature.m_aDefinition; + if (!aDefinition) + aDefinition = { nFontFeatureCode, "" }; + + if (rFontFeature.isStylisticSet()) + { + nIdx = nStylisticSets++; + m_xStylisticSetsBox->set_visible(true); + m_aFeatureItems.emplace_back( + std::make_unique(m_xStylisticSetsGrid.get())); + } + else if (rFontFeature.isCharacterVariant()) + { + nIdx = nCharacterVariants++; + m_xCharacterVariantsBox->set_visible(true); + m_aFeatureItems.emplace_back( + std::make_unique(m_xCharacterVariantsGrid.get())); + } + else + { + nIdx = nOtherFeatures++; + m_xContentBox->set_visible(true); + m_aFeatureItems.emplace_back(std::make_unique(m_xContentGrid.get())); + } + + int32_t nValue = 0; + if (aExistingFeatures.find(nFontFeatureCode) != aExistingFeatures.end()) + nValue = aExistingFeatures.at(nFontFeatureCode); + else + nValue = aDefinition.getDefault(); + + FontFeatureItem& aCurrentItem = *m_aFeatureItems.back(); + aCurrentItem.m_aFeatureCode = nFontFeatureCode; + aCurrentItem.m_nDefault = aDefinition.getDefault(); + + sal_Int32 nGridPositionX = (nIdx % 2) * 2; + sal_Int32 nGridPositionY = nIdx / 2; + aCurrentItem.m_xContainer->set_grid_left_attach(nGridPositionX); + aCurrentItem.m_xContainer->set_grid_top_attach(nGridPositionY); + + Link aComboBoxSelectHandler + = LINK(this, FontFeaturesDialog, ComboBoxSelectedHdl); + Link aCheckBoxToggleHandler + = LINK(this, FontFeaturesDialog, CheckBoxToggledHdl); + + if (aDefinition.getType() == vcl::font::FeatureParameterType::ENUM) + { + aCurrentItem.m_xText->set_label(aDefinition.getDescription()); + aCurrentItem.m_xText->show(); + + sal_Int32 nInit = makeEnumComboBox(*aCurrentItem.m_xCombo, aDefinition, nValue); + + aCurrentItem.m_xCombo->set_active(nInit); + aCurrentItem.m_xCombo->connect_changed(aComboBoxSelectHandler); + aCurrentItem.m_xCombo->show(); + } + else + { + if (nValue < 0) + { + aCurrentItem.m_xCheck->set_state(TRISTATE_INDET); + aCurrentItem.m_aTriStateEnabled.bTriStateEnabled = true; + aCurrentItem.m_aTriStateEnabled.eState = TRISTATE_INDET; + } + else + { + aCurrentItem.m_xCheck->set_state(nValue > 0 ? TRISTATE_TRUE : TRISTATE_FALSE); + aCurrentItem.m_aTriStateEnabled.bTriStateEnabled = false; + aCurrentItem.m_aTriStateEnabled.eState = aCurrentItem.m_xCheck->get_state(); + } + aCurrentItem.m_xCheck->set_label(aDefinition.getDescription()); + aCurrentItem.m_aToggleHdl = aCheckBoxToggleHandler; + aCurrentItem.m_xCheck->show(); + } + + nRowHeight + = std::max(nRowHeight, aCurrentItem.m_xContainer->get_preferred_size().Height()); + } + + return nRowHeight; +} + +void FontFeaturesDialog::updateFontPreview() +{ + vcl::Font rPreviewFont = m_aPreviewWindow.GetFont(); + vcl::Font rPreviewFontCJK = m_aPreviewWindow.GetCJKFont(); + vcl::Font rPreviewFontCTL = m_aPreviewWindow.GetCTLFont(); + + OUString sNewFontName = createFontNameWithFeatures(); + + rPreviewFont.SetFamilyName(sNewFontName); + rPreviewFontCJK.SetFamilyName(sNewFontName); + rPreviewFontCTL.SetFamilyName(sNewFontName); + + m_aPreviewWindow.SetFont(rPreviewFont, rPreviewFontCJK, rPreviewFontCTL); +} + +IMPL_LINK(FontFeatureItem, CheckBoxToggledHdl, weld::Toggleable&, rToggle, void) +{ + m_aTriStateEnabled.ButtonToggled(rToggle); + m_aTriStateEnabled.bTriStateEnabled = false; + m_aToggleHdl.Call(rToggle); +} + +IMPL_LINK_NOARG(FontFeaturesDialog, CheckBoxToggledHdl, weld::Toggleable&, void) +{ + updateFontPreview(); +} + +IMPL_LINK_NOARG(FontFeaturesDialog, ComboBoxSelectedHdl, weld::ComboBox&, void) +{ + updateFontPreview(); +} + +OUString FontFeaturesDialog::createFontNameWithFeatures() +{ + OUString sResultFontName; + OUStringBuffer sNameSuffix; + bool bFirst = true; + + for (const auto& rEntry : m_aFeatureItems) + { + const FontFeatureItem& rItem(*rEntry); + if (rItem.m_xCheck->get_visible()) + { + if (rItem.m_xCheck->get_state() != TRISTATE_INDET) + { + if (!bFirst) + sNameSuffix.append(vcl::font::FeatureSeparator); + else + bFirst = false; + + sNameSuffix.append(vcl::font::featureCodeAsString(rItem.m_aFeatureCode)); + if (rItem.m_xCheck->get_state() == TRISTATE_FALSE) + sNameSuffix.append("=0"); + } + } + else if (rItem.m_xCombo->get_visible() && rItem.m_xText->get_visible()) + { + sal_Int32 nSelection = rItem.m_xCombo->get_active_id().toInt32(); + if (nSelection != int(rItem.m_nDefault)) + { + if (!bFirst) + sNameSuffix.append(vcl::font::FeatureSeparator); + else + bFirst = false; + + sNameSuffix.append(vcl::font::featureCodeAsString(rItem.m_aFeatureCode) + "=" + + OUString::number(nSelection)); + } + } + } + sResultFontName = vcl::font::trimFontNameFeatures(m_sFontName); + if (!sNameSuffix.isEmpty()) + sResultFontName += OUStringChar(vcl::font::FeaturePrefix) + sNameSuffix; + return sResultFontName; +} + +short FontFeaturesDialog::run() +{ + short nResult = GenericDialogController::run(); + if (nResult == RET_OK) + { + m_sResultFontName = createFontNameWithFeatures(); + } + return nResult; +} + +} // end svx namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/GraphicTestsDialog.cxx b/cui/source/dialogs/GraphicTestsDialog.cxx new file mode 100644 index 0000000000..ad0c25aab1 --- /dev/null +++ b/cui/source/dialogs/GraphicTestsDialog.cxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +GraphicTestEntry::GraphicTestEntry(weld::Container* pParent, weld::Dialog* pDialog, + OUString aTestName, OUString aTestStatus, Bitmap aTestBitmap) + : m_xBuilder(Application::CreateBuilder(pParent, "cui/ui/graphictestentry.ui")) + , m_xContainer(m_xBuilder->weld_container("gptestbox")) + , m_xTestLabel(m_xBuilder->weld_label("gptestlabel")) + , m_xTestButton(m_xBuilder->weld_button("gptestbutton")) + , m_xResultBitmap(aTestBitmap) +{ + m_xParentDialog = pDialog; + m_xTestLabel->set_label(aTestName); + m_xTestButton->set_label(aTestStatus); + m_xTestButton->set_tooltip_text(aTestName); + m_xTestButton->set_background( + aTestStatus == SvlResId(GRTSTR_PASSED) + ? COL_LIGHTGREEN + : aTestStatus == SvlResId(GRTSTR_QUIRKY) + ? COL_YELLOW + : aTestStatus == SvlResId(GRTSTR_FAILED) ? COL_LIGHTRED : COL_LIGHTGRAY); + m_xTestButton->connect_clicked(LINK(this, GraphicTestEntry, HandleResultViewRequest)); + m_xContainer->show(); +} + +IMPL_LINK(GraphicTestEntry, HandleResultViewRequest, weld::Button&, rButton, void) +{ + if (rButton.get_label() == SvlResId(GRTSTR_SKIPPED)) + { + return; + } + ImageViewerDialog m_ImgVwDialog(m_xParentDialog, BitmapEx(m_xResultBitmap), + rButton.get_tooltip_text()); + m_ImgVwDialog.run(); +} + +GraphicsTestsDialog::GraphicsTestsDialog(weld::Container* pParent) + : GenericDialogController(pParent, "cui/ui/graphictestdlg.ui", "GraphicTestsDialog") + , m_xResultLog(m_xBuilder->weld_text_view("gptest_txtVW")) + , m_xDownloadResults(m_xBuilder->weld_button("gptest_downld")) + , m_xContainerBox(m_xBuilder->weld_box("gptest_box")) +{ + OUString userProfile = comphelper::BackupFileHelper::getUserProfileURL(); + m_xZipFileUrl = userProfile + "/GraphicTestResults.zip"; + m_xCreateFolderUrl = userProfile + "/GraphicTestResults"; + osl::Directory::create(m_xCreateFolderUrl); + m_xDownloadResults->connect_clicked(LINK(this, GraphicsTestsDialog, HandleDownloadRequest)); +} + +short GraphicsTestsDialog::run() +{ + GraphicsRenderTests aTestObject; + aTestObject.run(true); + OUString aResultLog + = aTestObject.getResultString(true) + "\n" + CuiResId(RID_CUISTR_CLICK_RESULT); + m_xResultLog->set_text(aResultLog); + sal_Int32 nTestNumber = 0; + for (VclTestResult& test : aTestObject.getTestResults()) + { + auto xGpTest = std::make_unique(m_xContainerBox.get(), m_xDialog.get(), + test.getTestName(), test.getStatus(true), + test.getBitmap()); + m_xContainerBox->reorder_child(xGpTest->get_widget(), nTestNumber++); + m_xGraphicTestEntries.push_back(std::move(xGpTest)); + } + return GenericDialogController::run(); +} + +IMPL_LINK_NOARG(GraphicsTestsDialog, HandleDownloadRequest, weld::Button&, void) +{ + osl::File::remove(m_xZipFileUrl); // Remove the previous export + try + { + utl::ZipPackageHelper aZipHelper(comphelper::getProcessComponentContext(), m_xZipFileUrl); + aZipHelper.addFolderWithContent(aZipHelper.getRootFolder(), m_xCreateFolderUrl); + aZipHelper.savePackage(); + } + catch (const std::exception&) + { + std::unique_ptr xBox( + Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Warning, + VclButtonsType::Ok, CuiResId(RID_CUISTR_ZIPFAIL))); + xBox->run(); + return; + } + FileExportedDialog aDialog(m_xDialog.get(), CuiResId(RID_CUISTR_SAVED)); + aDialog.run(); +} + +GraphicsTestsDialog::~GraphicsTestsDialog() +{ + comphelper::DirectoryHelper::deleteDirRecursively(m_xCreateFolderUrl); +} diff --git a/cui/source/dialogs/ImageViewerDialog.cxx b/cui/source/dialogs/ImageViewerDialog.cxx new file mode 100644 index 0000000000..b245c8c08b --- /dev/null +++ b/cui/source/dialogs/ImageViewerDialog.cxx @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +ImageViewerDialog::ImageViewerDialog(weld::Dialog* pParent, BitmapEx aBitmap, OUString atitle) + : GenericDialogController(pParent, "cui/ui/imageviewer.ui", "ImageViewerDialog") + , m_xDisplayImage(m_xBuilder->weld_image("ImgVW_mainImage")) +{ + m_xDialog->set_title(atitle); + aBitmap.Scale(Size(300, 300), BmpScaleFlag::Fast); + ScopedVclPtr m_pVirDev = m_xDisplayImage->create_virtual_device(); + m_pVirDev->SetOutputSizePixel(aBitmap.GetSizePixel()); + m_pVirDev->DrawBitmapEx(Point(0, 0), aBitmap); + m_xDisplayImage->set_image(m_pVirDev.get()); + m_pVirDev.disposeAndClear(); +} diff --git a/cui/source/dialogs/QrCodeGenDialog.cxx b/cui/source/dialogs/QrCodeGenDialog.cxx new file mode 100644 index 0000000000..8a25e3b364 --- /dev/null +++ b/cui/source/dialogs/QrCodeGenDialog.cxx @@ -0,0 +1,433 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if ENABLE_ZXING +#include + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#endif + +#include +#include +#include + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#if HAVE_ZXING_TOSVG +#include +#endif + +#if __has_include() +#include +#else +#include +#endif + +#endif // ENABLE_ZXING + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace css; +using namespace css::uno; +using namespace css::beans; +using namespace css::container; +using namespace css::frame; +using namespace css::io; +using namespace css::lang; +using namespace css::sheet; +using namespace css::text; +using namespace css::drawing; +using namespace css::graphic; + +namespace +{ +#if ENABLE_ZXING +// Implementation adapted from the answer: https://stackoverflow.com/questions/10789059/create-qr-code-in-vector-image/60638350#60638350 +#if !HAVE_ZXING_TOSVG +OString ConvertToSVGFormat(const ZXing::BitMatrix& bitmatrix) +{ + OStringBuffer sb; + const int width = bitmatrix.width(); + const int height = bitmatrix.height(); + sb.append("\n" + "\n" + "\n"); + return sb.toString(); +} +#endif + +std::string GetBarCodeType(int type) +{ + switch (type) + { + case 1: + return "Code128"; + default: + return "QRCode"; + } +} + +OString GenerateQRCode(std::u16string_view aQRText, tools::Long aQRECC, int aQRBorder, int aQRType) +{ + // Associated ZXing error correction levels (0-8) to our constants arbitrarily. + int bqrEcc = 1; + + switch (aQRECC) + { + case css::drawing::BarCodeErrorCorrection::LOW: + { + bqrEcc = 1; + break; + } + case css::drawing::BarCodeErrorCorrection::MEDIUM: + { + bqrEcc = 3; + break; + } + case css::drawing::BarCodeErrorCorrection::QUARTILE: + { + bqrEcc = 5; + break; + } + case css::drawing::BarCodeErrorCorrection::HIGH: + { + bqrEcc = 7; + break; + } + } + + OString o = OUStringToOString(aQRText, RTL_TEXTENCODING_UTF8); + std::string QRText(o); + ZXing::BarcodeFormat format = ZXing::BarcodeFormatFromString(GetBarCodeType(aQRType)); + auto writer = ZXing::MultiFormatWriter(format).setMargin(aQRBorder).setEccLevel(bqrEcc); + writer.setEncoding(ZXing::CharacterSet::UTF8); +#if __has_include() + ZXing::BitMatrix bitmatrix = writer.encode(ZXing::FromUtf8(QRText), 0, 0); +#else + ZXing::BitMatrix bitmatrix = writer.encode(ZXing::TextUtfEncoding::FromUtf8(QRText), 0, 0); +#endif +#if HAVE_ZXING_TOSVG + return OString(ZXing::ToSVG(bitmatrix)); +#else + return ConvertToSVGFormat(bitmatrix); +#endif +} +#endif + +} // anonymous namespace + +QrCodeGenDialog::QrCodeGenDialog(weld::Widget* pParent, Reference xModel, + bool bEditExisting) + : GenericDialogController(pParent, "cui/ui/qrcodegen.ui", "QrCodeGenDialog") + , m_xModel(std::move(xModel)) + , m_xEdittext(m_xBuilder->weld_text_view("edit_text")) + , m_xECC{ m_xBuilder->weld_radio_button("button_low"), + m_xBuilder->weld_radio_button("button_medium"), + m_xBuilder->weld_radio_button("button_quartile"), + m_xBuilder->weld_radio_button("button_high") } + , m_xSpinBorder(m_xBuilder->weld_spin_button("edit_margin")) + , m_xComboType(m_xBuilder->weld_combo_box("choose_type")) +#if ENABLE_ZXING + , mpParent(pParent) +#endif +{ + m_xEdittext->set_size_request(m_xEdittext->get_approximate_digit_width() * 28, + m_xEdittext->get_height_rows(6)); + if (!bEditExisting) + { + // TODO: This only works in Writer doc. Should also work in shapes + Reference xSelections(m_xModel->getCurrentSelection(), UNO_QUERY); + if (xSelections.is()) + { + Reference xSelection(xSelections->getByIndex(0), UNO_QUERY); + if (xSelection.is()) + m_xEdittext->set_text(xSelection->getString()); + } + return; + } + + Reference xIndexAccess(m_xModel->getCurrentSelection(), + UNO_QUERY_THROW); + Reference xProps(xIndexAccess->getByIndex(0), UNO_QUERY_THROW); + + // Read properties from selected QR Code + css::drawing::BarCode aBarCode; + xProps->getPropertyValue("BarCodeProperties") >>= aBarCode; + + m_xEdittext->set_text(aBarCode.Payload); + + //Get Error Correction Constant from selected QR Code + GetErrorCorrection(aBarCode.ErrorCorrection); + + m_xSpinBorder->set_value(aBarCode.Border); + + m_xComboType->set_active(aBarCode.Type); + + // Mark this as existing shape + m_xExistingShapeProperties = xProps; +} + +short QrCodeGenDialog::run() +{ +#if ENABLE_ZXING + short nRet; + while (true) + { + nRet = GenericDialogController::run(); + if (nRet == RET_OK) + { + try + { + Apply(); + break; + } + catch (const std::exception&) + { + std::unique_ptr xBox(Application::CreateMessageDialog( + mpParent, VclMessageType::Warning, VclButtonsType::Ok, + CuiResId(RID_CUISTR_QRCODEDATALONG))); + xBox->run(); + } + } + else + break; + } + return nRet; +#else + return RET_CANCEL; +#endif +} + +bool QrCodeGenDialog::runAsync(const std::shared_ptr& rController, + const std::function& rFunc) +{ +#if ENABLE_ZXING + + weld::GenericDialogController::runAsync(rController, [rController, rFunc](sal_Int32 nResult) { + if (nResult == RET_OK) + { + try + { + rController->Apply(); + } + catch (const std::exception&) + { + std::unique_ptr xBox(Application::CreateMessageDialog( + rController->GetParent(), VclMessageType::Warning, VclButtonsType::Ok, + CuiResId(RID_CUISTR_QRCODEDATALONG))); + xBox->run(); + } + } + + rFunc(nResult); + }); +#endif + return true; +} + +void QrCodeGenDialog::Apply() +{ +#if ENABLE_ZXING + css::drawing::BarCode aBarCode; + aBarCode.Payload = m_xEdittext->get_text(); + aBarCode.Type = m_xComboType->get_active(); + + bool bLowECCActive(m_xECC[0]->get_active()); + bool bMediumECCActive(m_xECC[1]->get_active()); + bool bQuartileECCActive(m_xECC[2]->get_active()); + + if (bLowECCActive) + { + aBarCode.ErrorCorrection = css::drawing::BarCodeErrorCorrection::LOW; + } + else if (bMediumECCActive) + { + aBarCode.ErrorCorrection = css::drawing::BarCodeErrorCorrection::MEDIUM; + } + else if (bQuartileECCActive) + { + aBarCode.ErrorCorrection = css::drawing::BarCodeErrorCorrection::QUARTILE; + } + else + { + aBarCode.ErrorCorrection = css::drawing::BarCodeErrorCorrection::HIGH; + } + + aBarCode.Border = m_xSpinBorder->get_value(); + + // Read svg and replace placeholder texts + OString aSvgImage = GenerateQRCode(aBarCode.Payload, aBarCode.ErrorCorrection, aBarCode.Border, + aBarCode.Type); + + // Insert/Update graphic + SvMemoryStream aSvgStream(4096, 4096); + aSvgStream.WriteOString(aSvgImage); + Reference xInputStream(new utl::OSeekableInputStreamWrapper(aSvgStream)); + Reference xContext(comphelper::getProcessComponentContext()); + Reference xProvider = css::graphic::GraphicProvider::create(xContext); + + Sequence aMediaProperties{ comphelper::makePropertyValue("InputStream", + xInputStream) }; + Reference xGraphic(xProvider->queryGraphic(aMediaProperties)); + + bool bIsExistingQRCode = m_xExistingShapeProperties.is(); + Reference xShapeProps; + if (bIsExistingQRCode) + xShapeProps = m_xExistingShapeProperties; + else + xShapeProps.set(Reference(m_xModel, UNO_QUERY_THROW) + ->createInstance("com.sun.star.drawing.GraphicObjectShape"), + UNO_QUERY); + + xShapeProps->setPropertyValue("Graphic", Any(xGraphic)); + + // Set QRCode properties + xShapeProps->setPropertyValue("BarCodeProperties", Any(aBarCode)); + + if (bIsExistingQRCode) + return; + + // Default size + Reference xShape(xShapeProps, UNO_QUERY); + awt::Size aShapeSize; + aShapeSize.Height = 4000; + aShapeSize.Width = 4000; + xShape->setSize(aShapeSize); + + // Default anchoring + xShapeProps->setPropertyValue("AnchorType", Any(TextContentAnchorType_AT_PARAGRAPH)); + + const Reference xServiceInfo(m_xModel, UNO_QUERY_THROW); + + // Writer + if (xServiceInfo->supportsService("com.sun.star.text.TextDocument")) + { + Reference xTextContent(xShape, UNO_QUERY_THROW); + Reference xViewCursorSupplier(m_xModel->getCurrentController(), + UNO_QUERY_THROW); + Reference xCursor = xViewCursorSupplier->getViewCursor(); + // use cursor's XText - it might be in table cell, frame, ... + Reference const xText(xCursor->getText()); + assert(xText.is()); + xText->insertTextContent(xCursor, xTextContent, true); + return; + } + + // Calc + else if (xServiceInfo->supportsService("com.sun.star.sheet.SpreadsheetDocument")) + { + Reference xSheetCell(m_xModel->getCurrentSelection(), UNO_QUERY_THROW); + awt::Point aCellPosition; + xSheetCell->getPropertyValue("Position") >>= aCellPosition; + xShape->setPosition(aCellPosition); + + Reference xView(m_xModel->getCurrentController(), UNO_QUERY_THROW); + Reference xSheet(xView->getActiveSheet(), UNO_SET_THROW); + Reference xDrawPageSupplier(xSheet, UNO_QUERY_THROW); + Reference xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW); + Reference xShapes(xDrawPage, UNO_QUERY_THROW); + + xShapes->add(xShape); + return; + } + + //Impress and Draw + else if (xServiceInfo->supportsService("com.sun.star.presentation.PresentationDocument") + || xServiceInfo->supportsService("com.sun.star.drawing.DrawingDocument")) + { + Reference xView(m_xModel->getCurrentController(), UNO_QUERY_THROW); + Reference xPage(xView->getCurrentPage(), UNO_SET_THROW); + Reference xShapes(xPage, UNO_QUERY_THROW); + + xShapes->add(xShape); + return; + } + + else + { + //Not implemented for math,base and other apps. + throw uno::RuntimeException("Not implemented"); + } +#endif +} + +void QrCodeGenDialog::GetErrorCorrection(tools::Long ErrorCorrection) +{ + switch (ErrorCorrection) + { + case css::drawing::BarCodeErrorCorrection::LOW: + { + m_xECC[0]->set_active(true); + break; + } + case css::drawing::BarCodeErrorCorrection::MEDIUM: + { + m_xECC[1]->set_active(true); + break; + } + case css::drawing::BarCodeErrorCorrection::QUARTILE: + { + m_xECC[2]->set_active(true); + break; + } + case css::drawing::BarCodeErrorCorrection::HIGH: + { + m_xECC[3]->set_active(true); + break; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/cui/source/dialogs/SignSignatureLineDialog.cxx b/cui/source/dialogs/SignSignatureLineDialog.cxx new file mode 100644 index 0000000000..4088b40e17 --- /dev/null +++ b/cui/source/dialogs/SignSignatureLineDialog.cxx @@ -0,0 +1,258 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace comphelper; +using namespace css; +using namespace css::uno; +using namespace css::beans; +using namespace css::frame; +using namespace css::io; +using namespace css::lang; +using namespace css::frame; +using namespace css::text; +using namespace css::graphic; +using namespace css::security; +using namespace css::ui::dialogs; + +SignSignatureLineDialog::SignSignatureLineDialog(weld::Widget* pParent, Reference xModel) + : SignatureLineDialogBase(pParent, std::move(xModel), "cui/ui/signsignatureline.ui", + "SignSignatureLineDialog") + , m_xEditName(m_xBuilder->weld_entry("edit_name")) + , m_xEditComment(m_xBuilder->weld_text_view("edit_comment")) + , m_xBtnLoadImage(m_xBuilder->weld_button("btn_load_image")) + , m_xBtnClearImage(m_xBuilder->weld_button("btn_clear_image")) + , m_xBtnChooseCertificate(m_xBuilder->weld_button("btn_select_certificate")) + , m_xBtnSign(m_xBuilder->weld_button("ok")) + , m_xLabelHint(m_xBuilder->weld_label("label_hint")) + , m_xLabelHintText(m_xBuilder->weld_label("label_hint_text")) + , m_xLabelAddComment(m_xBuilder->weld_label("label_add_comment")) + , m_bShowSignDate(false) +{ + Reference xIndexAccess(m_xModel->getCurrentSelection(), + UNO_QUERY_THROW); + m_xShapeProperties.set(xIndexAccess->getByIndex(0), UNO_QUERY_THROW); + + bool bIsSignatureLine(false); + m_xShapeProperties->getPropertyValue("IsSignatureLine") >>= bIsSignatureLine; + if (!bIsSignatureLine) + { + SAL_WARN("cui.dialogs", "No signature line selected!"); + return; + } + + m_xBtnLoadImage->connect_clicked(LINK(this, SignSignatureLineDialog, loadImage)); + m_xBtnClearImage->connect_clicked(LINK(this, SignSignatureLineDialog, clearImage)); + m_xBtnChooseCertificate->connect_clicked( + LINK(this, SignSignatureLineDialog, chooseCertificate)); + m_xEditName->connect_changed(LINK(this, SignSignatureLineDialog, entryChanged)); + + // Read properties from selected signature line + m_xShapeProperties->getPropertyValue("SignatureLineId") >>= m_aSignatureLineId; + m_xShapeProperties->getPropertyValue("SignatureLineSuggestedSignerName") + >>= m_aSuggestedSignerName; + m_xShapeProperties->getPropertyValue("SignatureLineSuggestedSignerTitle") + >>= m_aSuggestedSignerTitle; + OUString aSigningInstructions; + m_xShapeProperties->getPropertyValue("SignatureLineSigningInstructions") + >>= aSigningInstructions; + m_xShapeProperties->getPropertyValue("SignatureLineShowSignDate") >>= m_bShowSignDate; + bool bCanAddComment(false); + m_xShapeProperties->getPropertyValue("SignatureLineCanAddComment") >>= bCanAddComment; + + if (aSigningInstructions.isEmpty()) + { + m_xLabelHint->hide(); + m_xLabelHintText->hide(); + } + else + { + m_xLabelHintText->set_label(aSigningInstructions); + } + + if (bCanAddComment) + { + m_xEditComment->set_size_request(m_xEditComment->get_approximate_digit_width() * 48, + m_xEditComment->get_text_height() * 5); + } + else + { + m_xLabelAddComment->hide(); + m_xEditComment->hide(); + m_xEditComment->set_size_request(0, 0); + } + + ValidateFields(); +} + +IMPL_LINK_NOARG(SignSignatureLineDialog, loadImage, weld::Button&, void) +{ + Reference xContext = comphelper::getProcessComponentContext(); + sfx2::FileDialogHelper aHelper(TemplateDescription::FILEOPEN_PREVIEW, FileDialogFlags::NONE, + m_xDialog.get()); + aHelper.SetContext(sfx2::FileDialogHelper::SignatureLine); + Reference xFilePicker = aHelper.GetFilePicker(); + if (!xFilePicker->execute()) + return; + + Sequence aSelectedFiles = xFilePicker->getSelectedFiles(); + if (!aSelectedFiles.hasElements()) + return; + + Reference xProvider = GraphicProvider::create(xContext); + Sequence aMediaProperties{ comphelper::makePropertyValue("URL", + aSelectedFiles[0]) }; + m_xSignatureImage = xProvider->queryGraphic(aMediaProperties); + m_sOriginalImageBtnLabel = m_xBtnLoadImage->get_label(); + + INetURLObject aObj(aSelectedFiles[0]); + m_xBtnLoadImage->set_label(aObj.GetLastName()); + + ValidateFields(); +} + +IMPL_LINK_NOARG(SignSignatureLineDialog, clearImage, weld::Button&, void) +{ + m_xSignatureImage.clear(); + m_xBtnLoadImage->set_label(m_sOriginalImageBtnLabel); + ValidateFields(); +} + +IMPL_LINK_NOARG(SignSignatureLineDialog, chooseCertificate, weld::Button&, void) +{ + // Document needs to be saved before selecting a certificate + SfxObjectShell* pShell = SfxObjectShell::Current(); + if (!pShell || !pShell->PrepareForSigning(m_xDialog.get())) + return; + + Reference xSignCertificate + = svx::SignatureLineHelper::getSignatureCertificate(pShell, m_xDialog.get()); + + if (xSignCertificate.is()) + { + m_xSelectedCertifate = xSignCertificate; + m_xBtnChooseCertificate->set_label( + svx::SignatureLineHelper::getSignerName(xSignCertificate)); + } + ValidateFields(); +} + +IMPL_LINK_NOARG(SignSignatureLineDialog, entryChanged, weld::Entry&, void) { ValidateFields(); } + +void SignSignatureLineDialog::ValidateFields() +{ + bool bEnableSignBtn = m_xSelectedCertifate.is() + && (!m_xEditName->get_text().isEmpty() || m_xSignatureImage.is()); + m_xBtnSign->set_sensitive(bEnableSignBtn); + + m_xEditName->set_sensitive(!m_xSignatureImage.is()); + m_xBtnLoadImage->set_sensitive(m_xEditName->get_text().isEmpty()); + m_xBtnClearImage->set_sensitive(m_xSignatureImage.is()); +} + +void SignSignatureLineDialog::Apply() +{ + if (!m_xSelectedCertifate.is()) + { + SAL_WARN("cui.dialogs", "No certificate selected!"); + return; + } + + SfxObjectShell* pShell = SfxObjectShell::Current(); + if (!pShell) + { + SAL_WARN("cui.dialogs", "No SfxObjectShell!"); + return; + } + + Reference xValidGraphic = getSignedGraphic(true); + Reference xInvalidGraphic = getSignedGraphic(false); + pShell->SignSignatureLine(m_xDialog.get(), m_aSignatureLineId, m_xSelectedCertifate, + xValidGraphic, xInvalidGraphic, m_xEditComment->get_text()); +} + +css::uno::Reference SignSignatureLineDialog::getSignedGraphic(bool bValid) +{ + // Read svg and replace placeholder texts + OUString aSvgImage(svx::SignatureLineHelper::getSignatureImage()); + aSvgImage = aSvgImage.replaceAll("[SIGNER_NAME]", getCDataString(m_aSuggestedSignerName)); + aSvgImage = aSvgImage.replaceAll("[SIGNER_TITLE]", getCDataString(m_aSuggestedSignerTitle)); + + OUString aIssuerLine + = CuiResId(RID_CUISTR_SIGNATURELINE_SIGNED_BY) + .replaceFirst("%1", svx::SignatureLineHelper::getSignerName(m_xSelectedCertifate)); + aSvgImage = aSvgImage.replaceAll("[SIGNED_BY]", getCDataString(aIssuerLine)); + if (bValid) + aSvgImage = aSvgImage.replaceAll("[INVALID_SIGNATURE]", ""); + + OUString aDate; + if (m_bShowSignDate && bValid) + { + aDate = svx::SignatureLineHelper::getLocalizedDate(); + } + aSvgImage = aSvgImage.replaceAll("[DATE]", aDate); + + // Custom signature image + if (m_xSignatureImage.is()) + { + OUString aGraphicInBase64; + Graphic aGraphic(m_xSignatureImage); + if (!XOutBitmap::GraphicToBase64(aGraphic, aGraphicInBase64, false)) + SAL_WARN("cui.dialogs", "Could not convert graphic to base64"); + + OUString aImagePart = "\" " + "preserveAspectRatio=\"xMidYMid\" height=\"1520\" " + "width=\"7600\" />"; + aImagePart = aImagePart.replaceAll( + "[MIMETYPE]", GraphicMimeTypeHelper::GetMimeTypeForXGraphic(m_xSignatureImage)); + aImagePart = aImagePart.replaceAll("[BASE64_IMG]", aGraphicInBase64); + aSvgImage = aSvgImage.replaceAll("[SIGNATURE_IMAGE]", aImagePart); + + aSvgImage = aSvgImage.replaceAll("[SIGNATURE]", ""); + } + else + { + aSvgImage = aSvgImage.replaceAll("[SIGNATURE_IMAGE]", ""); + aSvgImage = aSvgImage.replaceAll("[SIGNATURE]", getCDataString(m_xEditName->get_text())); + } + + // Create graphic + return svx::SignatureLineHelper::importSVG(aSvgImage); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/cui/source/dialogs/SignatureLineDialog.cxx b/cui/source/dialogs/SignatureLineDialog.cxx new file mode 100644 index 0000000000..224dcdecd4 --- /dev/null +++ b/cui/source/dialogs/SignatureLineDialog.cxx @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace css; +using namespace css::uno; +using namespace css::beans; +using namespace css::container; +using namespace css::frame; +using namespace css::lang; +using namespace css::frame; +using namespace css::sheet; +using namespace css::text; +using namespace css::drawing; +using namespace css::graphic; + +SignatureLineDialog::SignatureLineDialog(weld::Widget* pParent, Reference xModel, + bool bEditExisting) + : SignatureLineDialogBase(pParent, std::move(xModel), "cui/ui/signatureline.ui", + "SignatureLineDialog") + , m_xEditName(m_xBuilder->weld_entry("edit_name")) + , m_xEditTitle(m_xBuilder->weld_entry("edit_title")) + , m_xEditEmail(m_xBuilder->weld_entry("edit_email")) + , m_xEditInstructions(m_xBuilder->weld_text_view("edit_instructions")) + , m_xCheckboxCanAddComments(m_xBuilder->weld_check_button("checkbox_can_add_comments")) + , m_xCheckboxShowSignDate(m_xBuilder->weld_check_button("checkbox_show_sign_date")) +{ + m_xEditInstructions->set_size_request(m_xEditInstructions->get_approximate_digit_width() * 48, + m_xEditInstructions->get_text_height() * 5); + + // No signature line selected - start with empty dialog and set some default values + if (!bEditExisting) + { + m_xCheckboxCanAddComments->set_active(true); + m_xCheckboxShowSignDate->set_active(true); + return; + } + + Reference xIndexAccess(m_xModel->getCurrentSelection(), + UNO_QUERY_THROW); + Reference xProps(xIndexAccess->getByIndex(0), UNO_QUERY_THROW); + + // Read properties from selected signature line + xProps->getPropertyValue("SignatureLineId") >>= m_aSignatureLineId; + OUString aSuggestedSignerName; + xProps->getPropertyValue("SignatureLineSuggestedSignerName") >>= aSuggestedSignerName; + m_xEditName->set_text(aSuggestedSignerName); + OUString aSuggestedSignerTitle; + xProps->getPropertyValue("SignatureLineSuggestedSignerTitle") >>= aSuggestedSignerTitle; + m_xEditTitle->set_text(aSuggestedSignerTitle); + OUString aSuggestedSignerEmail; + xProps->getPropertyValue("SignatureLineSuggestedSignerEmail") >>= aSuggestedSignerEmail; + m_xEditEmail->set_text(aSuggestedSignerEmail); + OUString aSigningInstructions; + xProps->getPropertyValue("SignatureLineSigningInstructions") >>= aSigningInstructions; + m_xEditInstructions->set_text(aSigningInstructions); + bool bCanAddComments = false; + xProps->getPropertyValue("SignatureLineCanAddComment") >>= bCanAddComments; + m_xCheckboxCanAddComments->set_active(bCanAddComments); + bool bShowSignDate = false; + xProps->getPropertyValue("SignatureLineShowSignDate") >>= bShowSignDate; + m_xCheckboxShowSignDate->set_active(bShowSignDate); + + // Mark this as existing shape + m_xExistingShapeProperties = xProps; +} + +void SignatureLineDialog::Apply() +{ + if (m_aSignatureLineId.isEmpty()) + m_aSignatureLineId + = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_ASCII_US); + OUString aSignerName(m_xEditName->get_text()); + OUString aSignerTitle(m_xEditTitle->get_text()); + OUString aSignerEmail(m_xEditEmail->get_text()); + OUString aSigningInstructions(m_xEditInstructions->get_text()); + bool bCanAddComments(m_xCheckboxCanAddComments->get_active()); + bool bShowSignDate(m_xCheckboxShowSignDate->get_active()); + + // Read svg and replace placeholder texts + OUString aSvgImage(svx::SignatureLineHelper::getSignatureImage()); + aSvgImage = aSvgImage.replaceAll("[SIGNER_NAME]", getCDataString(aSignerName)); + aSvgImage = aSvgImage.replaceAll("[SIGNER_TITLE]", getCDataString(aSignerTitle)); + + // These are only filled if the signature line is signed. + aSvgImage = aSvgImage.replaceAll("[SIGNATURE]", ""); + aSvgImage = aSvgImage.replaceAll("[SIGNED_BY]", ""); + aSvgImage = aSvgImage.replaceAll("[INVALID_SIGNATURE]", ""); + aSvgImage = aSvgImage.replaceAll("[DATE]", ""); + + // Insert/Update graphic + Reference xGraphic = svx::SignatureLineHelper::importSVG(aSvgImage); + + bool bIsExistingSignatureLine = m_xExistingShapeProperties.is(); + Reference xShapeProps; + if (bIsExistingSignatureLine) + xShapeProps = m_xExistingShapeProperties; + else + xShapeProps.set(Reference(m_xModel, UNO_QUERY_THROW) + ->createInstance("com.sun.star.drawing.GraphicObjectShape"), + UNO_QUERY); + + xShapeProps->setPropertyValue("Graphic", Any(xGraphic)); + xShapeProps->setPropertyValue("SignatureLineUnsignedImage", Any(xGraphic)); + + // Set signature line properties + xShapeProps->setPropertyValue("IsSignatureLine", Any(true)); + xShapeProps->setPropertyValue("SignatureLineId", Any(m_aSignatureLineId)); + if (!aSignerName.isEmpty()) + xShapeProps->setPropertyValue("SignatureLineSuggestedSignerName", Any(aSignerName)); + if (!aSignerTitle.isEmpty()) + xShapeProps->setPropertyValue("SignatureLineSuggestedSignerTitle", Any(aSignerTitle)); + if (!aSignerEmail.isEmpty()) + xShapeProps->setPropertyValue("SignatureLineSuggestedSignerEmail", Any(aSignerEmail)); + if (!aSigningInstructions.isEmpty()) + xShapeProps->setPropertyValue("SignatureLineSigningInstructions", + Any(aSigningInstructions)); + xShapeProps->setPropertyValue("SignatureLineShowSignDate", Any(bShowSignDate)); + xShapeProps->setPropertyValue("SignatureLineCanAddComment", Any(bCanAddComments)); + + if (bIsExistingSignatureLine) + return; + + // Default size + Reference xShape(xShapeProps, UNO_QUERY); + awt::Size aShapeSize; + aShapeSize.Height = 3000; + aShapeSize.Width = 6000; + xShape->setSize(aShapeSize); + + // Default anchoring + xShapeProps->setPropertyValue("AnchorType", Any(TextContentAnchorType_AT_PARAGRAPH)); + + // Writer + const Reference xTextDocument(m_xModel, UNO_QUERY); + if (xTextDocument.is()) + { + Reference xTextContent(xShape, UNO_QUERY_THROW); + Reference xViewCursorSupplier(m_xModel->getCurrentController(), + UNO_QUERY_THROW); + Reference xCursor = xViewCursorSupplier->getViewCursor(); + // use cursor's XText - it might be in table cell, frame, ... + Reference const xText(xCursor->getText()); + assert(xText.is()); + xText->insertTextContent(xCursor, xTextContent, true); + return; + } + + // Calc + const Reference xSpreadsheetDocument(m_xModel, UNO_QUERY); + if (!xSpreadsheetDocument.is()) + return; + + Reference xSheetCell(m_xModel->getCurrentSelection(), UNO_QUERY_THROW); + awt::Point aCellPosition; + xSheetCell->getPropertyValue("Position") >>= aCellPosition; + xShape->setPosition(aCellPosition); + + Reference xView(m_xModel->getCurrentController(), UNO_QUERY_THROW); + Reference xSheet(xView->getActiveSheet(), UNO_SET_THROW); + Reference xDrawPageSupplier(xSheet, UNO_QUERY_THROW); + Reference xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW); + Reference xShapes(xDrawPage, UNO_QUERY_THROW); + + xShapes->add(xShape); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/cui/source/dialogs/SignatureLineDialogBase.cxx b/cui/source/dialogs/SignatureLineDialogBase.cxx new file mode 100644 index 0000000000..e0ae28fe50 --- /dev/null +++ b/cui/source/dialogs/SignatureLineDialogBase.cxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include + +#include + +using namespace css; +using namespace css::uno; +using namespace css::frame; + +SignatureLineDialogBase::SignatureLineDialogBase(weld::Widget* pParent, Reference xModel, + const OUString& rUIFile, const OUString& rDialogId) + : GenericDialogController(pParent, rUIFile, rDialogId) + , m_xModel(std::move(xModel)) +{ +} + +short SignatureLineDialogBase::run() +{ + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; +} + +OUString SignatureLineDialogBase::getCDataString(std::u16string_view rString) +{ + return OUString::Concat(""; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/cui/source/dialogs/SpellAttrib.hxx b/cui/source/dialogs/SpellAttrib.hxx new file mode 100644 index 0000000000..cdc0cf2666 --- /dev/null +++ b/cui/source/dialogs/SpellAttrib.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include +#include +#include +#include + +namespace svx{ +struct SpellErrorDescription +{ + bool bIsGrammarError; + OUString sErrorText; + OUString sDialogTitle; + OUString sExplanation; + OUString sExplanationURL; + css::lang::Locale aLocale; + css::uno::Reference< css::linguistic2::XProofreader > xGrammarChecker; + css::uno::Sequence< OUString > aSuggestions; + OUString sRuleId; + + SpellErrorDescription( bool bGrammar, + OUString aText, + css::lang::Locale _aLocale, + const css::uno::Sequence< OUString >& rSuggestions, + css::uno::Reference< css::linguistic2::XProofreader > _xGrammarChecker, + const OUString* pDialogTitle = nullptr, + const OUString* pExplanation = nullptr, + const OUString* pRuleId = nullptr, + const OUString* pExplanationURL = nullptr ) : + bIsGrammarError( bGrammar ), + sErrorText(std::move( aText )), + sDialogTitle( ), + sExplanation( ), + sExplanationURL( ), + aLocale(std::move( _aLocale )), + xGrammarChecker(std::move( _xGrammarChecker )), + aSuggestions( rSuggestions ) + { + if( pDialogTitle ) + sDialogTitle = *pDialogTitle; + if( pExplanation ) + sExplanation = *pExplanation; + if( pExplanationURL ) + sExplanationURL = *pExplanationURL; + if( pRuleId ) + sRuleId = *pRuleId; + }; + + SpellErrorDescription() + : bIsGrammarError(false) + { + } + + bool operator==( const SpellErrorDescription& rDesc ) const + { + return bIsGrammarError == rDesc.bIsGrammarError && + sErrorText == rDesc.sErrorText && + aLocale.Language == rDesc.aLocale.Language && + aLocale.Country == rDesc.aLocale.Country && + aLocale.Variant == rDesc.aLocale.Variant && + aSuggestions == rDesc.aSuggestions && + xGrammarChecker == rDesc.xGrammarChecker && + sDialogTitle == rDesc.sDialogTitle && + sExplanation == rDesc.sExplanation && + sExplanationURL == rDesc.sExplanationURL && + sRuleId == rDesc.sRuleId; + } + + css::uno::Sequence toSequence() const + { + css::uno::Sequence aEntries{ css::uno::Any(bIsGrammarError), + css::uno::Any(sErrorText), + css::uno::Any(sDialogTitle), + css::uno::Any(sExplanation), + css::uno::Any(sExplanationURL), + css::uno::Any(aLocale), + css::uno::Any(xGrammarChecker), + css::uno::Any(aSuggestions), + css::uno::Any(sRuleId) }; + return aEntries; + } + + void fromSequence(const css::uno::Sequence& rEntries) + { + rEntries[0] >>= bIsGrammarError; + rEntries[1] >>= sErrorText; + rEntries[2] >>= sDialogTitle; + rEntries[3] >>= sExplanation; + rEntries[4] >>= sExplanationURL; + rEntries[5] >>= aLocale; + rEntries[6] >>= xGrammarChecker; + rEntries[7] >>= aSuggestions; + rEntries[8] >>= sRuleId; + } +}; + +}//namespace svx + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/SpellDialog.cxx b/cui/source/dialogs/SpellDialog.cxx new file mode 100644 index 0000000000..dfdad984ec --- /dev/null +++ b/cui/source/dialogs/SpellDialog.cxx @@ -0,0 +1,2179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include "SpellAttrib.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::linguistic2; +using namespace linguistic; + + +// struct SpellDialog_Impl --------------------------------------------- + +struct SpellDialog_Impl +{ + Sequence< Reference< XDictionary > > aDics; +}; + + +#define SPELLUNDO_START 200 + +#define SPELLUNDO_CHANGE_LANGUAGE (SPELLUNDO_START + 1) +#define SPELLUNDO_CHANGE_TEXTENGINE (SPELLUNDO_START + 2) +#define SPELLUNDO_CHANGE_NEXTERROR (SPELLUNDO_START + 3) +#define SPELLUNDO_CHANGE_ADD_TO_DICTIONARY (SPELLUNDO_START + 4) +#define SPELLUNDO_CHANGE_GROUP (SPELLUNDO_START + 5) //undo list +#define SPELLUNDO_MOVE_ERROREND (SPELLUNDO_START + 6) +#define SPELLUNDO_UNDO_EDIT_MODE (SPELLUNDO_START + 7) +#define SPELLUNDO_ADD_IGNORE_RULE (SPELLUNDO_START + 8) + +namespace svx{ +class SpellUndoAction_Impl : public SfxUndoAction +{ + sal_uInt16 m_nId; + const Link& m_rActionLink; + //undo of button enabling + bool m_bEnableChangePB; + bool m_bEnableChangeAllPB; + //undo of MarkNextError - used in change and change all, ignore and ignore all + tools::Long m_nOldErrorStart; + tools::Long m_nOldErrorEnd; + bool m_bIsErrorLanguageSelected; + //undo of AddToDictionary + Reference m_xDictionary; + OUString m_sAddedWord; + //move end of error - ::ChangeMarkedWord() + tools::Long m_nOffset; + +public: + SpellUndoAction_Impl(sal_uInt16 nId, const Link& rActionLink) : + m_nId(nId), + m_rActionLink( rActionLink), + m_bEnableChangePB(false), + m_bEnableChangeAllPB(false), + m_nOldErrorStart(-1), + m_nOldErrorEnd(-1), + m_bIsErrorLanguageSelected(false), + m_nOffset(0) + {} + + virtual void Undo() override; + sal_uInt16 GetId() const; + + void SetEnableChangePB(){m_bEnableChangePB = true;} + bool IsEnableChangePB() const {return m_bEnableChangePB;} + + void SetEnableChangeAllPB(){m_bEnableChangeAllPB = true;} + bool IsEnableChangeAllPB() const {return m_bEnableChangeAllPB;} + + void SetErrorMove(tools::Long nOldStart, tools::Long nOldEnd) + { + m_nOldErrorStart = nOldStart; + m_nOldErrorEnd = nOldEnd; + } + tools::Long GetOldErrorStart() const { return m_nOldErrorStart;} + tools::Long GetOldErrorEnd() const { return m_nOldErrorEnd;} + + void SetErrorLanguageSelected(bool bSet){ m_bIsErrorLanguageSelected = bSet;} + bool IsErrorLanguageSelected() const {return m_bIsErrorLanguageSelected;} + + void SetDictionary(const Reference& xDict) { m_xDictionary = xDict; } + const Reference& GetDictionary() const { return m_xDictionary; } + void SetAddedWord(const OUString& rWord) {m_sAddedWord = rWord;} + const OUString& GetAddedWord() const { return m_sAddedWord;} + + void SetOffset(tools::Long nSet) {m_nOffset = nSet;} + tools::Long GetOffset() const {return m_nOffset;} +}; +}//namespace svx +using namespace ::svx; + +void SpellUndoAction_Impl::Undo() +{ + m_rActionLink.Call(*this); +} + + +sal_uInt16 SpellUndoAction_Impl::GetId()const +{ + return m_nId; +} + +// class SvxSpellCheckDialog --------------------------------------------- + +SpellDialog::SpellDialog(SpellDialogChildWindow* pChildWindow, + weld::Window * pParent, SfxBindings* _pBindings) + : SfxModelessDialogController (_pBindings, pChildWindow, + pParent, "cui/ui/spellingdialog.ui", "SpellingDialog") + , aDialogUndoLink(LINK (this, SpellDialog, DialogUndoHdl)) + , m_pInitHdlEvent(nullptr) + , bFocusLocked(true) + , rParent(*pChildWindow) + , pImpl( new SpellDialog_Impl ) + , m_xAltTitle(m_xBuilder->weld_label("alttitleft")) + , m_xResumeFT(m_xBuilder->weld_label("resumeft")) + , m_xNoSuggestionsFT(m_xBuilder->weld_label("nosuggestionsft")) + , m_xLanguageFT(m_xBuilder->weld_label("languageft")) + , m_xLanguageLB(new SvxLanguageBox(m_xBuilder->weld_combo_box("languagelb"))) + , m_xExplainFT(m_xBuilder->weld_label("explain")) + , m_xExplainLink(m_xBuilder->weld_link_button("explainlink")) + , m_xNotInDictFT(m_xBuilder->weld_label("notindictft")) + , m_xSentenceED(new SentenceEditWindow_Impl(m_xBuilder->weld_scrolled_window("scrolledwindow", true))) + , m_xSuggestionFT(m_xBuilder->weld_label("suggestionsft")) + , m_xSuggestionLB(m_xBuilder->weld_tree_view("suggestionslb")) + , m_xIgnorePB(m_xBuilder->weld_button("ignore")) + , m_xIgnoreAllPB(m_xBuilder->weld_button("ignoreall")) + , m_xIgnoreRulePB(m_xBuilder->weld_button("ignorerule")) + , m_xAddToDictPB(m_xBuilder->weld_button("add")) + , m_xAddToDictMB(m_xBuilder->weld_menu_button("addmb")) + , m_xChangePB(m_xBuilder->weld_button("change")) + , m_xChangeAllPB(m_xBuilder->weld_button("changeall")) + , m_xAutoCorrPB(m_xBuilder->weld_button("autocorrect")) + , m_xCheckGrammarCB(m_xBuilder->weld_check_button("checkgrammar")) + , m_xOptionsPB(m_xBuilder->weld_button("options")) + , m_xUndoPB(m_xBuilder->weld_button("undo")) + , m_xClosePB(m_xBuilder->weld_button("close")) + , m_xToolbar(m_xBuilder->weld_toolbar("toolbar")) + , m_xSentenceEDWeld(new weld::CustomWeld(*m_xBuilder, "sentence", *m_xSentenceED)) +{ + m_xSentenceED->SetSpellDialog(this); + m_xSentenceED->Init(m_xToolbar.get()); + + m_sTitleSpellingGrammar = m_xDialog->get_title(); + m_sTitleSpelling = m_xAltTitle->get_label(); + + // fdo#68794 set initial title for cases where no text has been processed + // yet to show its language attributes + OUString sTitle = rParent.HasGrammarChecking() ? m_sTitleSpellingGrammar : m_sTitleSpelling; + m_xDialog->set_title(m_xDialog->strip_mnemonic(sTitle.replaceFirst("$LANGUAGE ($LOCATION)", ""))); + + m_sResumeST = m_xResumeFT->get_label(); + m_sNoSuggestionsST = m_xNoSuggestionsFT->strip_mnemonic(m_xNoSuggestionsFT->get_label()); + + Size aEdSize(m_xSuggestionLB->get_approximate_digit_width() * 60, + m_xSuggestionLB->get_height_rows(6)); + m_xSuggestionLB->set_size_request(aEdSize.Width(), -1); + m_sIgnoreOnceST = m_xIgnorePB->get_label(); + m_xAddToDictMB->set_help_id(m_xAddToDictPB->get_help_id()); + xSpell = LinguMgr::GetSpellChecker(); + + Init_Impl(); + + // disable controls if service is missing + m_xDialog->set_sensitive(xSpell.is()); + + //InitHdl wants to use virtual methods, so it + //can't be called during the ctor, so init + //it on next event cycle post-ctor + m_pInitHdlEvent = Application::PostUserEvent(LINK(this, SpellDialog, InitHdl)); +} + +SpellDialog::~SpellDialog() +{ + if (m_xOptionsDlg) + { + m_xOptionsDlg->response(RET_CANCEL); + m_xOptionsDlg.reset(); + } + + if (m_pInitHdlEvent) + Application::RemoveUserEvent(m_pInitHdlEvent); + if (pImpl) + { + // save possibly modified user-dictionaries + Reference< XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() ); + if (xDicList.is()) + SaveDictionaries( xDicList ); + + pImpl.reset(); + } +} + +void SpellDialog::Init_Impl() +{ + // initialize handler + m_xClosePB->connect_clicked(LINK( this, SpellDialog, CancelHdl ) ); + m_xChangePB->connect_clicked(LINK( this, SpellDialog, ChangeHdl ) ); + m_xChangeAllPB->connect_clicked(LINK( this, SpellDialog, ChangeAllHdl ) ); + m_xIgnorePB->connect_clicked(LINK( this, SpellDialog, IgnoreHdl ) ); + m_xIgnoreAllPB->connect_clicked(LINK( this, SpellDialog, IgnoreAllHdl ) ); + m_xIgnoreRulePB->connect_clicked(LINK( this, SpellDialog, IgnoreAllHdl ) ); + m_xUndoPB->connect_clicked(LINK( this, SpellDialog, UndoHdl ) ); + + m_xAutoCorrPB->connect_clicked( LINK( this, SpellDialog, ExtClickHdl ) ); + m_xCheckGrammarCB->connect_toggled( LINK( this, SpellDialog, CheckGrammarHdl )); + m_xOptionsPB->connect_clicked( LINK( this, SpellDialog, ExtClickHdl ) ); + + m_xSuggestionLB->connect_row_activated( LINK( this, SpellDialog, DoubleClickChangeHdl ) ); + + m_xSentenceED->SetModifyHdl(LINK ( this, SpellDialog, ModifyHdl) ); + + m_xAddToDictMB->connect_selected(LINK ( this, SpellDialog, AddToDictSelectHdl ) ); + m_xAddToDictPB->connect_clicked(LINK ( this, SpellDialog, AddToDictClickHdl ) ); + + m_xLanguageLB->connect_changed(LINK( this, SpellDialog, LanguageSelectHdl ) ); + + // initialize language ListBox + m_xLanguageLB->SetLanguageList(SvxLanguageListFlags::SPELL_USED, false, false, true); + + m_xSentenceED->ClearModifyFlag(); + LinguMgr::GetChangeAllList()->clear(); +} + +void SpellDialog::UpdateBoxes_Impl(bool bCallFromSelectHdl) +{ + sal_Int32 i; + m_xSuggestionLB->clear(); + + SpellErrorDescription aSpellErrorDescription; + bool bSpellErrorDescription = m_xSentenceED->GetAlternatives(aSpellErrorDescription); + + LanguageType nAltLanguage = LANGUAGE_NONE; + Sequence< OUString > aNewWords; + bool bIsGrammarError = false; + if( bSpellErrorDescription ) + { + nAltLanguage = LanguageTag::convertToLanguageType( aSpellErrorDescription.aLocale ); + aNewWords = aSpellErrorDescription.aSuggestions; + bIsGrammarError = aSpellErrorDescription.bIsGrammarError; + m_xExplainLink->set_uri( aSpellErrorDescription.sExplanationURL ); + m_xExplainFT->set_label( aSpellErrorDescription.sExplanation ); + } + if( bSpellErrorDescription && !aSpellErrorDescription.sDialogTitle.isEmpty() ) + { + // use this function to apply the correct image to be used... + SetTitle_Impl( nAltLanguage ); + // then change the title to the one to be actually used + m_xDialog->set_title(m_xDialog->strip_mnemonic(aSpellErrorDescription.sDialogTitle)); + } + else + SetTitle_Impl( nAltLanguage ); + if( !bCallFromSelectHdl ) + m_xLanguageLB->set_active_id( nAltLanguage ); + int nDicts = InitUserDicts(); + + // enter alternatives + const OUString *pNewWords = aNewWords.getConstArray(); + const sal_Int32 nSize = aNewWords.getLength(); + for ( i = 0; i < nSize; ++i ) + { + OUString aTmp( pNewWords[i] ); + if (m_xSuggestionLB->find_text(aTmp) == -1) + m_xSuggestionLB->append_text(aTmp); + } + if(!nSize) + m_xSuggestionLB->append_text(m_sNoSuggestionsST); + m_xAutoCorrPB->set_sensitive( nSize > 0 ); + + m_xSuggestionFT->set_sensitive(nSize > 0); + m_xSuggestionLB->set_sensitive(nSize > 0); + if( nSize ) + { + m_xSuggestionLB->select(0); + } + m_xChangePB->set_sensitive( nSize > 0); + m_xChangeAllPB->set_sensitive(nSize > 0); + bool bShowChangeAll = !bIsGrammarError; + m_xChangeAllPB->set_visible( bShowChangeAll ); + m_xExplainFT->set_visible( !bShowChangeAll ); + m_xLanguageLB->set_sensitive( bShowChangeAll ); + m_xIgnoreAllPB->set_visible( bShowChangeAll ); + + m_xAddToDictMB->set_visible( bShowChangeAll && nDicts > 1 && !comphelper::LibreOfficeKit::isActive()); + m_xAddToDictPB->set_visible( bShowChangeAll && nDicts <= 1 && !comphelper::LibreOfficeKit::isActive()); + m_xIgnoreRulePB->set_visible( !bShowChangeAll ); + m_xIgnoreRulePB->set_sensitive(bSpellErrorDescription && !aSpellErrorDescription.sRuleId.isEmpty()); + m_xAutoCorrPB->set_visible( bShowChangeAll && rParent.HasAutoCorrection() ); + + bool bOldShowGrammar = m_xCheckGrammarCB->get_visible(); + bool bOldShowExplain = m_xExplainLink->get_visible(); + + m_xCheckGrammarCB->set_visible(rParent.HasGrammarChecking()); + m_xExplainLink->set_visible(!m_xExplainLink->get_uri().isEmpty()); + if (m_xExplainFT->get_label().isEmpty()) + { + m_xExplainFT->hide(); + m_xExplainLink->hide(); + } + + if (bOldShowExplain != m_xExplainLink->get_visible() || bOldShowGrammar != m_xCheckGrammarCB->get_visible()) + m_xDialog->resize_to_request(); +} + +void SpellDialog::SpellContinue_Impl(std::unique_ptr* pGuard, bool bUseSavedSentence, bool bIgnoreCurrentError) +{ + //initially or after the last error of a sentence MarkNextError will fail + //then GetNextSentence() has to be called followed again by MarkNextError() + //MarkNextError is not initially called if the UndoEdit mode is active + bool bNextSentence = false; + if (!m_xSentenceED) + { + return; + } + + if(!((!m_xSentenceED->IsUndoEditMode() && m_xSentenceED->MarkNextError( bIgnoreCurrentError, xSpell )) || + ( bNextSentence = GetNextSentence_Impl(pGuard, bUseSavedSentence, m_xSentenceED->IsUndoEditMode()) && m_xSentenceED->MarkNextError( false, xSpell )))) + return; + + SpellErrorDescription aSpellErrorDescription; + bool bSpellErrorDescription = m_xSentenceED->GetAlternatives(aSpellErrorDescription); + if (bSpellErrorDescription) + { + UpdateBoxes_Impl(); + weld::Widget* aControls[] = + { + m_xNotInDictFT.get(), + m_xSentenceED->GetDrawingArea(), + m_xLanguageFT.get() + }; + for (weld::Widget* pWidget : aControls) + pWidget->set_sensitive(true); + } + if( bNextSentence ) + { + //remove undo if a new sentence is active + m_xSentenceED->ResetUndo(); + m_xUndoPB->set_sensitive(false); + } +} +/* Initialize, asynchronous to prevent virtual calls + from a constructor + */ +IMPL_LINK_NOARG( SpellDialog, InitHdl, void*, void) +{ + m_pInitHdlEvent = nullptr; + m_xDialog->freeze(); + //show or hide AutoCorrect depending on the modules abilities + m_xAutoCorrPB->set_visible(rParent.HasAutoCorrection()); + SpellContinue_Impl(nullptr); + m_xSentenceED->ResetUndo(); + m_xUndoPB->set_sensitive(false); + + // get current language + UpdateBoxes_Impl(); + + // fill dictionary PopupMenu + InitUserDicts(); + + LockFocusChanges(true); + if(m_xSentenceED->IsEnabled()) + m_xSentenceED->GrabFocus(); + else if( m_xChangePB->get_sensitive() ) + m_xChangePB->grab_focus(); + else if( m_xIgnorePB->get_sensitive() ) + m_xIgnorePB->grab_focus(); + else if( m_xClosePB->get_sensitive() ) + m_xClosePB->grab_focus(); + LockFocusChanges(false); + //show grammar CheckBox depending on the modules abilities + m_xCheckGrammarCB->set_active(rParent.IsGrammarChecking()); + m_xDialog->thaw(); +}; + +IMPL_LINK( SpellDialog, ExtClickHdl, weld::Button&, rBtn, void ) +{ + if (m_xOptionsPB.get() == &rBtn) + StartSpellOptDlg_Impl(); + else if (m_xAutoCorrPB.get() == &rBtn) + { + //get the currently selected wrong word + OUString sCurrentErrorText = m_xSentenceED->GetErrorText(); + //get the wrong word from the XSpellAlternative + SpellErrorDescription aSpellErrorDescription; + bool bSpellErrorDescription = m_xSentenceED->GetAlternatives(aSpellErrorDescription); + if (bSpellErrorDescription) + { + OUString sWrong(aSpellErrorDescription.sErrorText); + //if the word has not been edited in the MultiLineEdit then + //the current suggestion should be used + //if it's not the 'no suggestions' entry + if(sWrong == sCurrentErrorText && + m_xSuggestionLB->get_sensitive() && m_xSuggestionLB->get_selected_index() != -1 && + m_sNoSuggestionsST != m_xSuggestionLB->get_selected_text()) + { + sCurrentErrorText = m_xSuggestionLB->get_selected_text(); + } + if(sWrong != sCurrentErrorText) + { + SvxPrepareAutoCorrect( sWrong, sCurrentErrorText ); + LanguageType eLang = GetSelectedLang_Impl(); + rParent.AddAutoCorrection( sWrong, sCurrentErrorText, eLang ); + //correct the word immediately + ChangeHdl(*m_xAutoCorrPB); + } + } + } +} + +IMPL_LINK_NOARG(SpellDialog, CheckGrammarHdl, weld::Toggleable&, void) +{ + rParent.SetGrammarChecking(m_xCheckGrammarCB->get_active()); + Impl_Restore(true); +} + +void SpellDialog::StartSpellOptDlg_Impl() +{ + auto xSet = std::make_shared>( SfxGetpApp()->GetPool() ); + m_xOptionsDlg = std::make_shared( + m_xDialog.get(), xSet.get(), "content", "cui/ui/spelloptionsdialog.ui", "SpellOptionsDialog"); + + std::unique_ptr xPage = SvxLinguTabPage::Create(m_xOptionsDlg->get_content_area(), m_xOptionsDlg.get(), xSet.get()); + static_cast(xPage.get())->HideGroups( GROUP_MODULES ); + m_xOptionsDlg->SetTabPage(std::move(xPage)); + weld::GenericDialogController::runAsync(m_xOptionsDlg, [this, xSet] (sal_uInt32 nResult) { + if (RET_OK == nResult) + { + InitUserDicts(); + const SfxItemSet* pOutSet = m_xOptionsDlg->GetOutputItemSet(); + if(pOutSet) + OfaTreeOptionsDialog::ApplyLanguageOptions(*pOutSet); + } + }); +} + +namespace +{ + OUString getDotReplacementString(const OUString &rErrorText, const OUString &rSuggestedReplacement) + { + OUString aString = rErrorText; + + //dots are sometimes part of the spelled word but they are not necessarily part of the replacement + bool bDot = aString.endsWith("."); + + aString = rSuggestedReplacement; + + if(bDot && (aString.isEmpty() || !aString.endsWith("."))) + aString += "."; + + return aString; + } +} + +OUString SpellDialog::getReplacementString() const +{ + OUString sOrigString = m_xSentenceED->GetErrorText(); + + OUString sReplacement(sOrigString); + + if(m_xSuggestionLB->get_sensitive() && + m_xSuggestionLB->get_selected_index() != -1 && + m_sNoSuggestionsST != m_xSuggestionLB->get_selected_text()) + sReplacement = m_xSuggestionLB->get_selected_text(); + + return getDotReplacementString(sOrigString, sReplacement); +} + +IMPL_LINK_NOARG(SpellDialog, DoubleClickChangeHdl, weld::TreeView&, bool) +{ + ChangeHdl(*m_xChangePB); + return true; +} + +/* tdf#132822 start an undo group in ctor and close it in the dtor. This can + then be passed to SpellContinue_Impl which can delete it in advance of its + natural scope to force closing the undo group if SpellContinue_Impl needs to + fetch a new paragraph and discard all undo information which can only be + done properly if there are no open undo groups */ +class UndoChangeGroupGuard +{ +private: + SentenceEditWindow_Impl& m_rSentenceED; +public: + UndoChangeGroupGuard(SentenceEditWindow_Impl& rSentenceED) + : m_rSentenceED(rSentenceED) + { + m_rSentenceED.UndoActionStart(SPELLUNDO_CHANGE_GROUP); + } + ~UndoChangeGroupGuard() + { + m_rSentenceED.UndoActionEnd(); + } +}; + +IMPL_LINK_NOARG(SpellDialog, ChangeHdl, weld::Button&, void) +{ + if (m_xSentenceED->IsUndoEditMode()) + { + SpellContinue_Impl(); + } + else + { + auto xGuard(std::make_unique(*m_xSentenceED)); + OUString aString = getReplacementString(); + m_xSentenceED->ChangeMarkedWord(aString, GetSelectedLang_Impl()); + SpellContinue_Impl(&xGuard); + } + if(!m_xChangePB->get_sensitive()) + m_xIgnorePB->grab_focus(); +} + +IMPL_LINK_NOARG(SpellDialog, ChangeAllHdl, weld::Button&, void) +{ + auto xGuard(std::make_unique(*m_xSentenceED)); + OUString aString = getReplacementString(); + LanguageType eLang = GetSelectedLang_Impl(); + + // add new word to ChangeAll list + OUString aOldWord( m_xSentenceED->GetErrorText() ); + SvxPrepareAutoCorrect( aOldWord, aString ); + Reference aXDictionary = LinguMgr::GetChangeAllList(); + DictionaryError nAdded = AddEntryToDic( aXDictionary, + aOldWord, true, + aString ); + + if(nAdded == DictionaryError::NONE) + { + std::unique_ptr pAction(new SpellUndoAction_Impl( + SPELLUNDO_CHANGE_ADD_TO_DICTIONARY, aDialogUndoLink)); + pAction->SetDictionary(aXDictionary); + pAction->SetAddedWord(aOldWord); + m_xSentenceED->AddUndoAction(std::move(pAction)); + } + + m_xSentenceED->ChangeMarkedWord(aString, eLang); + SpellContinue_Impl(&xGuard); +} + +IMPL_LINK( SpellDialog, IgnoreAllHdl, weld::Button&, rButton, void ) +{ + auto xGuard(std::make_unique(*m_xSentenceED)); + // add word to IgnoreAll list + Reference< XDictionary > aXDictionary = LinguMgr::GetIgnoreAllList(); + //in case the error has been changed manually it has to be restored + m_xSentenceED->RestoreCurrentError(); + if (&rButton == m_xIgnoreRulePB.get()) + { + SpellErrorDescription aSpellErrorDescription; + bool bSpellErrorDescription = m_xSentenceED->GetAlternatives(aSpellErrorDescription); + try + { + if( bSpellErrorDescription && aSpellErrorDescription.xGrammarChecker.is() ) + { + aSpellErrorDescription.xGrammarChecker->ignoreRule(aSpellErrorDescription.sRuleId, + aSpellErrorDescription.aLocale); + // refresh the layout (workaround to launch a dictionary event) + aXDictionary->setActive(false); + aXDictionary->setActive(true); + } + } + catch( const uno::Exception& ) + { + } + } + else + { + OUString sErrorText(m_xSentenceED->GetErrorText()); + DictionaryError nAdded = AddEntryToDic( aXDictionary, + sErrorText, false, + OUString() ); + if (nAdded == DictionaryError::NONE) + { + std::unique_ptr pAction(new SpellUndoAction_Impl( + SPELLUNDO_CHANGE_ADD_TO_DICTIONARY, aDialogUndoLink)); + pAction->SetDictionary(aXDictionary); + pAction->SetAddedWord(sErrorText); + m_xSentenceED->AddUndoAction(std::move(pAction)); + } + } + + SpellContinue_Impl(&xGuard); +} + +IMPL_LINK_NOARG(SpellDialog, UndoHdl, weld::Button&, void) +{ + m_xSentenceED->Undo(); + if(!m_xSentenceED->GetUndoActionCount()) + m_xUndoPB->set_sensitive(false); +} + + +IMPL_LINK( SpellDialog, DialogUndoHdl, SpellUndoAction_Impl&, rAction, void ) +{ + switch(rAction.GetId()) + { + case SPELLUNDO_CHANGE_TEXTENGINE: + { + if(rAction.IsEnableChangePB()) + m_xChangePB->set_sensitive(false); + if(rAction.IsEnableChangeAllPB()) + m_xChangeAllPB->set_sensitive(false); + } + break; + case SPELLUNDO_CHANGE_NEXTERROR: + { + m_xSentenceED->MoveErrorMarkTo(static_cast(rAction.GetOldErrorStart()), + static_cast(rAction.GetOldErrorEnd()), + false); + if(rAction.IsErrorLanguageSelected()) + { + UpdateBoxes_Impl(); + } + } + break; + case SPELLUNDO_CHANGE_ADD_TO_DICTIONARY: + { + if(rAction.GetDictionary().is()) + rAction.GetDictionary()->remove(rAction.GetAddedWord()); + } + break; + case SPELLUNDO_MOVE_ERROREND : + { + if(rAction.GetOffset() != 0) + m_xSentenceED->MoveErrorEnd(rAction.GetOffset()); + } + break; + case SPELLUNDO_UNDO_EDIT_MODE : + { + //refill the dialog with the currently spelled sentence - throw away all changes + SpellContinue_Impl(nullptr, true); + } + break; + case SPELLUNDO_ADD_IGNORE_RULE: + //undo of ignored rules is not supported + break; + } +} + +void SpellDialog::Impl_Restore(bool bUseSavedSentence) +{ + //clear the "ChangeAllList" + LinguMgr::GetChangeAllList()->clear(); + //get a new sentence + m_xSentenceED->SetText(OUString()); + m_xSentenceED->ResetModified(); + //Resolves: fdo#39348 refill the dialog with the currently spelled sentence + SpellContinue_Impl(nullptr, bUseSavedSentence); + m_xIgnorePB->set_label(m_sIgnoreOnceST); +} + +IMPL_LINK_NOARG(SpellDialog, IgnoreHdl, weld::Button&, void) +{ + if (m_sResumeST == m_xIgnorePB->get_label()) + { + Impl_Restore(false); + } + else + { + //in case the error has been changed manually it has to be restored, + // since the users choice now was to ignore the error + m_xSentenceED->RestoreCurrentError(); + + // the word is being ignored + SpellContinue_Impl(nullptr, false, true); + } +} + +void SpellDialog::Close() +{ + if (IsClosing()) + return; + + // We have to call ToggleChildWindow directly; calling SfxDispatcher's + // Execute() does not work here when we are in a document with protected + // section - in that case, the cursor can move from the editable field to + // the protected area, and the slots get disabled because of + // SfxDisableFlags::SwOnProtectedCursor (see FN_SPELL_GRAMMAR_DIALOG in .sdi). + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + pViewFrame->ToggleChildWindow(rParent.GetType()); +} + +LanguageType SpellDialog::GetSelectedLang_Impl() const +{ + LanguageType nLang = m_xLanguageLB->get_active_id(); + return nLang; +} + +IMPL_LINK_NOARG(SpellDialog, LanguageSelectHdl, weld::ComboBox&, void) +{ + //If selected language changes, then add->list should be regenerated to + //match + InitUserDicts(); + + //if currently an error is selected then search for alternatives for + //this word and fill the alternatives ListBox accordingly + OUString sError = m_xSentenceED->GetErrorText(); + m_xSuggestionLB->clear(); + if (!sError.isEmpty()) + { + LanguageType eLanguage = m_xLanguageLB->get_active_id(); + Reference xAlt = xSpell->spell( sError, static_cast(eLanguage), + Sequence< PropertyValue >() ); + if( xAlt.is() ) + m_xSentenceED->SetAlternatives( xAlt ); + else + { + m_xSentenceED->ChangeMarkedWord( sError, eLanguage ); + SpellContinue_Impl(); + } + + m_xSentenceED->AddUndoAction(std::make_unique(SPELLUNDO_CHANGE_LANGUAGE, aDialogUndoLink)); + } + SpellDialog::UpdateBoxes_Impl(true); +} + +void SpellDialog::SetTitle_Impl(LanguageType nLang) +{ + OUString sTitle = rParent.HasGrammarChecking() ? m_sTitleSpellingGrammar : m_sTitleSpelling; + sTitle = sTitle.replaceFirst( "$LANGUAGE ($LOCATION)", SvtLanguageTable::GetLanguageString(nLang) ); + m_xDialog->set_title(m_xDialog->strip_mnemonic(sTitle)); +} + +int SpellDialog::InitUserDicts() +{ + const LanguageType nLang = m_xLanguageLB->get_active_id(); + + const Reference< XDictionary > *pDic = nullptr; + + // get list of dictionaries + Reference< XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() ); + if (xDicList.is()) + { + // add active, positive dictionary to dic-list (if not already done). + // This is to ensure that there is at least on dictionary to which + // words could be added. + Reference< XDictionary > xDic( LinguMgr::GetStandardDic() ); + if (xDic.is()) + xDic->setActive( true ); + + pImpl->aDics = xDicList->getDictionaries(); + } + + SvtLinguConfig aCfg; + + // list suitable dictionaries + bool bEnable = false; + const sal_Int32 nSize = pImpl->aDics.getLength(); + pDic = pImpl->aDics.getConstArray(); + m_xAddToDictMB->clear(); + sal_uInt16 nItemId = 1; // menu items should be enumerated from 1 and not 0 + for (sal_Int32 i = 0; i < nSize; ++i) + { + uno::Reference< linguistic2::XDictionary > xDicTmp = pDic[i]; + if (!xDicTmp.is() || LinguMgr::GetIgnoreAllList() == xDicTmp) + continue; + + uno::Reference< frame::XStorable > xStor( xDicTmp, uno::UNO_QUERY ); + LanguageType nActLanguage = LanguageTag( xDicTmp->getLocale() ).getLanguageType(); + if( xDicTmp->isActive() + && xDicTmp->getDictionaryType() != linguistic2::DictionaryType_NEGATIVE + && (nLang == nActLanguage || LANGUAGE_NONE == nActLanguage ) + && (!xStor.is() || !xStor->isReadonly()) ) + { + bEnable = true; + + OUString aDictionaryImageUrl; + uno::Reference< lang::XServiceInfo > xSvcInfo( xDicTmp, uno::UNO_QUERY ); + if (xSvcInfo.is()) + { + aDictionaryImageUrl = aCfg.GetSpellAndGrammarContextDictionaryImage( + xSvcInfo->getImplementationName()); + } + + m_xAddToDictMB->append_item(OUString::number(nItemId), xDicTmp->getName(), aDictionaryImageUrl); + + ++nItemId; + } + } + m_xAddToDictMB->set_sensitive( bEnable ); + m_xAddToDictPB->set_sensitive( bEnable ); + + int nDicts = nItemId-1; + + m_xAddToDictMB->set_visible(nDicts > 1 && !comphelper::LibreOfficeKit::isActive()); + m_xAddToDictPB->set_visible(nDicts <= 1 && !comphelper::LibreOfficeKit::isActive()); + + return nDicts; +} + +IMPL_LINK_NOARG(SpellDialog, AddToDictClickHdl, weld::Button&, void) +{ + AddToDictionaryExecute(OUString::number(1)); +} + +IMPL_LINK(SpellDialog, AddToDictSelectHdl, const OUString&, rIdent, void) +{ + AddToDictionaryExecute(rIdent); +} + +void SpellDialog::AddToDictionaryExecute(const OUString& rItemId) +{ + auto xGuard(std::make_unique(*m_xSentenceED)); + + //GetErrorText() returns the current error even if the text is already + //manually changed + const OUString aNewWord = m_xSentenceED->GetErrorText(); + + OUString aDicName(m_xAddToDictMB->get_item_label(rItemId)); + + uno::Reference< linguistic2::XDictionary > xDic; + uno::Reference< linguistic2::XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() ); + if (xDicList.is()) + xDic = xDicList->getDictionaryByName( aDicName ); + + DictionaryError nAddRes = DictionaryError::UNKNOWN; + if (xDic.is()) + { + nAddRes = AddEntryToDic( xDic, aNewWord, false, OUString() ); + // save modified user-dictionary if it is persistent + uno::Reference< frame::XStorable > xSavDic( xDic, uno::UNO_QUERY ); + if (xSavDic.is()) + xSavDic->store(); + + if (nAddRes == DictionaryError::NONE) + { + std::unique_ptr pAction(new SpellUndoAction_Impl( + SPELLUNDO_CHANGE_ADD_TO_DICTIONARY, aDialogUndoLink)); + pAction->SetDictionary( xDic ); + pAction->SetAddedWord( aNewWord ); + m_xSentenceED->AddUndoAction( std::move(pAction) ); + } + // failed because there is already an entry? + if (DictionaryError::NONE != nAddRes && xDic->getEntry( aNewWord ).is()) + nAddRes = DictionaryError::NONE; + } + if (DictionaryError::NONE != nAddRes) + { + SvxDicError(m_xDialog.get(), nAddRes); + return; // don't continue + } + + // go on + SpellContinue_Impl(&xGuard); +} + +IMPL_LINK_NOARG(SpellDialog, ModifyHdl, LinkParamNone*, void) +{ + m_xSuggestionLB->unselect_all(); + m_xSuggestionLB->set_sensitive(false); + m_xAutoCorrPB->set_sensitive(false); + std::unique_ptr pSpellAction(new SpellUndoAction_Impl(SPELLUNDO_CHANGE_TEXTENGINE, aDialogUndoLink)); + if(!m_xChangeAllPB->get_sensitive()) + { + m_xChangeAllPB->set_sensitive(true); + pSpellAction->SetEnableChangeAllPB(); + } + if(!m_xChangePB->get_sensitive()) + { + m_xChangePB->set_sensitive(true); + pSpellAction->SetEnableChangePB(); + } + m_xSentenceED->AddUndoAction(std::move(pSpellAction)); +} + +IMPL_LINK_NOARG(SpellDialog, CancelHdl, weld::Button&, void) +{ + //apply changes and ignored text parts first - if there are any + if (m_xSentenceED->IsModified()) + { + rParent.ApplyChangedSentence(m_xSentenceED->CreateSpellPortions(), false); + } + Close(); +} + +void SpellDialog::ToplevelFocusChanged() +{ + /* #i38338# + * FIXME: LoseFocus and GetFocus are signals from vcl that + * a window actually got/lost the focus, it never should be + * forwarded from another window, that is simply wrong. + * FIXME: overriding the virtual methods GetFocus and LoseFocus + * in SpellDialogChildWindow by making them pure is at least questionable. + * The only sensible thing would be to call the new Method differently, + * e.g. DialogGot/LostFocus or so. + */ + if (!m_xDialog->get_visible() || bFocusLocked) + return; + + if (m_xDialog->has_toplevel_focus()) + { + //notify the child window of the focus change + rParent.GetFocus(); + } + else + { + //notify the child window of the focus change + rParent.LoseFocus(); + } +} + +void SpellDialog::Activate() +{ + SfxModelessDialogController::Activate(); + ToplevelFocusChanged(); +} + +void SpellDialog::Deactivate() +{ + SfxModelessDialogController::Deactivate(); + ToplevelFocusChanged(); +} + +void SpellDialog::InvalidateDialog() +{ + if( bFocusLocked ) + return; + m_xIgnorePB->set_label(m_sResumeST); + weld::Widget* aDisableArr[] = + { + m_xNotInDictFT.get(), + m_xSentenceED->GetDrawingArea(), + m_xSuggestionFT.get(), + m_xSuggestionLB.get(), + m_xLanguageFT.get(), + m_xLanguageLB->get_widget(), + m_xIgnoreAllPB.get(), + m_xIgnoreRulePB.get(), + m_xAddToDictMB.get(), + m_xAddToDictPB.get(), + m_xChangePB.get(), + m_xChangeAllPB.get(), + m_xAutoCorrPB.get(), + m_xUndoPB.get() + }; + for (weld::Widget* pWidget : aDisableArr) + pWidget->set_sensitive(false); + + SfxModelessDialogController::Deactivate(); +} + +bool SpellDialog::GetNextSentence_Impl(std::unique_ptr* pGuard, bool bUseSavedSentence, bool bRecheck) +{ + bool bRet = false; + if(!bUseSavedSentence) + { + //apply changes and ignored text parts + rParent.ApplyChangedSentence(m_xSentenceED->CreateSpellPortions(), bRecheck); + } + m_xSentenceED->ResetIgnoreErrorsAt(); + m_xSentenceED->ResetModified(); + SpellPortions aSentence = bUseSavedSentence ? m_aSavedSentence : rParent.GetNextWrongSentence( bRecheck ); + if(!bUseSavedSentence) + m_aSavedSentence = aSentence; + bool bHasReplaced = false; + while(!aSentence.empty()) + { + //apply all changes that are already part of the "ChangeAllList" + //returns true if the list still contains errors after the changes have been applied + + if(!ApplyChangeAllList_Impl(aSentence, bHasReplaced)) + { + rParent.ApplyChangedSentence(aSentence, bRecheck); + aSentence = rParent.GetNextWrongSentence( bRecheck ); + } + else + break; + } + + if(!aSentence.empty()) + { + OUStringBuffer sText; + for (auto const& elem : aSentence) + { + // hidden text has to be ignored + if(!elem.bIsHidden) + sText.append(elem.sText); + } + // tdf#132822 fire undo-stack UndoActionEnd to close undo stack because we're about to throw away the paragraph entirely + if (pGuard) + pGuard->reset(); + m_xSentenceED->SetText(sText.makeStringAndClear()); + sal_Int32 nStartPosition = 0; + sal_Int32 nEndPosition = 0; + + for (auto const& elem : aSentence) + { + // hidden text has to be ignored + if(!elem.bIsHidden) + { + nEndPosition += elem.sText.getLength(); + if(elem.xAlternatives.is()) + { + SpellErrorDescription aDesc( false, elem.xAlternatives->getWord(), + elem.xAlternatives->getLocale(), elem.xAlternatives->getAlternatives(), nullptr); + SfxGrabBagItem aSpellErrorDescription(EE_CHAR_GRABBAG); + aSpellErrorDescription.GetGrabBag()["SpellErrorDescription"] <<= aDesc.toSequence(); + m_xSentenceED->SetAttrib(aSpellErrorDescription, nStartPosition, nEndPosition); + } + else if(elem.bIsGrammarError ) + { + beans::PropertyValues aProperties = elem.aGrammarError.aProperties; + OUString sFullCommentURL; + sal_Int32 i = 0; + while ( sFullCommentURL.isEmpty() && i < aProperties.getLength() ) + { + if ( aProperties[i].Name == "FullCommentURL" ) + { + uno::Any aValue = aProperties[i].Value; + aValue >>= sFullCommentURL; + } + ++i; + } + + SpellErrorDescription aDesc( true, + elem.sText, + LanguageTag::convertToLocale( elem.eLanguage ), + elem.aGrammarError.aSuggestions, + elem.xGrammarChecker, + &elem.sDialogTitle, + &elem.aGrammarError.aFullComment, + &elem.aGrammarError.aRuleIdentifier, + &sFullCommentURL ); + + SfxGrabBagItem aSpellErrorDescriptionItem(EE_CHAR_GRABBAG); + aSpellErrorDescriptionItem.GetGrabBag()["SpellErrorDescription"] <<= aDesc.toSequence(); + m_xSentenceED->SetAttrib(aSpellErrorDescriptionItem, nStartPosition, nEndPosition); + } + + if (elem.bIsField) + m_xSentenceED->SetAttrib(SvxColorItem(COL_LIGHTGRAY, EE_CHAR_BKGCOLOR), nStartPosition, nEndPosition); + m_xSentenceED->SetAttrib(SvxLanguageItem(elem.eLanguage, EE_CHAR_LANGUAGE), nStartPosition, nEndPosition); + nStartPosition = nEndPosition; + } + } + //the edit field needs to be modified to apply the change from the ApplyChangeAllList + if(!bHasReplaced) + m_xSentenceED->ClearModifyFlag(); + m_xSentenceED->ResetUndo(); + m_xUndoPB->set_sensitive(false); + bRet = nStartPosition > 0; + } + return bRet; +} +/*------------------------------------------------------------------------- + replace errors that have a replacement in the ChangeAllList + returns false if the result doesn't contain errors after the replacement + -----------------------------------------------------------------------*/ +bool SpellDialog::ApplyChangeAllList_Impl(SpellPortions& rSentence, bool &bHasReplaced) +{ + bHasReplaced = false; + bool bRet = true; + Reference xChangeAll = LinguMgr::GetChangeAllList(); + if(!xChangeAll->getCount()) + return bRet; + bRet = false; + for (auto & elem : rSentence) + { + if(elem.xAlternatives.is()) + { + const OUString &rString = elem.sText; + + Reference xEntry = xChangeAll->getEntry(rString); + + if(xEntry.is()) + { + elem.sText = getDotReplacementString(rString, xEntry->getReplacementText()); + elem.xAlternatives = nullptr; + bHasReplaced = true; + } + else + bRet = true; + } + else if( elem.bIsGrammarError ) + bRet = true; + } + return bRet; +} + +SentenceEditWindow_Impl::SentenceEditWindow_Impl(std::unique_ptr xScrolledWindow) + : m_xScrolledWindow(std::move(xScrolledWindow)) + , m_pSpellDialog(nullptr) + , m_pToolbar(nullptr) + , m_nErrorStart(0) + , m_nErrorEnd(0) + , m_bIsUndoEditMode(false) +{ + m_xScrolledWindow->connect_vadjustment_changed(LINK(this, SentenceEditWindow_Impl, ScrollHdl)); +} + +void SentenceEditWindow_Impl::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aSize(pDrawingArea->get_approximate_digit_width() * 60, + pDrawingArea->get_text_height() * 6); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + WeldEditView::SetDrawingArea(pDrawingArea); + // tdf#132288 don't merge equal adjacent attributes + m_xEditEngine->DisableAttributeExpanding(); + + m_xEditEngine->SetStatusEventHdl(LINK(this, SentenceEditWindow_Impl, EditStatusHdl)); + + // tdf#142631 use document background color in this widget + Color aBgColor = svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + rDevice.SetBackground(aBgColor); + m_xEditView->SetBackgroundColor(aBgColor); + m_xEditEngine->SetBackgroundColor(aBgColor); +} + +IMPL_LINK_NOARG(SentenceEditWindow_Impl, EditStatusHdl, EditStatus&, void) +{ + SetScrollBarRange(); + DoScroll(); +} + +IMPL_LINK_NOARG(SentenceEditWindow_Impl, ScrollHdl, weld::ScrolledWindow&, void) +{ + DoScroll(); +} + +void SentenceEditWindow_Impl::DoScroll() +{ + if (m_xEditView) + { + auto currentDocPos = m_xEditView->GetVisArea().Top(); + auto nDiff = currentDocPos - m_xScrolledWindow->vadjustment_get_value(); + // we expect SetScrollBarRange callback to be triggered by Scroll + // to set where we ended up + m_xEditView->Scroll(0, nDiff); + } +} + +void SentenceEditWindow_Impl::EditViewScrollStateChange() +{ + // editengine height has changed or editview scroll pos has changed + SetScrollBarRange(); +} + +void SentenceEditWindow_Impl::SetScrollBarRange() +{ + EditEngine *pEditEngine = GetEditEngine(); + if (!pEditEngine) + return; + if (!m_xScrolledWindow) + return; + EditView* pEditView = GetEditView(); + if (!pEditView) + return; + + int nVUpper = pEditEngine->GetTextHeight(); + int nVCurrentDocPos = pEditView->GetVisArea().Top(); + const Size aOut(pEditView->GetOutputArea().GetSize()); + int nVStepIncrement = aOut.Height() * 2 / 10; + int nVPageIncrement = aOut.Height() * 8 / 10; + int nVPageSize = aOut.Height(); + + /* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has + effectively... + + lower = gtk_adjustment_get_lower + upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size + + and requires that upper > lower or the deceleration animation never ends + */ + nVPageSize = std::min(nVPageSize, nVUpper); + + m_xScrolledWindow->vadjustment_configure(nVCurrentDocPos, 0, nVUpper, + nVStepIncrement, nVPageIncrement, nVPageSize); + m_xScrolledWindow->set_vpolicy(nVUpper > nVPageSize ? VclPolicyType::ALWAYS : VclPolicyType::NEVER); +} + +SentenceEditWindow_Impl::~SentenceEditWindow_Impl() +{ +} + +namespace +{ + const EECharAttrib* FindCharAttrib(int nPosition, sal_uInt16 nWhich, std::vector& rAttribList) + { + for (auto it = rAttribList.rbegin(); it != rAttribList.rend(); ++it) + { + const auto& rTextAtr = *it; + if (rTextAtr.pAttr->Which() != nWhich) + continue; + if (rTextAtr.nStart <= nPosition && rTextAtr.nEnd >= nPosition) + { + return &rTextAtr; + } + } + + return nullptr; + } + + void ExtractErrorDescription(const EECharAttrib& rEECharAttrib, SpellErrorDescription& rSpellErrorDescription) + { + css::uno::Sequence aSequence; + const auto pGrabBag = static_cast(rEECharAttrib.pAttr)->GetGrabBag(); + const auto iter = pGrabBag.find("SpellErrorDescription"); + assert(iter != pGrabBag.end()); + iter->second >>= aSequence; + rSpellErrorDescription.fromSequence(aSequence); + } +} + +/*------------------------------------------------------------------------- + The selection before inputting a key may have a range or not + and it may be inside or outside of field or error attributes. + A range may include the attribute partially, completely or together + with surrounding text. It may also contain more than one attribute + or no attribute at all. + Depending on this starting conditions some actions are necessary: + Attempts to delete a field are only allowed if the selection is the same + as the field's selection. Otherwise the field has to be selected and the key + input action has to be skipped. + Input of text at the start of the field requires the field attribute to be + corrected - it is not allowed to grow. + + In case of errors the appending of text should grow the error attribute because + that is what the user usually wants to do. + + Backspace at the start of the attribute requires to find out if a field ends + directly in front of the cursor position. In case of a field this attribute has to be + selected otherwise the key input method is allowed. + + All changes outside of the error attributes switch the dialog mode to a "Undo edit" state that + removes all visible attributes and switches off further attribute checks. + Undo in this restarts the dialog with a current sentence newly presented. + All changes to the sentence are undone including the ones before the "Undo edit state" has been reached + + We end up with 9 types of selection + 1 (LEFT_NO) - no range, start of attribute - can also be 3 at the same time + 2 (INSIDE_NO) - no range, inside of attribute + 3 (RIGHT_NO) - no range, end of attribute - can also be 1 at the same time + 4 (FULL) - range, same as attribute + 5 (INSIDE_YES) - range, inside of the attribute + 6 (BRACE)- range, from outside of the attribute to the inside or + including the complete attribute and something outside, + maybe more than one attribute + 7 (OUTSIDE_NO) - no range, not at an attribute + 8 (OUTSIDE_YES) - range, completely outside of all attributes + + What has to be done depending on the attribute type involved + possible actions: UE - Undo edit mode + CO - Continue, no additional action is required + FS - Field has to be completely selected + EX - The attribute has to be expanded to include the added text + + 1 - backspace delete any other + UE on field FS on error CO on field FS on error CO + + 2 - on field FS on error C + 3 - backspace delete any other + on field FS on error CO UE on field UE on error EX + + if 1 and 3 happen to apply both then backspace and other handling is 1 delete is 3 + + 4 - on field UE and on error CO + 5 - on field FS and on error CO + 6 - on field FS and on error UE + 7 - UE + 8 - UE + -----------------------------------------------------------------------*/ +#define INVALID 0 +#define LEFT_NO 1 +#define INSIDE_NO 2 +#define RIGHT_NO 3 +#define FULL 4 +#define INSIDE_YES 5 +#define BRACE 6 +#define OUTSIDE_NO 7 +#define OUTSIDE_YES 8 + +#define ACTION_UNDOEDIT 0 +#define ACTION_CONTINUE 1 +#define ACTION_SELECTFIELD 2 +#define ACTION_EXPAND 3 + +bool SentenceEditWindow_Impl::KeyInput(const KeyEvent& rKeyEvt) +{ + if (rKeyEvt.GetKeyCode().GetCode() == KEY_TAB) + return false; + + bool bConsumed = false; + + bool bChange = TextEngine::DoesKeyChangeText( rKeyEvt ); + if (bChange && !IsUndoEditMode()) + { + bConsumed = true; + + ESelection aCurrentSelection(m_xEditView->GetSelection()); + aCurrentSelection.Adjust(); + + //determine if the selection contains a field + bool bHasFieldLeft = false; + bool bHasErrorLeft = false; + + bool bHasRange = aCurrentSelection.HasRange(); + sal_uInt8 nSelectionType = 0; // invalid type! + + std::vector aAttribList; + m_xEditEngine->GetCharAttribs(0, aAttribList); + + auto nCursor = aCurrentSelection.nStartPos; + const EECharAttrib* pBackAttr = FindCharAttrib(nCursor, EE_CHAR_BKGCOLOR, aAttribList); + const EECharAttrib* pErrorAttr = FindCharAttrib(nCursor, EE_CHAR_GRABBAG, aAttribList); + const EECharAttrib* pBackAttrLeft = nullptr; + const EECharAttrib* pErrorAttrLeft = nullptr; + + bool bHasField = pBackAttr != nullptr && (bHasRange || pBackAttr->nEnd > nCursor); + bool bHasError = pErrorAttr != nullptr && (bHasRange || pErrorAttr->nEnd > nCursor); + if (bHasRange) + { + if (pBackAttr && + pBackAttr->nStart == aCurrentSelection.nStartPos && + pBackAttr->nEnd == aCurrentSelection.nEndPos) + { + nSelectionType = FULL; + } + else if (pErrorAttr && + pErrorAttr->nStart <= aCurrentSelection.nStartPos && + pErrorAttr->nEnd >= aCurrentSelection.nEndPos) + { + nSelectionType = INSIDE_YES; + } + else + { + nSelectionType = bHasField||bHasError ? BRACE : OUTSIDE_NO; + while (nCursor < aCurrentSelection.nEndPos) + { + ++nCursor; + const EECharAttrib* pIntBackAttr = FindCharAttrib(nCursor, EE_CHAR_BKGCOLOR, aAttribList); + const EECharAttrib* pIntErrorAttr = FindCharAttrib(nCursor, EE_CHAR_GRABBAG, aAttribList); + //if any attr has been found then BRACE + if (pIntBackAttr || pIntErrorAttr) + nSelectionType = BRACE; + //the field has to be selected + if (pIntBackAttr && !pBackAttr) + pBackAttr = pIntBackAttr; + bHasField |= pIntBackAttr != nullptr; + } + } + } + else + { + //no range selection: then 1 2 3 and 8 are possible + const EECharAttrib* pCurAttr = pBackAttr ? pBackAttr : pErrorAttr; + if (pCurAttr) + { + nSelectionType = pCurAttr->nStart == aCurrentSelection.nStartPos ? + LEFT_NO : pCurAttr->nEnd == aCurrentSelection.nEndPos ? RIGHT_NO : INSIDE_NO; + } + else + nSelectionType = OUTSIDE_NO; + + bHasFieldLeft = pBackAttr && pBackAttr->nEnd == nCursor; + if(bHasFieldLeft) + { + pBackAttrLeft = pBackAttr; + pBackAttr = nullptr; + } + bHasErrorLeft = pErrorAttr && pErrorAttr->nEnd == nCursor; + if(bHasErrorLeft) + { + pErrorAttrLeft = pErrorAttr; + pErrorAttr = nullptr; + } + + //check previous position if this exists + //that is a redundant in the case the attribute found above already is on the left cursor side + //but it's o.k. for two errors/fields side by side + if (nCursor) + { + --nCursor; + pBackAttrLeft = FindCharAttrib(nCursor, EE_CHAR_BKGCOLOR, aAttribList); + pErrorAttrLeft = FindCharAttrib(nCursor, EE_CHAR_GRABBAG, aAttribList); + bHasFieldLeft = pBackAttrLeft !=nullptr; + bHasErrorLeft = pErrorAttrLeft != nullptr; + ++nCursor; + } + } + //Here we have to determine if the error found is the one currently active + bool bIsErrorActive = (pErrorAttr && pErrorAttr->nStart == m_nErrorStart) || + (pErrorAttrLeft && pErrorAttrLeft->nStart == m_nErrorStart); + + SAL_WARN_IF( + nSelectionType == INVALID, "cui.dialogs", + "selection type not set"); + + const vcl::KeyCode& rKeyCode = rKeyEvt.GetKeyCode(); + bool bDelete = rKeyCode.GetCode() == KEY_DELETE; + bool bBackspace = rKeyCode.GetCode() == KEY_BACKSPACE; + + sal_Int8 nAction = ACTION_CONTINUE; + switch(nSelectionType) + { +// 1 - backspace delete any other +// UE on field FS on error CO on field FS on error CO + case LEFT_NO : + if(bBackspace) + { + nAction = bHasFieldLeft ? ACTION_SELECTFIELD : ACTION_UNDOEDIT; + //to force the use of pBackAttrLeft + pBackAttr = nullptr; + } + else if(bDelete) + nAction = bHasField ? ACTION_SELECTFIELD : ACTION_CONTINUE; + else + nAction = bHasError && !nCursor ? ACTION_CONTINUE : + bHasError ? ACTION_EXPAND : bHasErrorLeft ? ACTION_CONTINUE : ACTION_UNDOEDIT; + break; +// 2 - on field FS on error C + case INSIDE_NO : + nAction = bHasField ? ACTION_SELECTFIELD : + bIsErrorActive ? ACTION_CONTINUE : ACTION_UNDOEDIT; + break; +// 3 - backspace delete any other +// on field FS on error CO UE on field UE on error EX + case RIGHT_NO : + if(bBackspace) + nAction = bHasFieldLeft ? ACTION_SELECTFIELD : ACTION_CONTINUE; + else if(bDelete) + nAction = bHasFieldLeft && bHasError ? ACTION_CONTINUE : ACTION_UNDOEDIT; + else + nAction = bHasFieldLeft && bHasError ? ACTION_EXPAND : + bHasError ? ACTION_CONTINUE : bHasErrorLeft ? ACTION_EXPAND :ACTION_UNDOEDIT; + break; +// 4 - on field UE and on error CO + case FULL : + nAction = ACTION_UNDOEDIT; + break; +// 5 - on field FS and on error CO + case INSIDE_YES : + nAction = bHasField ? ACTION_SELECTFIELD : ACTION_CONTINUE; + break; +// 6 - on field FS and on error UE + case BRACE : + nAction = bHasField ? ACTION_SELECTFIELD : ACTION_UNDOEDIT; + break; +// 7 - UE +// 8 - UE + case OUTSIDE_NO : + case OUTSIDE_YES: + nAction = ACTION_UNDOEDIT; + break; + } + //save the current paragraph + sal_Int32 nCurrentLen = m_xEditEngine->GetText().getLength(); + if (nAction != ACTION_SELECTFIELD) + { + m_xEditView->PostKeyEvent(rKeyEvt); + } + else + { + const EECharAttrib* pCharAttr = pBackAttr ? pBackAttr : pBackAttrLeft; + if (pCharAttr) + m_xEditView->SetSelection(ESelection(0, pCharAttr->nStart, 0, pCharAttr->nEnd)); + } + if(nAction == ACTION_EXPAND) + { + DBG_ASSERT(pErrorAttrLeft || pErrorAttr, "where is the error"); + //text has been added on the right and only the 'error attribute has to be corrected + if (pErrorAttrLeft) + { + SpellErrorDescription aSpellErrorDescription; + ExtractErrorDescription(*pErrorAttrLeft, aSpellErrorDescription); + + std::unique_ptr xNewError(pErrorAttrLeft->pAttr->Clone()); + sal_Int32 nStart = pErrorAttrLeft->nStart; + sal_Int32 nEnd = pErrorAttrLeft->nEnd + 1; + m_xEditEngine->RemoveAttribs(ESelection(0, nStart, 0, nEnd), false, EE_CHAR_GRABBAG); + SetAttrib(*xNewError, nStart, nEnd); + //only active errors move the mark + if (bIsErrorActive) + { + bool bGrammar = aSpellErrorDescription.bIsGrammarError; + MoveErrorMarkTo(nStart, nEnd, bGrammar); + } + } + //text has been added on the left then the error attribute has to be expanded and the + //field attribute on the right - if any - has to be contracted + else if (pErrorAttr) + { + SpellErrorDescription aSpellErrorDescription; + ExtractErrorDescription(*pErrorAttr, aSpellErrorDescription); + + //determine the change + sal_Int32 nAddedChars = m_xEditEngine->GetText().getLength() - nCurrentLen; + + std::unique_ptr xNewError(pErrorAttr->pAttr->Clone()); + sal_Int32 nStart = pErrorAttr->nStart + nAddedChars; + sal_Int32 nEnd = pErrorAttr->nEnd + nAddedChars; + m_xEditEngine->RemoveAttribs(ESelection(0, nStart, 0, nEnd), false, EE_CHAR_GRABBAG); + nStart = pErrorAttr->nStart; + SetAttrib(*xNewError, nStart, nEnd); + //only if the error is active the mark is moved here + if (bIsErrorActive) + { + bool bGrammar = aSpellErrorDescription.bIsGrammarError; + MoveErrorMarkTo(nStart, nEnd, bGrammar); + } + xNewError.reset(); + + if (pBackAttrLeft) + { + std::unique_ptr xNewBack(pBackAttrLeft->pAttr->Clone()); + sal_Int32 _nStart = pBackAttrLeft->nStart + nAddedChars; + sal_Int32 _nEnd = pBackAttrLeft->nEnd + nAddedChars; + m_xEditEngine->RemoveAttribs(ESelection(0, _nStart, 0, _nEnd), false, EE_CHAR_BKGCOLOR); + _nStart = pBackAttrLeft->nStart; + SetAttrib(*xNewBack, _nStart, _nEnd); + } + } + } + else if(nAction == ACTION_UNDOEDIT) + { + SetUndoEditMode(true); + } + //make sure the error positions are correct after text changes + //the old attribute may have been deleted + //all changes inside of the current error leave the error attribute at the current + //start position + if (!IsUndoEditMode() && bIsErrorActive) + { + aAttribList.clear(); + m_xEditEngine->GetCharAttribs(0, aAttribList); + const EECharAttrib* pFontColor = FindCharAttrib(nCursor, EE_CHAR_COLOR, aAttribList); + const EECharAttrib* pErrorAttrib = FindCharAttrib(m_nErrorStart, EE_CHAR_GRABBAG, aAttribList); + if (pFontColor && pErrorAttrib) + { + m_nErrorStart = pFontColor->nStart; + m_nErrorEnd = pFontColor->nEnd; + if (pErrorAttrib->nStart != m_nErrorStart || pErrorAttrib->nEnd != m_nErrorEnd) + { + std::unique_ptr xNewError(pErrorAttrib->pAttr->Clone()); + assert(pErrorAttr); + m_xEditEngine->RemoveAttribs(ESelection(0, pErrorAttr->nStart, 0, pErrorAttr->nEnd), false, EE_CHAR_GRABBAG); + SetAttrib(*xNewError, m_nErrorStart, m_nErrorEnd); + } + } + } + //this is not a modification anymore + if(nAction != ACTION_SELECTFIELD && !m_bIsUndoEditMode) + CallModifyLink(); + } + else + bConsumed = m_xEditView->PostKeyEvent(rKeyEvt); + + return bConsumed; +} + +void SentenceEditWindow_Impl::Init(weld::Toolbar* pToolbar) +{ + m_pToolbar = pToolbar; + m_pToolbar->connect_clicked(LINK(this,SentenceEditWindow_Impl,ToolbarHdl)); +} + +IMPL_LINK(SentenceEditWindow_Impl, ToolbarHdl, const OUString&, rCurItemId, void) +{ + if (rCurItemId == "paste") + { + m_xEditView->Paste(); + CallModifyLink(); + } + else if (rCurItemId == "insert") + { + if (auto pImplFncGetSpecialChars = vcl::GetGetSpecialCharsFunction()) + { + OUString aChars = pImplFncGetSpecialChars(GetDrawingArea(), m_xEditEngine->GetStandardFont(0)); + if (!aChars.isEmpty()) + { + ESelection aCurrentSelection(m_xEditView->GetSelection()); + m_xEditEngine->QuickInsertText(aChars, aCurrentSelection); + CallModifyLink(); + } + } + } +} + +bool SentenceEditWindow_Impl::MarkNextError( bool bIgnoreCurrentError, const css::uno::Reference& xSpell ) +{ + if (bIgnoreCurrentError) + m_aIgnoreErrorsAt.insert( m_nErrorStart ); + + const sal_Int32 nTextLen = m_xEditEngine->GetTextLen(0); + + if (m_nErrorEnd >= nTextLen - 1) + return false; + //if it's not already modified the modified flag has to be reset at the end of the marking + bool bModified = IsModified(); + bool bRet = false; + const sal_Int32 nOldErrorStart = m_nErrorStart; + const sal_Int32 nOldErrorEnd = m_nErrorEnd; + + //create a cursor behind the end of the last error + //- or at 0 at the start of the sentence + sal_Int32 nCursor(m_nErrorEnd ? m_nErrorEnd + 1 : 0); + + //search for SpellErrorDescription + SpellErrorDescription aSpellErrorDescription; + + std::vector aAttribList; + m_xEditEngine->GetCharAttribs(0, aAttribList); + + //iterate over the text and search for the next error that maybe has + //to be replace by a ChangeAllList replacement + bool bGrammarError = false; + while (nCursor < nTextLen) + { + const SpellErrorDescription* pSpellErrorDescription = nullptr; + const EECharAttrib* pEECharAttrib = nullptr; + + sal_Int32 nMinPos = nTextLen + 1; + for (const auto& rTextAtr : aAttribList) + { + if (rTextAtr.pAttr->Which() != EE_CHAR_GRABBAG) + continue; + if (rTextAtr.nEnd > nCursor && rTextAtr.nStart < nMinPos) + { + nMinPos = rTextAtr.nStart; + pEECharAttrib = &rTextAtr; + } + } + + if (pEECharAttrib) + { + ExtractErrorDescription(*pEECharAttrib, aSpellErrorDescription); + + bGrammarError = aSpellErrorDescription.bIsGrammarError; + m_nErrorStart = pEECharAttrib->nStart; + m_nErrorEnd = pEECharAttrib->nEnd; + + pSpellErrorDescription = &aSpellErrorDescription; + } + + nCursor = std::max(nCursor, nMinPos); // move forward if possible + + // maybe the error found here is already in the ChangeAllList and has to be replaced + Reference xChangeAll = LinguMgr::GetChangeAllList(); + Reference xEntry; + + if (xChangeAll->getCount() && pSpellErrorDescription && + (xEntry = xChangeAll->getEntry( pSpellErrorDescription->sErrorText )).is()) + { + OUString sReplacement(getDotReplacementString(GetErrorText(), xEntry->getReplacementText())); + + int nLenChange = ChangeMarkedWord(sReplacement, LanguageTag::convertToLanguageType(pSpellErrorDescription->aLocale)); + + nCursor += sReplacement.getLength(); + + if (nLenChange) + m_xEditEngine->GetCharAttribs(0, aAttribList); + // maybe the error found here is already added to the dictionary and has to be ignored + } + else if(pSpellErrorDescription && !bGrammarError && + xSpell->isValid(GetErrorText(), + static_cast(LanguageTag::convertToLanguageType( pSpellErrorDescription->aLocale )), + Sequence< PropertyValue >() )) + { + ++nCursor; + } + else + break; + } + + //if an attrib has been found search for the end of the error string + if (nCursor < nTextLen) + { + MoveErrorMarkTo(nCursor, m_nErrorEnd, bGrammarError); + bRet = true; + //add an undo action + std::unique_ptr pAction(new SpellUndoAction_Impl( + SPELLUNDO_CHANGE_NEXTERROR, GetSpellDialog()->aDialogUndoLink)); + pAction->SetErrorMove(nOldErrorStart, nOldErrorEnd); + + if (GetErrorDescription(aSpellErrorDescription, nOldErrorStart)) + { + pAction->SetErrorLanguageSelected(aSpellErrorDescription.aSuggestions.hasElements() && + LanguageTag(aSpellErrorDescription.aLocale).getLanguageType() == GetSpellDialog()->m_xLanguageLB->get_active_id()); + } + else + pAction->SetErrorLanguageSelected(false); + + AddUndoAction(std::move(pAction)); + } + else + m_nErrorStart = m_nErrorEnd = nTextLen; + if( !bModified ) + ClearModifyFlag(); + SpellDialog* pSpellDialog = GetSpellDialog(); + pSpellDialog->m_xIgnorePB->set_sensitive(bRet); + pSpellDialog->m_xIgnoreAllPB->set_sensitive(bRet); + pSpellDialog->m_xAutoCorrPB->set_sensitive(bRet); + pSpellDialog->m_xAddToDictMB->set_sensitive(bRet); + pSpellDialog->m_xAddToDictPB->set_sensitive(bRet); + return bRet; +} + +void SentenceEditWindow_Impl::MoveErrorMarkTo(sal_Int32 nStart, sal_Int32 nEnd, bool bGrammarError) +{ + ESelection aAll(0, 0, 0, EE_TEXTPOS_ALL); + m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_COLOR); + m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT); + m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT_CJK); + m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT_CTL); + + // tdf#116566 Use color defined in the current Color Scheme + Color aSpellErrorCollor = svtools::ColorConfig().GetColorValue(svtools::SPELL).nColor; + Color aGrammarErrorCollor = svtools::ColorConfig().GetColorValue(svtools::GRAMMAR).nColor; + + SfxItemSet aSet(m_xEditEngine->GetEmptyItemSet()); + aSet.Put(SvxColorItem(bGrammarError ? aGrammarErrorCollor : aSpellErrorCollor, EE_CHAR_COLOR)); + aSet.Put(SvxWeightItem(WEIGHT_BOLD, EE_CHAR_WEIGHT)); + aSet.Put(SvxWeightItem(WEIGHT_BOLD, EE_CHAR_WEIGHT_CJK)); + aSet.Put(SvxWeightItem(WEIGHT_BOLD, EE_CHAR_WEIGHT_CTL)); + + m_xEditEngine->QuickSetAttribs(aSet, ESelection(0, nStart, 0, nEnd)); + + // Set the selection so the editview will autoscroll to make this visible + // unless (tdf#133958) the selection already overlaps this range + ESelection aCurrentSelection = m_xEditView->GetSelection(); + aCurrentSelection.Adjust(); + bool bCurrentSelectionInRange = nStart <= aCurrentSelection.nEndPos && aCurrentSelection.nStartPos <= nEnd; + if (!bCurrentSelectionInRange) + { + m_xEditView->SetSelection(ESelection(0, nStart)); + // tdf#157148 ensure current location is auto-scrolled to be visible + m_xEditView->ShowCursor(); + } + + Invalidate(); + + m_nErrorStart = nStart; + m_nErrorEnd = nEnd; +} + +int SentenceEditWindow_Impl::ChangeMarkedWord(const OUString& rNewWord, LanguageType eLanguage) +{ + std::vector aAttribList; + m_xEditEngine->GetCharAttribs(0, aAttribList); + + //calculate length changes + auto nDiffLen = rNewWord.getLength() - m_nErrorEnd + m_nErrorStart; + //Remove spell error attribute + m_xEditEngine->UndoActionStart(SPELLUNDO_MOVE_ERROREND); + const EECharAttrib* pErrorAttrib = FindCharAttrib(m_nErrorStart, EE_CHAR_GRABBAG, aAttribList); + DBG_ASSERT(pErrorAttrib, "no error attribute found"); + bool bSpellErrorDescription = false; + SpellErrorDescription aSpellErrorDescription; + if (pErrorAttrib) + { + ExtractErrorDescription(*pErrorAttrib, aSpellErrorDescription); + m_xEditEngine->RemoveAttribs(ESelection(0, pErrorAttrib->nStart, 0, pErrorAttrib->nEnd), false, EE_CHAR_GRABBAG); + bSpellErrorDescription = true; + } + + const EECharAttrib* pBackAttrib = FindCharAttrib(m_nErrorStart, EE_CHAR_BKGCOLOR, aAttribList); + + ESelection aSel(0, m_nErrorStart, 0, m_nErrorEnd); + m_xEditEngine->QuickInsertText(rNewWord, aSel); + + const sal_Int32 nTextLen = m_xEditEngine->GetTextLen(0); + + if (nDiffLen) + m_xEditEngine->GetCharAttribs(0, aAttribList); + + if (!m_nErrorStart) + { + //attributes following an error at the start of the text are not moved but expanded from the + //text engine - this is done to keep full-paragraph-attributes + //in the current case that handling is not desired + const EECharAttrib* pLangAttrib = FindCharAttrib(m_nErrorEnd, EE_CHAR_LANGUAGE, aAttribList); + + if (pLangAttrib && !pLangAttrib->nStart && pLangAttrib->nEnd == nTextLen) + { + LanguageType eNewLanguage = static_cast(pLangAttrib->pAttr)->GetLanguage(); + m_xEditEngine->RemoveAttribs(ESelection(0, pLangAttrib->nStart, 0, pLangAttrib->nEnd), false, EE_CHAR_LANGUAGE); + SetAttrib(SvxLanguageItem(eNewLanguage, EE_CHAR_LANGUAGE), m_nErrorEnd + nDiffLen, nTextLen); + } + } + + // undo expanded attributes! + if (pBackAttrib && pBackAttrib->nStart < m_nErrorStart && pBackAttrib->nEnd == m_nErrorEnd + nDiffLen) + { + std::unique_ptr xNewBackground(pBackAttrib->pAttr->Clone()); + const sal_Int32 nStart = pBackAttrib->nStart; + + m_xEditEngine->RemoveAttribs(ESelection(0, pBackAttrib->nStart, 0, pBackAttrib->nEnd), false, EE_CHAR_BKGCOLOR); + + SetAttrib(*xNewBackground, nStart, m_nErrorStart); + } + m_xEditEngine->SetModified(); + + //adjust end position + tools::Long nEndTemp = m_nErrorEnd; + nEndTemp += nDiffLen; + m_nErrorEnd = static_cast(nEndTemp); + + std::unique_ptr pAction(new SpellUndoAction_Impl( + SPELLUNDO_MOVE_ERROREND, GetSpellDialog()->aDialogUndoLink)); + pAction->SetOffset(nDiffLen); + AddUndoAction(std::move(pAction)); + if (bSpellErrorDescription) + { + SfxGrabBagItem aSpellErrorDescriptionItem(EE_CHAR_GRABBAG); + aSpellErrorDescriptionItem.GetGrabBag()["SpellErrorDescription"] <<= aSpellErrorDescription.toSequence(); + SetAttrib(aSpellErrorDescriptionItem, m_nErrorStart, m_nErrorEnd); + } + SetAttrib(SvxLanguageItem(eLanguage, EE_CHAR_LANGUAGE), m_nErrorStart, m_nErrorEnd); + m_xEditEngine->UndoActionEnd(); + + Invalidate(); + + return nDiffLen; +} + +OUString SentenceEditWindow_Impl::GetErrorText() const +{ + return m_xEditEngine->GetText(ESelection(0, m_nErrorStart, 0, m_nErrorEnd)); +} + +bool SentenceEditWindow_Impl::GetErrorDescription(SpellErrorDescription& rSpellErrorDescription, sal_Int32 nPosition) +{ + std::vector aAttribList; + m_xEditEngine->GetCharAttribs(0, aAttribList); + + if (const EECharAttrib* pEECharAttrib = FindCharAttrib(nPosition, EE_CHAR_GRABBAG, aAttribList)) + { + ExtractErrorDescription(*pEECharAttrib, rSpellErrorDescription); + return true; + } + + return false; +} + +bool SentenceEditWindow_Impl::GetAlternatives(SpellErrorDescription& rSpellErrorDescription) +{ + return GetErrorDescription(rSpellErrorDescription, m_nErrorStart); +} + +void SentenceEditWindow_Impl::RestoreCurrentError() +{ + SpellErrorDescription aSpellErrorDescription; + if (GetErrorDescription(aSpellErrorDescription, m_nErrorStart)) + { + if (aSpellErrorDescription.sErrorText != GetErrorText() ) + ChangeMarkedWord(aSpellErrorDescription.sErrorText, LanguageTag::convertToLanguageType(aSpellErrorDescription.aLocale)); + } +} + +void SentenceEditWindow_Impl::SetAlternatives( const Reference< XSpellAlternatives>& xAlt ) +{ + OUString aWord; + lang::Locale aLocale; + uno::Sequence< OUString > aAlts; + if (xAlt.is()) + { + aWord = xAlt->getWord(); + aLocale = xAlt->getLocale(); + aAlts = xAlt->getAlternatives(); + } + SpellErrorDescription aDesc( false, aWord, std::move(aLocale), aAlts, nullptr); + SfxGrabBagItem aSpellErrorDescription(EE_CHAR_GRABBAG); + aSpellErrorDescription.GetGrabBag()["SpellErrorDescription"] <<= aDesc.toSequence(); + SetAttrib(aSpellErrorDescription, m_nErrorStart, m_nErrorEnd); +} + +void SentenceEditWindow_Impl::SetAttrib(const SfxPoolItem& rItem, sal_Int32 nStart, sal_Int32 nEnd) +{ + SfxItemSet aSet(m_xEditEngine->GetEmptyItemSet()); + aSet.Put(rItem); + m_xEditEngine->QuickSetAttribs(aSet, ESelection(0, nStart, 0, nEnd)); + Invalidate(); +} + +void SentenceEditWindow_Impl::SetText( const OUString& rStr ) +{ + m_nErrorStart = m_nErrorEnd = 0; + m_xEditEngine->SetText(rStr); +} + +namespace { + +struct LanguagePosition_Impl +{ + sal_Int32 nPosition; + LanguageType eLanguage; + + LanguagePosition_Impl(sal_Int32 nPos, LanguageType eLang) : + nPosition(nPos), + eLanguage(eLang) + {} +}; + +} + +typedef std::vector LanguagePositions_Impl; + +static void lcl_InsertBreakPosition_Impl( + LanguagePositions_Impl& rBreakPositions, sal_Int32 nInsert, LanguageType eLanguage) +{ + LanguagePositions_Impl::iterator aStart = rBreakPositions.begin(); + while(aStart != rBreakPositions.end()) + { + if(aStart->nPosition == nInsert) + { + //the language of following starts has to overwrite + //the one of previous ends + aStart->eLanguage = eLanguage; + return; + } + else if(aStart->nPosition > nInsert) + { + + rBreakPositions.insert(aStart, LanguagePosition_Impl(nInsert, eLanguage)); + return; + } + else + ++aStart; + } + rBreakPositions.emplace_back(nInsert, eLanguage); +} + +/*------------------------------------------------------------------------- + Returns the text in spell portions. Each portion contains text with an + equal language and attribute. The spell alternatives are empty. + -----------------------------------------------------------------------*/ +svx::SpellPortions SentenceEditWindow_Impl::CreateSpellPortions() const +{ + svx::SpellPortions aRet; + + const sal_Int32 nTextLen = m_xEditEngine->GetTextLen(0); + + std::vector aAttribList; + m_xEditEngine->GetCharAttribs(0, aAttribList); + + if (nTextLen) + { + int nCursor(0); + LanguagePositions_Impl aBreakPositions; + const EECharAttrib* pLastLang = nullptr; + const EECharAttrib* pLastError = nullptr; + LanguageType eLang = LANGUAGE_DONTKNOW; + const EECharAttrib* pError = nullptr; + while (nCursor < nTextLen) + { + const EECharAttrib* pLang = FindCharAttrib(nCursor, EE_CHAR_LANGUAGE, aAttribList); + if(pLang && pLang != pLastLang) + { + eLang = static_cast(pLang->pAttr)->GetLanguage(); + lcl_InsertBreakPosition_Impl(aBreakPositions, pLang->nStart, eLang); + lcl_InsertBreakPosition_Impl(aBreakPositions, pLang->nEnd, eLang); + pLastLang = pLang; + } + pError = FindCharAttrib(nCursor, EE_CHAR_GRABBAG, aAttribList); + if (pError && pLastError != pError) + { + lcl_InsertBreakPosition_Impl(aBreakPositions, pError->nStart, eLang); + lcl_InsertBreakPosition_Impl(aBreakPositions, pError->nEnd, eLang); + pLastError = pError; + + } + ++nCursor; + } + + if (aBreakPositions.empty()) + { + //if all content has been overwritten the attributes may have been removed, too + svx::SpellPortion aPortion1; + aPortion1.eLanguage = GetSpellDialog()->GetSelectedLang_Impl(); + + aPortion1.sText = m_xEditEngine->GetText(ESelection(0, 0, 0, nTextLen)); + + aRet.push_back(aPortion1); + } + else + { + LanguagePositions_Impl::iterator aStart = aBreakPositions.begin(); + //start should always be Null + eLang = aStart->eLanguage; + sal_Int32 nStart = aStart->nPosition; + DBG_ASSERT(!nStart, "invalid start position - language attribute missing?"); + ++aStart; + + while(aStart != aBreakPositions.end()) + { + svx::SpellPortion aPortion1; + aPortion1.eLanguage = eLang; + + aPortion1.sText = m_xEditEngine->GetText(ESelection(0, nStart, 0, aStart->nPosition)); + bool bIsIgnoreError = m_aIgnoreErrorsAt.find( nStart ) != m_aIgnoreErrorsAt.end(); + if( bIsIgnoreError ) + { + aPortion1.bIgnoreThisError = true; + } + aRet.push_back(aPortion1); + nStart = aStart->nPosition; + eLang = aStart->eLanguage; + ++aStart; + } + } + + // quick partly fix of #i71318. Correct fix needs to patch the EditEngine itself... + // this one will only prevent text from disappearing. It may to not have the + // correct language and will probably not spell checked... + const sal_uInt32 nPara = m_xEditEngine->GetParagraphCount(); + if (nPara > 1) + { + OUStringBuffer aLeftOverText; + for (sal_uInt32 i = 1; i < nPara; ++i) + { + aLeftOverText.append("\x0a"); // the manual line break... + aLeftOverText.append(m_xEditEngine->GetText(i)); + } + if (pError) + { // we need to add a new portion containing the left-over text + svx::SpellPortion aPortion2; + aPortion2.eLanguage = eLang; + aPortion2.sText = aLeftOverText.makeStringAndClear(); + aRet.push_back( aPortion2 ); + } + else if (!aLeftOverText.isEmpty() && !aRet.empty()) + { // we just need to append the left-over text to the last portion (which had no errors) + aRet[ aRet.size() - 1 ].sText += aLeftOverText; + } + } + } + + return aRet; +} + +void SentenceEditWindow_Impl::Undo() +{ + EditUndoManager& rUndoMgr = m_xEditEngine->GetUndoManager(); + DBG_ASSERT(GetUndoActionCount(), "no undo actions available" ); + if(!GetUndoActionCount()) + return; + bool bSaveUndoEdit = IsUndoEditMode(); + SpellUndoAction_Impl* pUndoAction; + //if the undo edit mode is active then undo all changes until the UNDO_EDIT_MODE action has been found + do + { + pUndoAction = static_cast(rUndoMgr.GetUndoAction()); + rUndoMgr.Undo(); + }while(bSaveUndoEdit && SPELLUNDO_UNDO_EDIT_MODE != pUndoAction->GetId() && GetUndoActionCount()); + + if(bSaveUndoEdit || SPELLUNDO_CHANGE_GROUP == pUndoAction->GetId()) + GetSpellDialog()->UpdateBoxes_Impl(); +} + +void SentenceEditWindow_Impl::ResetUndo() +{ + EditUndoManager& rUndo = m_xEditEngine->GetUndoManager(); + rUndo.Clear(); +} + +void SentenceEditWindow_Impl::AddUndoAction( std::unique_ptr pAction ) +{ + EditUndoManager& rUndoMgr = m_xEditEngine->GetUndoManager(); + rUndoMgr.AddUndoAction(std::move(pAction)); + GetSpellDialog()->m_xUndoPB->set_sensitive(true); +} + +size_t SentenceEditWindow_Impl::GetUndoActionCount() const +{ + return m_xEditEngine->GetUndoManager().GetUndoActionCount(); +} + +void SentenceEditWindow_Impl::UndoActionStart( sal_uInt16 nId ) +{ + m_xEditEngine->UndoActionStart(nId); +} + +void SentenceEditWindow_Impl::UndoActionEnd() +{ + m_xEditEngine->UndoActionEnd(); +} + +void SentenceEditWindow_Impl::MoveErrorEnd(tools::Long nOffset) +{ + // Shouldn't we always add the real signed value instead??? + if(nOffset > 0) + m_nErrorEnd = m_nErrorEnd - static_cast(nOffset); + else + m_nErrorEnd = m_nErrorEnd - static_cast(-nOffset); +} + + +void SentenceEditWindow_Impl::SetUndoEditMode(bool bSet) +{ + DBG_ASSERT(!bSet || m_bIsUndoEditMode != bSet, "SetUndoEditMode with equal values?"); + m_bIsUndoEditMode = bSet; + //disable all buttons except the Change + SpellDialog* pSpellDialog = GetSpellDialog(); + weld::Widget* aControls[] = + { + pSpellDialog->m_xChangeAllPB.get(), + pSpellDialog->m_xExplainFT.get(), + pSpellDialog->m_xIgnoreAllPB.get(), + pSpellDialog->m_xIgnoreRulePB.get(), + pSpellDialog->m_xIgnorePB.get(), + pSpellDialog->m_xSuggestionLB.get(), + pSpellDialog->m_xSuggestionFT.get(), + pSpellDialog->m_xLanguageFT.get(), + pSpellDialog->m_xLanguageLB->get_widget(), + pSpellDialog->m_xAddToDictMB.get(), + pSpellDialog->m_xAddToDictPB.get(), + pSpellDialog->m_xAutoCorrPB.get() + }; + for (weld::Widget* pWidget : aControls) + pWidget->set_sensitive(false); + + //remove error marks + ESelection aAll(0, 0, 0, EE_TEXTPOS_ALL); + m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_COLOR); + m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT); + m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT_CJK); + m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT_CTL); + Invalidate(); + + //put the appropriate action on the Undo-stack + AddUndoAction( std::make_unique( + SPELLUNDO_UNDO_EDIT_MODE, GetSpellDialog()->aDialogUndoLink) ); + pSpellDialog->m_xChangePB->set_sensitive(true); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/about.cxx b/cui/source/dialogs/about.cxx new file mode 100644 index 0000000000..ce82e418cf --- /dev/null +++ b/cui/source/dialogs/about.cxx @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include //osl_getProcessLocale +#include +#include //SAL_WARN +#include //Graphic +#include //GetSettings +#include //Application:: +#include +#include //Translate + +#include //EXTRA_BUILDID +#include +#include //CuiResId +#include +#include //SfxApplication::loadBrandSvg +#include +#include +#include //utl::Bootstrap::getBuildIdData +#include //ConfigManager:: + +#include +#include + +#include +#if HAVE_FEATURE_OPENCL +#include +#endif +#include +#include + +using namespace ::com::sun::star::uno; + +AboutDialog::AboutDialog(weld::Window *pParent) + : GenericDialogController(pParent, "cui/ui/aboutdialog.ui", "AboutDialog"), + m_pCreditsButton(m_xBuilder->weld_link_button("btnCredits")), + m_pWebsiteButton(m_xBuilder->weld_link_button("btnWebsite")), + m_pReleaseNotesButton(m_xBuilder->weld_link_button("btnReleaseNotes")), + m_pCloseButton(m_xBuilder->weld_button("btnClose")), + m_pCopyButton(m_xBuilder->weld_button("btnCopyVersion")), + m_pBrandImage(m_xBuilder->weld_image("imBrand")), + m_pAboutImage(m_xBuilder->weld_image("imAbout")), + m_pVersionLabel(m_xBuilder->weld_label("lbVersionString")), + m_pBuildCaption(m_xBuilder->weld_label("lbBuild")), + m_pBuildLabel(m_xBuilder->weld_link_button("lbBuildString")), + m_pEnvLabel(m_xBuilder->weld_label("lbEnvString")), + m_pUILabel(m_xBuilder->weld_label("lbUIString")), + m_pLocaleLabel(m_xBuilder->weld_label("lbLocaleString")), + m_pMiscLabel(m_xBuilder->weld_label("lbMiscString")), + m_pCopyrightLabel(m_xBuilder->weld_label("lbCopyright")) { + + // Labels + m_pVersionLabel->set_label(GetVersionString()); + + OUString sbuildId = GetBuildString(); + if (IsStringValidGitHash(sbuildId)) { + const tools::Long nMaxChar = 25; + m_pBuildLabel->set_uri("https://gerrit.libreoffice.org/gitweb?p=core.git;a=log;h=" + + sbuildId); + m_pBuildLabel->set_label(sbuildId.getLength() > nMaxChar ? sbuildId.replaceAt( + nMaxChar, sbuildId.getLength() - nMaxChar, u"...") + : sbuildId); + } else { + m_pBuildCaption->hide(); + m_pBuildLabel->hide(); + } + + m_pEnvLabel->set_label(Application::GetHWOSConfInfo(1)); + m_pUILabel->set_label(Application::GetHWOSConfInfo(2)); + m_pLocaleLabel->set_label(GetLocaleString()); + m_pMiscLabel->set_label(GetMiscString()); + m_pCopyrightLabel->set_label(GetCopyrightString()); + + // Images + const tools::Long nWidth(m_pCopyrightLabel->get_preferred_size().getWidth()); + BitmapEx aBackgroundBitmap; + + if (SfxApplication::loadBrandSvg(Application::GetSettings() + .GetStyleSettings() + .GetDialogColor() + .IsDark() + ? "shell/logo_inverted" + : "shell/logo", + aBackgroundBitmap, nWidth * 0.8)) { + // Eliminate white background when Skia is disabled by not drawing the + // background bitmap to a VirtualDevice. On most platforms, non-Skia + // VirtualDevices will be filled with a solid color when drawing + // the bitmap. + Graphic aGraphic(aBackgroundBitmap); + m_pBrandImage->set_image(aGraphic.GetXGraphic()); + } + if (SfxApplication::loadBrandSvg("shell/about", aBackgroundBitmap, nWidth * 0.9)) { + // Eliminate white background when Skia is disabled by not drawing the + // background bitmap to a VirtualDevice. On most platforms, non-Skia + // VirtualDevices will be filled with a solid color when drawing + // the bitmap. + Graphic aGraphic(aBackgroundBitmap); + m_pAboutImage->set_image(aGraphic.GetXGraphic()); + } + + // Links + m_pCreditsButton->set_uri(officecfg::Office::Common::Menus::CreditsURL::get()); + + OUString sURL(officecfg::Office::Common::Help::StartCenter::InfoURL::get()); + localizeWebserviceURI(sURL); + m_pWebsiteButton->set_uri(sURL); + + // See also SID_WHATSNEW in sfx2/source/appl/appserv.cxx + sURL = officecfg::Office::Common::Menus::ReleaseNotesURL::get() + + "?LOvers=" + utl::ConfigManager::getProductVersion() + "&LOlocale=" + + LanguageTag(utl::ConfigManager::getUILocale()).getBcp47(); + m_pReleaseNotesButton->set_uri(sURL); + + // Handler + m_pCopyButton->connect_clicked(LINK(this, AboutDialog, HandleClick)); + m_pCloseButton->grab_focus(); +} + +AboutDialog::~AboutDialog() {} + +bool AboutDialog::IsStringValidGitHash(std::u16string_view hash) { + return std::all_of(hash.begin(), hash.end(), + [](auto &rSymbol) { return std::isxdigit(rSymbol); }); +} + +OUString AboutDialog::GetVersionString() { + OUString arch; + auto const ok = rtl::Bootstrap::get("_ARCH", arch); + assert(ok); (void) ok; + OUString sVersion = CuiResId(TranslateId(nullptr, "%ABOUTBOXPRODUCTVERSION%ABOUTBOXPRODUCTVERSIONSUFFIX")) + " (" + arch + ")"; + +#if HAVE_FEATURE_COMMUNITY_FLAVOR + sVersion += " / LibreOffice Community"; +#endif + + return sVersion; +} + +OUString AboutDialog::GetBuildString() +{ + OUString sBuildId(utl::Bootstrap::getBuildIdData("")); + SAL_WARN_IF(sBuildId.isEmpty(), "cui.dialogs", "No BUILDID in bootstrap file"); + + return sBuildId; +} + +OUString AboutDialog::GetLocaleString(const bool bLocalized) { + + OUString sLocaleStr; + + rtl_Locale *pLocale; + osl_getProcessLocale(&pLocale); + if (pLocale && pLocale->Language) { + if (pLocale->Country && rtl_uString_getLength(pLocale->Country) > 0) + sLocaleStr = OUString::unacquired(&pLocale->Language) + "_" + + OUString::unacquired(&pLocale->Country); + else + sLocaleStr = OUString(pLocale->Language); + if (pLocale->Variant && rtl_uString_getLength(pLocale->Variant) > 0) + sLocaleStr += OUString(pLocale->Variant); + } + + sLocaleStr = Application::GetSettings().GetLanguageTag().getBcp47() + " (" + + sLocaleStr + ")"; + + OUString aUILocaleStr = + Application::GetSettings().GetUILanguageTag().getBcp47(); + OUString sUILocaleStr; + if (bLocalized) + sUILocaleStr = CuiResId(RID_CUISTR_ABOUT_UILOCALE); + else + sUILocaleStr = Translate::get(RID_CUISTR_ABOUT_UILOCALE, Translate::Create("cui", LanguageTag("en-US"))); + + if (sUILocaleStr.indexOf("$LOCALE") == -1) { + SAL_WARN("cui.dialogs", "translated uilocale string in translations " + "doesn't contain $LOCALE placeholder"); + sUILocaleStr += " $LOCALE"; + } + sUILocaleStr = sUILocaleStr.replaceAll("$LOCALE", aUILocaleStr); + + return sLocaleStr + "; " + sUILocaleStr; +} + +OUString AboutDialog::GetMiscString() { + + OUString sMisc; + + bool const extra = EXTRA_BUILDID[0] != '\0'; + // extracted from the 'if' to avoid Clang -Wunreachable-code + if (extra) { + sMisc = EXTRA_BUILDID "\n"; + } + + OUString aCalcMode; // Calc calculation mode + +#if HAVE_FEATURE_OPENCL + if (openclwrapper::GPUEnv::isOpenCLEnabled()) + aCalcMode += " CL"; +#endif + + static const bool bThreadingProhibited = + std::getenv("SC_NO_THREADED_CALCULATION"); + bool bThreadedCalc = officecfg::Office::Calc::Formula::Calculation:: + UseThreadedCalculationForFormulaGroups::get(); + + if (!bThreadingProhibited && bThreadedCalc) { + aCalcMode += " threaded"; + } + + if (officecfg::Office::Calc::Defaults::Sheet::JumboSheets::get()) + { + aCalcMode += " Jumbo"; + } + + if (aCalcMode.isEmpty()) + aCalcMode = " default"; + sMisc += "Calc:" + aCalcMode; + + return sMisc; +} + +OUString AboutDialog::GetCopyrightString() { + OUString sVendorTextStr(CuiResId(RID_CUISTR_ABOUT_VENDOR)); + OUString aCopyrightString = + sVendorTextStr + "\n" + CuiResId(RID_CUISTR_ABOUT_COPYRIGHT) + "\n"; + + if (utl::ConfigManager::getProductName() == "LibreOffice") + aCopyrightString += CuiResId(RID_CUISTR_ABOUT_BASED_ON); + else + aCopyrightString += CuiResId(RID_CUISTR_ABOUT_DERIVED); + + return aCopyrightString; +} + +// special labels to comply with previous version info +// untranslated English for QA +IMPL_LINK_NOARG(AboutDialog, HandleClick, weld::Button &, void) { + css::uno::Reference xClipboard = + css::datatransfer::clipboard::SystemClipboard::create( + comphelper::getProcessComponentContext()); + + OUString sInfo = "Version: " + m_pVersionLabel->get_label() + "\n" // version + "Build ID: " + GetBuildString() + "\n" + // build id + Application::GetHWOSConfInfo(0,false) + "\n" // env+UI + "Locale: " + GetLocaleString(false) + "\n" + // locale + GetMiscString(); // misc + + vcl::unohelper::TextDataObject::CopyStringTo(sInfo, xClipboard); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/colorpicker.cxx b/cui/source/dialogs/colorpicker.cxx new file mode 100644 index 0000000000..71b0d0970f --- /dev/null +++ b/cui/source/dialogs/colorpicker.cxx @@ -0,0 +1,1361 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::beans; +using namespace ::basegfx; + +namespace { + +enum class UpdateFlags +{ + NONE = 0x00, + RGB = 0x01, + CMYK = 0x02, + HSB = 0x04, + ColorChooser = 0x08, + ColorSlider = 0x10, + Hex = 0x20, + All = 0x3f, +}; + +} + +namespace o3tl { + template<> struct typed_flags : is_typed_flags {}; +} + + +namespace cui +{ + +namespace { + +enum class ColorComponent { + Red, + Green, + Blue, + Hue, + Saturation, + Brightness, + Cyan, + Yellow, + Magenta, + Key, +}; + +} + +// color space conversion helpers + +static void RGBtoHSV( double dR, double dG, double dB, double& dH, double& dS, double& dV ) +{ + BColor result = basegfx::utils::rgb2hsv( BColor( dR, dG, dB ) ); + + dH = result.getX(); + dS = result.getY(); + dV = result.getZ(); +} + +static void HSVtoRGB(double dH, double dS, double dV, double& dR, double& dG, double& dB ) +{ + BColor result = basegfx::utils::hsv2rgb( BColor( dH, dS, dV ) ); + + dR = result.getRed(); + dG = result.getGreen(); + dB = result.getBlue(); +} + +// CMYK values from 0 to 1 +static void CMYKtoRGB( double fCyan, double fMagenta, double fYellow, double fKey, double& dR, double& dG, double& dB ) +{ + fCyan = (fCyan * ( 1.0 - fKey )) + fKey; + fMagenta = (fMagenta * ( 1.0 - fKey )) + fKey; + fYellow = (fYellow * ( 1.0 - fKey )) + fKey; + + dR = std::clamp( 1.0 - fCyan, 0.0, 1.0 ); + dG = std::clamp( 1.0 - fMagenta, 0.0, 1.0 ); + dB = std::clamp( 1.0 - fYellow, 0.0, 1.0 ); +} + +// CMY results from 0 to 1 +static void RGBtoCMYK( double dR, double dG, double dB, double& fCyan, double& fMagenta, double& fYellow, double& fKey ) +{ + fCyan = 1 - dR; + fMagenta = 1 - dG; + fYellow = 1 - dB; + + //CMYK and CMY values from 0 to 1 + fKey = 1.0; + if( fCyan < fKey ) fKey = fCyan; + if( fMagenta < fKey ) fKey = fMagenta; + if( fYellow < fKey ) fKey = fYellow; + + if( fKey >= 1.0 ) + { + //Black + fCyan = 0.0; + fMagenta = 0.0; + fYellow = 0.0; + } + else + { + fCyan = ( fCyan - fKey ) / ( 1.0 - fKey ); + fMagenta = ( fMagenta - fKey ) / ( 1.0 - fKey ); + fYellow = ( fYellow - fKey ) / ( 1.0 - fKey ); + } +} + +namespace { + +class ColorPreviewControl : public weld::CustomWidgetController +{ +private: + Color m_aColor; + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; +public: + ColorPreviewControl() + { + } + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override + { + CustomWidgetController::SetDrawingArea(pDrawingArea); + pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 10, + pDrawingArea->get_text_height() * 2); + } + + void SetColor(const Color& rCol) + { + if (rCol != m_aColor) + { + m_aColor = rCol; + Invalidate(); + } + } +}; + +} + +void ColorPreviewControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.SetFillColor(m_aColor); + rRenderContext.SetLineColor(m_aColor); + rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), GetOutputSizePixel())); +} + +namespace { + +enum ColorMode { HUE, SATURATION, BRIGHTNESS, RED, GREEN, BLUE }; + +} + +const ColorMode DefaultMode = HUE; + +namespace { + +class ColorFieldControl : public weld::CustomWidgetController +{ +public: + ColorFieldControl() + : meMode( DefaultMode ) + , mnBaseValue(USHRT_MAX) + , mdX( -1.0 ) + , mdY( -1.0 ) + , mbMouseCaptured(false) + { + } + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override + { + CustomWidgetController::SetDrawingArea(pDrawingArea); + pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 40, + pDrawingArea->get_text_height() * 10); + } + + virtual ~ColorFieldControl() override + { + mxBitmap.disposeAndClear(); + } + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void Resize() override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + + void UpdateBitmap(); + void ShowPosition( const Point& rPos, bool bUpdate ); + void UpdatePosition(); + void Modify(); + + void SetValues(sal_uInt16 nBaseValue, ColorMode eMode, double x, double y); + double GetX() const { return mdX;} + double GetY() const { return mdY;} + + void SetModifyHdl(const Link& rLink) { maModifyHdl = rLink; } + +private: + ColorMode meMode; + sal_uInt16 mnBaseValue; + double mdX; + double mdY; + bool mbMouseCaptured; + Point maPosition; + VclPtr mxBitmap; + Link maModifyHdl; + std::vector maRGB_Horiz; + std::vector maGrad_Horiz; + std::vector maPercent_Horiz; + std::vector maRGB_Vert; + std::vector maPercent_Vert; +}; + +} + +void ColorFieldControl::UpdateBitmap() +{ + const Size aSize(GetOutputSizePixel()); + + if (mxBitmap && mxBitmap->GetOutputSizePixel() != aSize) + mxBitmap.disposeAndClear(); + + const sal_Int32 nWidth = aSize.Width(); + const sal_Int32 nHeight = aSize.Height(); + + if (nWidth == 0 || nHeight == 0) + return; + + if (!mxBitmap) + { + mxBitmap = VclPtr::Create(); + mxBitmap->SetOutputSizePixel(aSize); + + maRGB_Horiz.resize( nWidth ); + maGrad_Horiz.resize( nWidth ); + maPercent_Horiz.resize( nWidth ); + + sal_uInt8* pRGB = maRGB_Horiz.data(); + sal_uInt16* pGrad = maGrad_Horiz.data(); + sal_uInt16* pPercent = maPercent_Horiz.data(); + + for( sal_Int32 x = 0; x < nWidth; x++ ) + { + *pRGB++ = static_cast((x * 256) / nWidth); + *pGrad++ = static_cast((x * 359) / nWidth); + *pPercent++ = static_cast((x * 100) / nWidth); + } + + maRGB_Vert.resize(nHeight); + maPercent_Vert.resize(nHeight); + + pRGB = maRGB_Vert.data(); + pPercent = maPercent_Vert.data(); + + sal_Int32 y = nHeight; + while (y--) + { + *pRGB++ = static_cast((y * 256) / nHeight); + *pPercent++ = static_cast((y * 100) / nHeight); + } + } + + sal_uInt8* pRGB_Horiz = maRGB_Horiz.data(); + sal_uInt16* pGrad_Horiz = maGrad_Horiz.data(); + sal_uInt16* pPercent_Horiz = maPercent_Horiz.data(); + sal_uInt8* pRGB_Vert = maRGB_Vert.data(); + sal_uInt16* pPercent_Vert = maPercent_Vert.data(); + + // this has been unlooped for performance reason, please do not merge back! + + sal_uInt16 y = nHeight,x; + + switch(meMode) + { + case HUE: + while (y--) + { + sal_uInt16 nBri = pPercent_Vert[y]; + x = nWidth; + while (x--) + { + sal_uInt16 nSat = pPercent_Horiz[x]; + mxBitmap->DrawPixel(Point(x,y), Color::HSBtoRGB(mnBaseValue, nSat, nBri)); + } + } + break; + case SATURATION: + while (y--) + { + sal_uInt16 nBri = pPercent_Vert[y]; + x = nWidth; + while (x--) + { + sal_uInt16 nHue = pGrad_Horiz[x]; + mxBitmap->DrawPixel(Point(x,y), Color::HSBtoRGB(nHue, mnBaseValue, nBri)); + } + } + break; + case BRIGHTNESS: + while (y--) + { + sal_uInt16 nSat = pPercent_Vert[y]; + x = nWidth; + while (x--) + { + sal_uInt16 nHue = pGrad_Horiz[x]; + mxBitmap->DrawPixel(Point(x,y), Color::HSBtoRGB(nHue, nSat, mnBaseValue)); + } + } + break; + case RED: + { + Color aBitmapColor; + aBitmapColor.SetRed(mnBaseValue); + while (y--) + { + aBitmapColor.SetGreen(pRGB_Vert[y]); + x = nWidth; + while (x--) + { + aBitmapColor.SetBlue(pRGB_Horiz[x]); + mxBitmap->DrawPixel(Point(x,y), aBitmapColor); + } + } + break; + } + case GREEN: + { + Color aBitmapColor; + aBitmapColor.SetGreen(mnBaseValue); + while (y--) + { + aBitmapColor.SetRed(pRGB_Vert[y]); + x = nWidth; + while (x--) + { + aBitmapColor.SetBlue(pRGB_Horiz[x]); + mxBitmap->DrawPixel(Point(x,y), aBitmapColor); + } + } + break; + } + case BLUE: + { + Color aBitmapColor; + aBitmapColor.SetBlue(mnBaseValue); + while (y--) + { + aBitmapColor.SetGreen(pRGB_Vert[y]); + x = nWidth; + while (x--) + { + aBitmapColor.SetRed(pRGB_Horiz[x]); + mxBitmap->DrawPixel(Point(x,y), aBitmapColor); + } + } + break; + } + } +} + +constexpr int nCenterOffset = 5; + +void ColorFieldControl::ShowPosition( const Point& rPos, bool bUpdate ) +{ + if (!mxBitmap) + { + UpdateBitmap(); + Invalidate(); + } + + if (!mxBitmap) + return; + + const Size aSize(mxBitmap->GetOutputSizePixel()); + + tools::Long nX = rPos.X(); + tools::Long nY = rPos.Y(); + if (nX < 0) + nX = 0; + else if (nX >= aSize.Width()) + nX = aSize.Width() - 1; + + if (nY < 0) + nY = 0; + else if (nY >= aSize.Height()) + nY = aSize.Height() - 1; + + Point aPos = maPosition; + maPosition.setX( nX - nCenterOffset ); + maPosition.setY( nY - nCenterOffset ); + Invalidate(tools::Rectangle(aPos, Size(11, 11))); + Invalidate(tools::Rectangle(maPosition, Size(11, 11))); + + if (bUpdate) + { + mdX = double(nX) / double(aSize.Width() - 1.0); + mdY = double(aSize.Height() - 1.0 - nY) / double(aSize.Height() - 1.0); + } +} + +bool ColorFieldControl::MouseButtonDown(const MouseEvent& rMEvt) +{ + CaptureMouse(); + mbMouseCaptured = true; + ShowPosition(rMEvt.GetPosPixel(), true); + Modify(); + return true; +} + +bool ColorFieldControl::MouseMove(const MouseEvent& rMEvt) +{ + if (mbMouseCaptured) + { + ShowPosition(rMEvt.GetPosPixel(), true); + Modify(); + } + return true; +} + +bool ColorFieldControl::MouseButtonUp(const MouseEvent&) +{ + ReleaseMouse(); + mbMouseCaptured = false; + return true; +} + +void ColorFieldControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + if (!mxBitmap) + UpdateBitmap(); + + if (!mxBitmap) + return; + + Size aSize(GetOutputSizePixel()); + rRenderContext.DrawOutDev(Point(0, 0), aSize, Point(0, 0), aSize, *mxBitmap); + + // draw circle around current color + Point aPos(maPosition.X() + nCenterOffset, maPosition.Y() + nCenterOffset); + Color aColor = mxBitmap->GetPixel(aPos); + if (aColor.IsDark()) + rRenderContext.SetLineColor(COL_WHITE); + else + rRenderContext.SetLineColor(COL_BLACK); + + rRenderContext.SetFillColor(); + rRenderContext.DrawEllipse(::tools::Rectangle(maPosition, Size(11, 11))); +} + +void ColorFieldControl::Resize() +{ + CustomWidgetController::Resize(); + UpdateBitmap(); + UpdatePosition(); +} + +void ColorFieldControl::Modify() +{ + maModifyHdl.Call( *this ); +} + +void ColorFieldControl::SetValues(sal_uInt16 nBaseValue, ColorMode eMode, double x, double y) +{ + bool bUpdateBitmap = (mnBaseValue != nBaseValue) || (meMode != eMode); + if (!bUpdateBitmap && mdX == x && mdY == y) + return; + + mnBaseValue = nBaseValue; + meMode = eMode; + mdX = x; + mdY = y; + + if (bUpdateBitmap) + UpdateBitmap(); + UpdatePosition(); + if (bUpdateBitmap) + Invalidate(); +} + +void ColorFieldControl::UpdatePosition() +{ + Size aSize(GetOutputSizePixel()); + ShowPosition(Point(static_cast(mdX * aSize.Width()), static_cast((1.0 - mdY) * aSize.Height())), false); +} + +namespace { + +class ColorSliderControl : public weld::CustomWidgetController +{ +public: + ColorSliderControl(); + virtual ~ColorSliderControl() override; + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + virtual void Resize() override; + + void UpdateBitmap(); + void ChangePosition( tools::Long nY ); + void Modify(); + + void SetValue( const Color& rColor, ColorMode eMode, double dValue ); + double GetValue() const { return mdValue; } + + void SetModifyHdl( const Link& rLink ) { maModifyHdl = rLink; } + + sal_Int16 GetLevel() const { return mnLevel; } + +private: + Link maModifyHdl; + Color maColor; + ColorMode meMode; + VclPtr mxBitmap; + sal_Int16 mnLevel; + double mdValue; +}; + +} + +ColorSliderControl::ColorSliderControl() + : meMode( DefaultMode ) + , mnLevel( 0 ) + , mdValue( -1.0 ) +{ +} + +void ColorSliderControl::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 3, -1); +} + +ColorSliderControl::~ColorSliderControl() +{ + mxBitmap.disposeAndClear(); +} + +void ColorSliderControl::UpdateBitmap() +{ + Size aSize(1, GetOutputSizePixel().Height()); + + if (mxBitmap && mxBitmap->GetOutputSizePixel() != aSize) + mxBitmap.disposeAndClear(); + + if (!mxBitmap) + { + mxBitmap = VclPtr::Create(); + mxBitmap->SetOutputSizePixel(aSize); + } + + const tools::Long nY = aSize.Height() - 1; + + Color aBitmapColor(maColor); + + sal_uInt16 nHue, nSat, nBri; + maColor.RGBtoHSB(nHue, nSat, nBri); + + // this has been unlooped for performance reason, please do not merge back! + + switch (meMode) + { + case HUE: + nSat = 100; + nBri = 100; + for (tools::Long y = 0; y <= nY; y++) + { + nHue = static_cast((359 * y) / nY); + mxBitmap->DrawPixel(Point(0, nY - y), Color::HSBtoRGB(nHue, nSat, nBri)); + } + break; + + case SATURATION: + nBri = std::max(sal_uInt16(32), nBri); + for (tools::Long y = 0; y <= nY; y++) + { + nSat = static_cast((100 * y) / nY); + mxBitmap->DrawPixel(Point(0, nY - y), Color::HSBtoRGB(nHue, nSat, nBri)); + } + break; + + case BRIGHTNESS: + for (tools::Long y = 0; y <= nY; y++) + { + nBri = static_cast((100 * y) / nY); + mxBitmap->DrawPixel(Point(0, nY - y), Color::HSBtoRGB(nHue, nSat, nBri)); + } + break; + + case RED: + for (tools::Long y = 0; y <= nY; y++) + { + aBitmapColor.SetRed(sal_uInt8((tools::Long(255) * y) / nY)); + mxBitmap->DrawPixel(Point(0, nY - y), aBitmapColor); + } + break; + + case GREEN: + for (tools::Long y = 0; y <= nY; y++) + { + aBitmapColor.SetGreen(sal_uInt8((tools::Long(255) * y) / nY)); + mxBitmap->DrawPixel(Point(0, nY - y), aBitmapColor); + } + break; + + case BLUE: + for (tools::Long y = 0; y <= nY; y++) + { + aBitmapColor.SetBlue(sal_uInt8((tools::Long(255) * y) / nY)); + mxBitmap->DrawPixel(Point(0, nY - y), aBitmapColor); + } + break; + } +} + +void ColorSliderControl::ChangePosition(tools::Long nY) +{ + const tools::Long nHeight = GetOutputSizePixel().Height() - 1; + + if (nY < 0) + nY = 0; + else if (nY > nHeight) + nY = nHeight; + + mnLevel = nY; + mdValue = double(nHeight - nY) / double(nHeight); +} + +bool ColorSliderControl::MouseButtonDown(const MouseEvent& rMEvt) +{ + CaptureMouse(); + ChangePosition(rMEvt.GetPosPixel().Y()); + Modify(); + return true; +} + +bool ColorSliderControl::MouseMove(const MouseEvent& rMEvt) +{ + if (IsMouseCaptured()) + { + ChangePosition(rMEvt.GetPosPixel().Y()); + Modify(); + } + return true; +} + +bool ColorSliderControl::MouseButtonUp(const MouseEvent&) +{ + ReleaseMouse(); + return true; +} + +void ColorSliderControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + if (!mxBitmap) + UpdateBitmap(); + + const Size aSize(GetOutputSizePixel()); + + Point aPos; + int x = aSize.Width(); + while (x--) + { + rRenderContext.DrawOutDev(aPos, aSize, Point(0,0), aSize, *mxBitmap); + aPos.AdjustX(1); + } +} + +void ColorSliderControl::Resize() +{ + CustomWidgetController::Resize(); + UpdateBitmap(); +} + +void ColorSliderControl::Modify() +{ + maModifyHdl.Call(*this); +} + +void ColorSliderControl::SetValue(const Color& rColor, ColorMode eMode, double dValue) +{ + bool bUpdateBitmap = (rColor != maColor) || (eMode != meMode); + if( bUpdateBitmap || (mdValue != dValue)) + { + maColor = rColor; + mdValue = dValue; + mnLevel = static_cast((1.0-dValue) * GetOutputSizePixel().Height()); + meMode = eMode; + if (bUpdateBitmap) + UpdateBitmap(); + Invalidate(); + } +} + +namespace { + +class ColorPickerDialog : public SfxDialogController +{ +private: + ColorFieldControl m_aColorField; + ColorSliderControl m_aColorSlider; + ColorPreviewControl m_aColorPreview; + ColorPreviewControl m_aColorPrevious; + + std::unique_ptr m_xColorField; + std::unique_ptr m_xColorSlider; + std::unique_ptr m_xColorPreview; + std::unique_ptr m_xColorPrevious; + + std::unique_ptr m_xFISliderLeft; + std::unique_ptr m_xFISliderRight; + std::unique_ptr m_xRBRed; + std::unique_ptr m_xRBGreen; + std::unique_ptr m_xRBBlue; + std::unique_ptr m_xRBHue; + std::unique_ptr m_xRBSaturation; + std::unique_ptr m_xRBBrightness; + + std::unique_ptr m_xMFRed; + std::unique_ptr m_xMFGreen; + std::unique_ptr m_xMFBlue; + std::unique_ptr m_xEDHex; + + std::unique_ptr m_xMFHue; + std::unique_ptr m_xMFSaturation; + std::unique_ptr m_xMFBrightness; + + std::unique_ptr m_xMFCyan; + std::unique_ptr m_xMFMagenta; + std::unique_ptr m_xMFYellow; + std::unique_ptr m_xMFKey; + +public: + ColorPickerDialog(weld::Window* pParent, Color nColor, sal_Int16 nMode); + + void update_color(UpdateFlags n = UpdateFlags::All); + + DECL_LINK(ColorFieldControlModifydl, ColorFieldControl&, void); + DECL_LINK(ColorSliderControlModifyHdl, ColorSliderControl&, void); + DECL_LINK(ColorModifyMetricHdl, weld::MetricSpinButton&, void); + DECL_LINK(ColorModifySpinHdl, weld::SpinButton&, void); + DECL_LINK(ColorModifyEditHdl, weld::Entry&, void); + DECL_LINK(ModeModifyHdl, weld::Toggleable&, void); + + Color GetColor() const; + + void setColorComponent(ColorComponent nComp, double dValue); + +private: + ColorMode meMode; + + double mdRed, mdGreen, mdBlue; + double mdHue, mdSat, mdBri; + double mdCyan, mdMagenta, mdYellow, mdKey; +}; + +} + +ColorPickerDialog::ColorPickerDialog(weld::Window* pParent, Color nColor, sal_Int16 nDialogMode) + : SfxDialogController(pParent, "cui/ui/colorpickerdialog.ui", "ColorPicker") + , m_xColorField(new weld::CustomWeld(*m_xBuilder, "colorField", m_aColorField)) + , m_xColorSlider(new weld::CustomWeld(*m_xBuilder, "colorSlider", m_aColorSlider)) + , m_xColorPreview(new weld::CustomWeld(*m_xBuilder, "preview", m_aColorPreview)) + , m_xColorPrevious(new weld::CustomWeld(*m_xBuilder, "previous", m_aColorPrevious)) + , m_xFISliderLeft(m_xBuilder->weld_widget("leftImage")) + , m_xFISliderRight(m_xBuilder->weld_widget("rightImage")) + , m_xRBRed(m_xBuilder->weld_radio_button("redRadiobutton")) + , m_xRBGreen(m_xBuilder->weld_radio_button("greenRadiobutton")) + , m_xRBBlue(m_xBuilder->weld_radio_button("blueRadiobutton")) + , m_xRBHue(m_xBuilder->weld_radio_button("hueRadiobutton")) + , m_xRBSaturation(m_xBuilder->weld_radio_button("satRadiobutton")) + , m_xRBBrightness(m_xBuilder->weld_radio_button("brightRadiobutton")) + , m_xMFRed(m_xBuilder->weld_spin_button("redSpinbutton")) + , m_xMFGreen(m_xBuilder->weld_spin_button("greenSpinbutton")) + , m_xMFBlue(m_xBuilder->weld_spin_button("blueSpinbutton")) + , m_xEDHex(new weld::HexColorControl(m_xBuilder->weld_entry("hexEntry"))) + , m_xMFHue(m_xBuilder->weld_metric_spin_button("hueSpinbutton", FieldUnit::DEGREE)) + , m_xMFSaturation(m_xBuilder->weld_metric_spin_button("satSpinbutton", FieldUnit::PERCENT)) + , m_xMFBrightness(m_xBuilder->weld_metric_spin_button("brightSpinbutton", FieldUnit::PERCENT)) + , m_xMFCyan(m_xBuilder->weld_metric_spin_button("cyanSpinbutton", FieldUnit::PERCENT)) + , m_xMFMagenta(m_xBuilder->weld_metric_spin_button("magSpinbutton", FieldUnit::PERCENT)) + , m_xMFYellow(m_xBuilder->weld_metric_spin_button("yellowSpinbutton", FieldUnit::PERCENT)) + , m_xMFKey(m_xBuilder->weld_metric_spin_button("keySpinbutton", FieldUnit::PERCENT)) + , meMode( DefaultMode ) +{ + m_aColorField.SetModifyHdl( LINK( this, ColorPickerDialog, ColorFieldControlModifydl ) ); + m_aColorSlider.SetModifyHdl( LINK( this, ColorPickerDialog, ColorSliderControlModifyHdl ) ); + + int nMargin = (m_xFISliderLeft->get_preferred_size().Height() + 1) / 2; + m_xColorSlider->set_margin_top(nMargin); + m_xColorSlider->set_margin_bottom(nMargin); + + Link aLink3( LINK( this, ColorPickerDialog, ColorModifyMetricHdl ) ); + m_xMFCyan->connect_value_changed( aLink3 ); + m_xMFMagenta->connect_value_changed( aLink3 ); + m_xMFYellow->connect_value_changed( aLink3 ); + m_xMFKey->connect_value_changed( aLink3 ); + + m_xMFHue->connect_value_changed( aLink3 ); + m_xMFSaturation->connect_value_changed( aLink3 ); + m_xMFBrightness->connect_value_changed( aLink3 ); + + Link aLink4(LINK(this, ColorPickerDialog, ColorModifySpinHdl)); + m_xMFRed->connect_value_changed(aLink4); + m_xMFGreen->connect_value_changed(aLink4); + m_xMFBlue->connect_value_changed(aLink4); + + m_xEDHex->connect_changed(LINK(this, ColorPickerDialog, ColorModifyEditHdl)); + + Link aLink2 = LINK( this, ColorPickerDialog, ModeModifyHdl ); + m_xRBRed->connect_toggled( aLink2 ); + m_xRBGreen->connect_toggled( aLink2 ); + m_xRBBlue->connect_toggled( aLink2 ); + m_xRBHue->connect_toggled( aLink2 ); + m_xRBSaturation->connect_toggled( aLink2 ); + m_xRBBrightness->connect_toggled( aLink2 ); + + Color aColor(nColor); + + // modify + if (nDialogMode == 2) + { + m_aColorPrevious.SetColor(aColor); + m_xColorPrevious->show(); + } + + mdRed = static_cast(aColor.GetRed()) / 255.0; + mdGreen = static_cast(aColor.GetGreen()) / 255.0; + mdBlue = static_cast(aColor.GetBlue()) / 255.0; + + RGBtoHSV( mdRed, mdGreen, mdBlue, mdHue, mdSat, mdBri ); + RGBtoCMYK( mdRed, mdGreen, mdBlue, mdCyan, mdMagenta, mdYellow, mdKey ); + + update_color(); +} + +static int toInt( double dValue, double dRange ) +{ + return static_cast< int >( std::floor((dValue * dRange) + 0.5 ) ); +} + +Color ColorPickerDialog::GetColor() const +{ + return Color( toInt(mdRed,255.0), toInt(mdGreen,255.0), toInt(mdBlue,255.0) ); +} + +void ColorPickerDialog::update_color( UpdateFlags n ) +{ + sal_uInt8 nRed = toInt(mdRed,255.0); + sal_uInt8 nGreen = toInt(mdGreen,255.0); + sal_uInt8 nBlue = toInt(mdBlue,255.0); + + sal_uInt16 nHue = toInt(mdHue, 1.0); + sal_uInt16 nSat = toInt(mdSat, 100.0); + sal_uInt16 nBri = toInt(mdBri, 100.0); + + if (n & UpdateFlags::RGB) // update RGB + { + m_xMFRed->set_value(nRed); + m_xMFGreen->set_value(nGreen); + m_xMFBlue->set_value(nBlue); + } + + if (n & UpdateFlags::CMYK) // update CMYK + { + m_xMFCyan->set_value(toInt(mdCyan, 100.0), FieldUnit::PERCENT); + m_xMFMagenta->set_value(toInt(mdMagenta, 100.0), FieldUnit::PERCENT); + m_xMFYellow->set_value(toInt(mdYellow, 100.0), FieldUnit::PERCENT); + m_xMFKey->set_value(toInt(mdKey, 100.0), FieldUnit::PERCENT); + } + + if (n & UpdateFlags::HSB ) // update HSB + { + m_xMFHue->set_value(nHue, FieldUnit::DEGREE); + m_xMFSaturation->set_value(nSat, FieldUnit::PERCENT); + m_xMFBrightness->set_value(nBri, FieldUnit::PERCENT); + } + + if (n & UpdateFlags::ColorChooser ) // update Color Chooser 1 + { + switch( meMode ) + { + case HUE: + m_aColorField.SetValues(nHue, meMode, mdSat, mdBri); + break; + case SATURATION: + m_aColorField.SetValues(nSat, meMode, mdHue / 360.0, mdBri); + break; + case BRIGHTNESS: + m_aColorField.SetValues(nBri, meMode, mdHue / 360.0, mdSat); + break; + case RED: + m_aColorField.SetValues(nRed, meMode, mdBlue, mdGreen); + break; + case GREEN: + m_aColorField.SetValues(nGreen, meMode, mdBlue, mdRed); + break; + case BLUE: + m_aColorField.SetValues(nBlue, meMode, mdRed, mdGreen); + break; + } + } + + Color aColor(nRed, nGreen, nBlue); + + if (n & UpdateFlags::ColorSlider) // update Color Chooser 2 + { + switch (meMode) + { + case HUE: + m_aColorSlider.SetValue(aColor, meMode, mdHue / 360.0); + break; + case SATURATION: + m_aColorSlider.SetValue(aColor, meMode, mdSat); + break; + case BRIGHTNESS: + m_aColorSlider.SetValue(aColor, meMode, mdBri); + break; + case RED: + m_aColorSlider.SetValue(aColor, meMode, mdRed); + break; + case GREEN: + m_aColorSlider.SetValue(aColor, meMode, mdGreen); + break; + case BLUE: + m_aColorSlider.SetValue(aColor, meMode, mdBlue); + break; + } + } + + if (n & UpdateFlags::Hex) // update hex + { + m_xFISliderLeft->set_margin_top(m_aColorSlider.GetLevel()); + m_xFISliderRight->set_margin_top(m_aColorSlider.GetLevel()); + m_xEDHex->SetColor(aColor); + } + m_aColorPreview.SetColor(aColor); +} + +IMPL_LINK_NOARG(ColorPickerDialog, ColorFieldControlModifydl, ColorFieldControl&, void) +{ + double x = m_aColorField.GetX(); + double y = m_aColorField.GetY(); + + switch( meMode ) + { + case HUE: + mdSat = x; + setColorComponent( ColorComponent::Brightness, y ); + break; + case SATURATION: + mdHue = x * 360.0; + setColorComponent( ColorComponent::Brightness, y ); + break; + case BRIGHTNESS: + mdHue = x * 360.0; + setColorComponent( ColorComponent::Saturation, y ); + break; + case RED: + mdBlue = x; + setColorComponent( ColorComponent::Green, y ); + break; + case GREEN: + mdBlue = x; + setColorComponent( ColorComponent::Red, y ); + break; + case BLUE: + mdRed = x; + setColorComponent( ColorComponent::Green, y ); + break; + } + + update_color(UpdateFlags::All & ~UpdateFlags::ColorChooser); +} + +IMPL_LINK_NOARG(ColorPickerDialog, ColorSliderControlModifyHdl, ColorSliderControl&, void) +{ + double dValue = m_aColorSlider.GetValue(); + switch (meMode) + { + case HUE: + setColorComponent( ColorComponent::Hue, dValue * 360.0 ); + break; + case SATURATION: + setColorComponent( ColorComponent::Saturation, dValue ); + break; + case BRIGHTNESS: + setColorComponent( ColorComponent::Brightness, dValue ); + break; + case RED: + setColorComponent( ColorComponent::Red, dValue ); + break; + case GREEN: + setColorComponent( ColorComponent::Green, dValue ); + break; + case BLUE: + setColorComponent( ColorComponent::Blue, dValue ); + break; + } + + update_color(UpdateFlags::All & ~UpdateFlags::ColorSlider); +} + +IMPL_LINK(ColorPickerDialog, ColorModifyMetricHdl, weld::MetricSpinButton&, rEdit, void) +{ + UpdateFlags n = UpdateFlags::NONE; + + if (&rEdit == m_xMFHue.get()) + { + setColorComponent( ColorComponent::Hue, static_cast(m_xMFHue->get_value(FieldUnit::DEGREE)) ); + n = UpdateFlags::All & ~UpdateFlags::HSB; + } + else if (&rEdit == m_xMFSaturation.get()) + { + setColorComponent( ColorComponent::Saturation, static_cast(m_xMFSaturation->get_value(FieldUnit::PERCENT)) / 100.0 ); + n = UpdateFlags::All & ~UpdateFlags::HSB; + } + else if (&rEdit == m_xMFBrightness.get()) + { + setColorComponent( ColorComponent::Brightness, static_cast(m_xMFBrightness->get_value(FieldUnit::PERCENT)) / 100.0 ); + n = UpdateFlags::All & ~UpdateFlags::HSB; + } + else if (&rEdit == m_xMFCyan.get()) + { + setColorComponent( ColorComponent::Cyan, static_cast(m_xMFCyan->get_value(FieldUnit::PERCENT)) / 100.0 ); + n = UpdateFlags::All & ~UpdateFlags::CMYK; + } + else if (&rEdit == m_xMFMagenta.get()) + { + setColorComponent( ColorComponent::Magenta, static_cast(m_xMFMagenta->get_value(FieldUnit::PERCENT)) / 100.0 ); + n = UpdateFlags::All & ~UpdateFlags::CMYK; + } + else if (&rEdit == m_xMFYellow.get()) + { + setColorComponent( ColorComponent::Yellow, static_cast(m_xMFYellow->get_value(FieldUnit::PERCENT)) / 100.0 ); + n = UpdateFlags::All & ~UpdateFlags::CMYK; + } + else if (&rEdit == m_xMFKey.get()) + { + setColorComponent( ColorComponent::Key, static_cast(m_xMFKey->get_value(FieldUnit::PERCENT)) / 100.0 ); + n = UpdateFlags::All & ~UpdateFlags::CMYK; + } + + if (n != UpdateFlags::NONE) + update_color(n); +} + +IMPL_LINK_NOARG(ColorPickerDialog, ColorModifyEditHdl, weld::Entry&, void) +{ + UpdateFlags n = UpdateFlags::NONE; + + Color aColor = m_xEDHex->GetColor(); + + if (aColor != COL_AUTO && aColor != GetColor()) + { + mdRed = static_cast(aColor.GetRed()) / 255.0; + mdGreen = static_cast(aColor.GetGreen()) / 255.0; + mdBlue = static_cast(aColor.GetBlue()) / 255.0; + + RGBtoHSV( mdRed, mdGreen, mdBlue, mdHue, mdSat, mdBri ); + RGBtoCMYK( mdRed, mdGreen, mdBlue, mdCyan, mdMagenta, mdYellow, mdKey ); + n = UpdateFlags::All & ~UpdateFlags::Hex; + } + + if (n != UpdateFlags::NONE) + update_color(n); +} + +IMPL_LINK(ColorPickerDialog, ColorModifySpinHdl, weld::SpinButton&, rEdit, void) +{ + UpdateFlags n = UpdateFlags::NONE; + + if (&rEdit == m_xMFRed.get()) + { + setColorComponent( ColorComponent::Red, static_cast(m_xMFRed->get_value()) / 255.0 ); + n = UpdateFlags::All & ~UpdateFlags::RGB; + } + else if (&rEdit == m_xMFGreen.get()) + { + setColorComponent( ColorComponent::Green, static_cast(m_xMFGreen->get_value()) / 255.0 ); + n = UpdateFlags::All & ~UpdateFlags::RGB; + } + else if (&rEdit == m_xMFBlue.get()) + { + setColorComponent( ColorComponent::Blue, static_cast(m_xMFBlue->get_value()) / 255.0 ); + n = UpdateFlags::All & ~UpdateFlags::RGB; + } + + if (n != UpdateFlags::NONE) + update_color(n); +} + + +IMPL_LINK_NOARG(ColorPickerDialog, ModeModifyHdl, weld::Toggleable&, void) +{ + ColorMode eMode = HUE; + + if (m_xRBRed->get_active()) + { + eMode = RED; + } + else if (m_xRBGreen->get_active()) + { + eMode = GREEN; + } + else if (m_xRBBlue->get_active()) + { + eMode = BLUE; + } + else if (m_xRBSaturation->get_active()) + { + eMode = SATURATION; + } + else if (m_xRBBrightness->get_active()) + { + eMode = BRIGHTNESS; + } + + if (meMode != eMode) + { + meMode = eMode; + update_color(UpdateFlags::ColorChooser | UpdateFlags::ColorSlider); + } +} + +void ColorPickerDialog::setColorComponent( ColorComponent nComp, double dValue ) +{ + switch( nComp ) + { + case ColorComponent::Red: + mdRed = dValue; + break; + case ColorComponent::Green: + mdGreen = dValue; + break; + case ColorComponent::Blue: + mdBlue = dValue; + break; + case ColorComponent::Hue: + mdHue = dValue; + break; + case ColorComponent::Saturation: + mdSat = dValue; + break; + case ColorComponent::Brightness: + mdBri = dValue; + break; + case ColorComponent::Cyan: + mdCyan = dValue; + break; + case ColorComponent::Yellow: + mdYellow = dValue; + break; + case ColorComponent::Magenta: + mdMagenta = dValue; + break; + case ColorComponent::Key: + mdKey = dValue; + break; + } + + if (nComp == ColorComponent::Red || nComp == ColorComponent::Green || nComp == ColorComponent::Blue) + { + RGBtoHSV( mdRed, mdGreen, mdBlue, mdHue, mdSat, mdBri ); + RGBtoCMYK( mdRed, mdGreen, mdBlue, mdCyan, mdMagenta, mdYellow, mdKey ); + } + else if (nComp == ColorComponent::Hue || nComp == ColorComponent::Saturation || nComp == ColorComponent::Brightness) + { + HSVtoRGB( mdHue, mdSat, mdBri, mdRed, mdGreen, mdBlue ); + RGBtoCMYK( mdRed, mdGreen, mdBlue, mdCyan, mdMagenta, mdYellow, mdKey ); + } + else + { + CMYKtoRGB( mdCyan, mdMagenta, mdYellow, mdKey, mdRed, mdGreen, mdBlue ); + RGBtoHSV( mdRed, mdGreen, mdBlue, mdHue, mdSat, mdBri ); + } +} + +typedef ::comphelper::WeakComponentImplHelper< XServiceInfo, XExecutableDialog, XAsynchronousExecutableDialog, XInitialization, XPropertyAccess > ColorPickerBase; + +namespace { + +class ColorPicker : public ColorPickerBase +{ +public: + explicit ColorPicker(); + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + + // XInitialization + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XPropertyAccess + virtual Sequence< PropertyValue > SAL_CALL getPropertyValues( ) override; + virtual void SAL_CALL setPropertyValues( const Sequence< PropertyValue >& aProps ) override; + + // XExecutableDialog + virtual void SAL_CALL setTitle( const OUString& aTitle ) override; + virtual sal_Int16 SAL_CALL execute( ) override; + + // XAsynchronousExecutableDialog + virtual void SAL_CALL setDialogTitle( const OUString& aTitle ) override; + virtual void SAL_CALL startExecuteModal( const css::uno::Reference< css::ui::dialogs::XDialogClosedListener >& xListener ) override; + +private: + Color mnColor; + sal_Int16 mnMode; + Reference mxParent; +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_cui_ColorPicker_get_implementation( + css::uno::XComponentContext*, css::uno::Sequence const&) +{ + return cppu::acquire( new ColorPicker ); +} + + +constexpr OUString gsColorKey( u"Color"_ustr ); +constexpr OUStringLiteral gsModeKey( u"Mode" ); + +ColorPicker::ColorPicker() + : mnColor( 0 ) + , mnMode( 0 ) +{ +} + +// XInitialization +void SAL_CALL ColorPicker::initialize( const Sequence< Any >& aArguments ) +{ + if( aArguments.getLength() == 1 ) + { + aArguments[0] >>= mxParent; + } +} + +// XInitialization +OUString SAL_CALL ColorPicker::getImplementationName( ) +{ + return "com.sun.star.cui.ColorPicker"; +} + +sal_Bool SAL_CALL ColorPicker::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +Sequence< OUString > SAL_CALL ColorPicker::getSupportedServiceNames( ) +{ + return { "com.sun.star.ui.dialogs.ColorPicker", + "com.sun.star.ui.dialogs.AsynchronousColorPicker" }; +} + +// XPropertyAccess +Sequence< PropertyValue > SAL_CALL ColorPicker::getPropertyValues( ) +{ + Sequence< PropertyValue > props{ comphelper::makePropertyValue(gsColorKey, mnColor) }; + return props; +} + +void SAL_CALL ColorPicker::setPropertyValues( const Sequence< PropertyValue >& aProps ) +{ + for ( const PropertyValue& rProp : aProps ) + { + if( rProp.Name == gsColorKey ) + { + rProp.Value >>= mnColor; + } + else if( rProp.Name == gsModeKey ) + { + rProp.Value >>= mnMode; + } + } +} + +// XExecutableDialog +void SAL_CALL ColorPicker::setTitle( const OUString& ) +{ +} + +sal_Int16 SAL_CALL ColorPicker::execute() +{ + std::unique_ptr xDlg(new ColorPickerDialog(Application::GetFrameWeld(mxParent), mnColor, mnMode)); + sal_Int16 ret = xDlg->run(); + if (ret) + mnColor = xDlg->GetColor(); + return ret; +} + +// XAsynchronousExecutableDialog +void SAL_CALL ColorPicker::setDialogTitle( const OUString& ) +{ +} + +void SAL_CALL ColorPicker::startExecuteModal( const css::uno::Reference< css::ui::dialogs::XDialogClosedListener >& xListener ) +{ + std::shared_ptr xDlg = std::make_shared(Application::GetFrameWeld(mxParent), mnColor, mnMode); + rtl::Reference xThis(this); + weld::DialogController::runAsync(xDlg, [xThis, xDlg, xListener] (sal_Int32 nResult) { + if (nResult) + xThis->mnColor = xDlg->GetColor(); + + sal_Int16 nRet = static_cast(nResult); + css::ui::dialogs::DialogClosedEvent aEvent( *xThis, nRet ); + xListener->dialogClosed( aEvent ); + }); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/cuicharmap.cxx b/cui/source/dialogs/cuicharmap.cxx new file mode 100644 index 0000000000..03d1e8c90b --- /dev/null +++ b/cui/source/dialogs/cuicharmap.cxx @@ -0,0 +1,890 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace css; + +SvxCharacterMap::SvxCharacterMap(weld::Widget* pParent, const SfxItemSet* pSet, + css::uno::Reference xFrame) + : SfxDialogController(pParent, "cui/ui/specialcharacters.ui", "SpecialCharactersDialog") + , m_xVirDev(VclPtr::Create()) + , isSearchMode(true) + , m_xFrame(std::move(xFrame)) + , m_aCharmapContents(*m_xBuilder, m_xVirDev, true) + , m_aShowChar(m_xVirDev) + , m_xOKBtn(m_xFrame.is() ? m_xBuilder->weld_button("insert") : m_xBuilder->weld_button("ok")) + , m_xFontText(m_xBuilder->weld_label("fontft")) + , m_xFontLB(m_xBuilder->weld_combo_box("fontlb")) + , m_xSubsetText(m_xBuilder->weld_label("subsetft")) + , m_xSubsetLB(m_xBuilder->weld_combo_box("subsetlb")) + , m_xSearchText(m_xBuilder->weld_entry("search")) + , m_xHexCodeText(m_xBuilder->weld_entry("hexvalue")) + , m_xDecimalCodeText(m_xBuilder->weld_entry("decimalvalue")) + , m_xFavouritesBtn(m_xBuilder->weld_button("favbtn")) + , m_xCharName(m_xBuilder->weld_label("charname")) + , m_xShowChar(new weld::CustomWeld(*m_xBuilder, "showchar", m_aShowChar)) + , m_xShowSet(new SvxShowCharSet(m_xBuilder->weld_scrolled_window("showscroll", true), m_xVirDev)) + , m_xShowSetArea(new weld::CustomWeld(*m_xBuilder, "showcharset", *m_xShowSet)) + , m_xSearchSet(new SvxSearchCharSet(m_xBuilder->weld_scrolled_window("searchscroll", true), m_xVirDev)) + , m_xSearchSetArea(new weld::CustomWeld(*m_xBuilder, "searchcharset", *m_xSearchSet)) +{ + m_aShowChar.SetCentered(true); + m_xFontLB->make_sorted(); + //lock the size request of this widget to the width of all possible entries + fillAllSubsets(*m_xSubsetLB); + m_xSubsetLB->set_size_request(m_xSubsetLB->get_preferred_size().Width(), -1); + m_xCharName->set_size_request(m_aShowChar.get_preferred_size().Width(), m_xCharName->get_text_height() * 4); + //lock the size request of this widget to the width of the original .ui string + m_xHexCodeText->set_size_request(m_xHexCodeText->get_preferred_size().Width(), -1); + + init(); + + const SfxInt32Item* pCharItem = SfxItemSet::GetItem(pSet, SID_ATTR_CHAR, false); + if ( pCharItem ) + SetChar( pCharItem->GetValue() ); + + const SfxBoolItem* pDisableItem = SfxItemSet::GetItem(pSet, FN_PARAM_2, false); + if ( pDisableItem && pDisableItem->GetValue() ) + DisableFontSelection(); + + const SvxFontItem* pFontItem = SfxItemSet::GetItem(pSet, SID_ATTR_CHAR_FONT, false); + const SfxStringItem* pFontNameItem = SfxItemSet::GetItem(pSet, SID_FONT_NAME, false); + if ( pFontItem ) + { + vcl::Font aTmpFont( pFontItem->GetFamilyName(), pFontItem->GetStyleName(), GetCharFont().GetFontSize() ); + aTmpFont.SetCharSet( pFontItem->GetCharSet() ); + aTmpFont.SetPitch( pFontItem->GetPitch() ); + SetCharFont( aTmpFont ); + } + else if ( pFontNameItem ) + { + vcl::Font aTmpFont( GetCharFont() ); + aTmpFont.SetFamilyName( pFontNameItem->GetValue() ); + SetCharFont( aTmpFont ); + } + + m_xOutputSet.reset(new SfxAllItemSet(pSet ? *pSet->GetPool() : SfxGetpApp()->GetPool())); + m_xShowSet->Show(); + m_xSearchSet->Hide(); +} + +short SvxCharacterMap::run() +{ + if( SvxShowCharSet::getSelectedChar() == ' ') + { + m_xOKBtn->set_sensitive(false); + setFavButtonState(u"", u""); + } + else + { + sal_UCS4 cChar = m_xShowSet->GetSelectCharacter(); + // using the new UCS4 constructor + OUString aOUStr( &cChar, 1 ); + m_aShowChar.SetText(aOUStr); + + setFavButtonState(aOUStr, m_aShowChar.GetFont().GetFamilyName()); + m_xOKBtn->set_sensitive(true); + } + + return SfxDialogController::run(); +} + +void SvxCharacterMap::SetChar( sal_UCS4 c ) +{ + m_xShowSet->SelectCharacter( c ); + setFavButtonState(OUString(&c, 1), aFont.GetFamilyName()); +} + +sal_UCS4 SvxCharacterMap::GetChar() const +{ + return m_aShowChar.GetText().iterateCodePoints(&o3tl::temporary(sal_Int32(0))); +} + +void SvxCharacterMap::DisableFontSelection() +{ + m_xFontText->set_sensitive(false); + m_xFontLB->set_sensitive(false); +} + +IMPL_LINK_NOARG(SvxCharacterMap, UpdateFavHdl, void*, void) +{ + m_xShowSet->getFavCharacterList(); + m_xSearchSet->getFavCharacterList(); + // tdf#109214 - redraw highlight of the favorite characters + m_xShowSet->Invalidate(); +} + +void SvxCharacterMap::init() +{ + aFont = m_xVirDev->GetFont(); + aFont.SetTransparent( true ); + aFont.SetFamily( FAMILY_DONTKNOW ); + aFont.SetPitch( PITCH_DONTKNOW ); + aFont.SetCharSet( RTL_TEXTENCODING_DONTKNOW ); + + OUString aDefStr( aFont.GetFamilyName() ); + OUString aLastName; + int nCount = m_xVirDev->GetFontFaceCollectionCount(); + std::vector aEntries; + aEntries.reserve(nCount); + for (int i = 0; i < nCount; ++i) + { + OUString aFontName( m_xVirDev->GetFontMetricFromCollection( i ).GetFamilyName() ); + if (aFontName != aLastName) + { + aLastName = aFontName; + aEntries.emplace_back(aFontName, OUString::number(i)); + } + } + m_xFontLB->insert_vector(aEntries, true); + // the font may not be in the list => + // try to find a font name token in list and select found font, + // else select topmost entry + bool bFound = (m_xFontLB->find_text(aDefStr) != -1); + if (!bFound) + { + sal_Int32 nIndex = 0; + do + { + OUString aToken = aDefStr.getToken(0, ';', nIndex); + if (m_xFontLB->find_text(aToken) != -1) + { + aDefStr = aToken; + bFound = true; + break; + } + } + while ( nIndex >= 0 ); + } + + if (bFound) + m_xFontLB->set_active_text(aDefStr); + else if (m_xFontLB->get_count() ) + m_xFontLB->set_active(0); + FontSelectHdl(*m_xFontLB); + if (m_xSubsetLB->get_count()) + m_xSubsetLB->set_active(0); + + m_xFontLB->connect_changed(LINK( this, SvxCharacterMap, FontSelectHdl)); + m_xSubsetLB->connect_changed(LINK( this, SvxCharacterMap, SubsetSelectHdl)); + m_xOKBtn->connect_clicked(LINK(this, SvxCharacterMap, InsertClickHdl)); + m_xOKBtn->show(); + + m_xShowSet->SetDoubleClickHdl( LINK( this, SvxCharacterMap, CharDoubleClickHdl ) ); + m_xShowSet->SetReturnKeyPressHdl(LINK(this, SvxCharacterMap, ReturnKeypressOnCharHdl)); + m_xShowSet->SetSelectHdl( LINK( this, SvxCharacterMap, CharSelectHdl ) ); + m_xShowSet->SetHighlightHdl( LINK( this, SvxCharacterMap, CharHighlightHdl ) ); + m_xShowSet->SetPreSelectHdl( LINK( this, SvxCharacterMap, CharPreSelectHdl ) ); + m_xShowSet->SetFavClickHdl( LINK( this, SvxCharacterMap, FavClickHdl ) ); + + m_xSearchSet->SetDoubleClickHdl( LINK( this, SvxCharacterMap, CharDoubleClickHdl ) ); + m_xSearchSet->SetReturnKeyPressHdl(LINK(this, SvxCharacterMap, ReturnKeypressOnCharHdl)); + m_xSearchSet->SetSelectHdl( LINK( this, SvxCharacterMap, CharSelectHdl ) ); + m_xSearchSet->SetHighlightHdl( LINK( this, SvxCharacterMap, SearchCharHighlightHdl ) ); + m_xSearchSet->SetPreSelectHdl( LINK( this, SvxCharacterMap, CharPreSelectHdl ) ); + m_xSearchSet->SetFavClickHdl( LINK( this, SvxCharacterMap, FavClickHdl ) ); + + m_xDecimalCodeText->connect_changed( LINK( this, SvxCharacterMap, DecimalCodeChangeHdl ) ); + m_xHexCodeText->connect_changed( LINK( this, SvxCharacterMap, HexCodeChangeHdl ) ); + m_xFavouritesBtn->connect_clicked( LINK(this, SvxCharacterMap, FavSelectHdl)); + + // tdf#117038 set the buttons width to its max possible width so it doesn't + // make layout change when the label changes + m_xFavouritesBtn->set_label(CuiResId(RID_CUISTR_REMOVE_FAVORITES)); + auto nMaxWidth = m_xFavouritesBtn->get_preferred_size().Width(); + m_xFavouritesBtn->set_label(CuiResId(RID_CUISTR_ADD_FAVORITES)); + nMaxWidth = std::max(nMaxWidth, m_xFavouritesBtn->get_preferred_size().Width()); + m_xFavouritesBtn->set_size_request(nMaxWidth, -1); + + if( SvxShowCharSet::getSelectedChar() == ' ') + { + m_xOKBtn->set_sensitive(false); + } + else + { + sal_UCS4 cChar = m_xShowSet->GetSelectCharacter(); + // using the new UCS4 constructor + OUString aOUStr( &cChar, 1 ); + m_aShowChar.SetText(aOUStr); + + setFavButtonState(aOUStr, aDefStr); + m_xOKBtn->set_sensitive(true); + } + + m_aCharmapContents.init(m_xFrame.is(), + LINK(this, SvxCharacterMap, CharClickHdl), + LINK(this, SvxCharacterMap, UpdateFavHdl), + Link()); + + setCharName(90); + + m_xSearchText->connect_focus_in(LINK( this, SvxCharacterMap, SearchFieldGetFocusHdl )); + m_xSearchText->connect_changed(LINK(this, SvxCharacterMap, SearchUpdateHdl)); +} + +void SvxCharacterMap::setFavButtonState(std::u16string_view sTitle, std::u16string_view rFont) +{ + if(sTitle.empty() || rFont.empty()) + { + m_xFavouritesBtn->set_sensitive(false); + return; + } + else + m_xFavouritesBtn->set_sensitive(true); + + if (m_aCharmapContents.isFavChar(sTitle, rFont)) + { + m_xFavouritesBtn->set_label(CuiResId(RID_CUISTR_REMOVE_FAVORITES)); + } + else + { + if (m_aCharmapContents.FavCharListIsFull()) + m_xFavouritesBtn->set_sensitive(false); + + m_xFavouritesBtn->set_label(CuiResId(RID_CUISTR_ADD_FAVORITES)); + } +} + + +void SvxCharacterMap::SetCharFont( const vcl::Font& rFont ) +{ + // first get the underlying info in order to get font names + // like "Times New Roman;Times" resolved + vcl::Font aTmp(m_xVirDev->GetFontMetric(rFont)); + + // tdf#56363 - search font family without the font feature after the colon + OUString sFontFamilyName = aTmp.GetFamilyName(); + if (const sal_Int32 nIndex = sFontFamilyName.indexOf(":"); nIndex != -1) + sFontFamilyName = sFontFamilyName.copy(0, nIndex); + if (sFontFamilyName == "StarSymbol" && m_xFontLB->find_text(sFontFamilyName) == -1) + { + //if for some reason, like font in an old document, StarSymbol is requested and it's not available, then + //try OpenSymbol instead + aTmp.SetFamilyName("OpenSymbol"); + } + + if (m_xFontLB->find_text(sFontFamilyName) == -1) + return; + + m_xFontLB->set_active_text(sFontFamilyName); + aFont = aTmp; + FontSelectHdl(*m_xFontLB); + if (m_xSubsetLB->get_count()) + m_xSubsetLB->set_active(0); +} + +void SvxCharacterMap::fillAllSubsets(weld::ComboBox& rListBox) +{ + SubsetMap aAll(nullptr); + std::vector aEntries; + for (auto & subset : aAll.GetSubsetMap()) + aEntries.emplace_back(subset.GetName()); + rListBox.insert_vector(aEntries, true); +} + +void SvxCharacterMap::insertCharToDoc(const OUString& sGlyph) +{ + if(sGlyph.isEmpty()) + return; + + if (m_xFrame.is()) { + uno::Sequence aArgs{ + comphelper::makePropertyValue("Symbols", sGlyph), + comphelper::makePropertyValue("FontName", aFont.GetFamilyName()) + }; + comphelper::dispatchCommand(".uno:InsertSymbol", m_xFrame, aArgs); + + m_aCharmapContents.updateRecentCharacterList(sGlyph, aFont.GetFamilyName()); + + } else { + sal_UCS4 cChar = sGlyph.iterateCodePoints(&o3tl::temporary(sal_Int32(0))); + const SfxItemPool* pPool = m_xOutputSet->GetPool(); + m_xOutputSet->Put( SfxStringItem( SID_CHARMAP, sGlyph ) ); + m_xOutputSet->Put( SvxFontItem( aFont.GetFamilyType(), aFont.GetFamilyName(), + aFont.GetStyleName(), aFont.GetPitch(), aFont.GetCharSet(), pPool->GetWhich(SID_ATTR_CHAR_FONT) ) ); + m_xOutputSet->Put( SfxStringItem( SID_FONT_NAME, aFont.GetFamilyName() ) ); + m_xOutputSet->Put( SfxInt32Item( SID_ATTR_CHAR, cChar ) ); + } +} + +IMPL_LINK_NOARG(SvxCharacterMap, FontSelectHdl, weld::ComboBox&, void) +{ + const sal_uInt32 nFont = m_xFontLB->get_active_id().toUInt32(); + aFont = m_xVirDev->GetFontMetricFromCollection(nFont); + aFont.SetWeight( WEIGHT_DONTKNOW ); + aFont.SetItalic( ITALIC_NONE ); + aFont.SetWidthType( WIDTH_DONTKNOW ); + aFont.SetPitch( PITCH_DONTKNOW ); + aFont.SetFamily( FAMILY_DONTKNOW ); + + // notify children using this font + m_xShowSet->SetFont( aFont ); + m_xSearchSet->SetFont( aFont ); + m_aShowChar.SetFont( aFont ); + + // setup unicode subset listbar with font specific subsets, + // hide unicode subset listbar for symbol fonts + // TODO: get info from the Font once it provides it + pSubsetMap.reset(); + m_xSubsetLB->clear(); + + bool bNeedSubset = (aFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL); + if (bNeedSubset) + { + FontCharMapRef xFontCharMap = m_xShowSet->GetFontCharMap(); + pSubsetMap.reset(new SubsetMap( xFontCharMap )); + + // update subset listbox for new font's unicode subsets + for (auto const& subset : pSubsetMap->GetSubsetMap()) + { + m_xSubsetLB->append(weld::toId(&subset), subset.GetName()); + // NOTE: subset must live at least as long as the selected font + } + + if (m_xSubsetLB->get_count() <= 1) + bNeedSubset = false; + } + + m_xSubsetText->set_sensitive(bNeedSubset); + m_xSubsetLB->set_sensitive(bNeedSubset); + + if (isSearchMode) + { + // tdf#137294 do this after modifying m_xSubsetLB sensitivity to + // restore insensitive for the search case + SearchUpdateHdl(*m_xSearchText); + SearchCharHighlightHdl(m_xSearchSet.get()); + } + + // tdf#118304 reselect current glyph to see if it's still there in new font + selectCharByCode(Radix::hexadecimal); +} + +void SvxCharacterMap::toggleSearchView(bool state) +{ + isSearchMode = state; + m_xHexCodeText->set_editable(!state); + m_xDecimalCodeText->set_editable(!state); + m_xSubsetLB->set_sensitive(!state); + + if(state) + { + m_xSearchSet->Show(); + m_xShowSet->Hide(); + } + else + { + m_xSearchSet->Hide(); + m_xShowSet->Show(); + } +} + +void SvxCharacterMap::setCharName(sal_UCS4 nDecimalValue) +{ + /* get the character name */ + UErrorCode errorCode = U_ZERO_ERROR; + // icu has a private uprv_getMaxCharNameLength function which returns the max possible + // length of this property. Unicode 3.2 max char name length was 83 + char buffer[100]; + u_charName(nDecimalValue, U_UNICODE_CHAR_NAME, buffer, sizeof(buffer), &errorCode); + if (U_SUCCESS(errorCode)) + m_xCharName->set_label(OUString::createFromAscii(buffer)); +} + +IMPL_LINK_NOARG(SvxCharacterMap, SubsetSelectHdl, weld::ComboBox&, void) +{ + const sal_Int32 nPos = m_xSubsetLB->get_active(); + const Subset* pSubset = weld::fromId(m_xSubsetLB->get_active_id()); + + if( pSubset && !isSearchMode) + { + sal_UCS4 cFirst = pSubset->GetRangeMin(); + m_xShowSet->SelectCharacter( cFirst ); + + setFavButtonState(OUString(&cFirst, 1), aFont.GetFamilyName()); + m_xSubsetLB->set_active(nPos); + } + else if( pSubset && isSearchMode) + { + m_xSearchSet->SelectCharacter( pSubset ); + + const Subset* curSubset = nullptr; + if( pSubsetMap ) + curSubset = pSubsetMap->GetSubsetByUnicode( m_xSearchSet->GetSelectCharacter() ); + if( curSubset ) + m_xSubsetLB->set_active_text(curSubset->GetName()); + else + m_xSubsetLB->set_active(-1); + + sal_UCS4 sChar = m_xSearchSet->GetSelectCharacter(); + setFavButtonState(OUString(&sChar, 1), aFont.GetFamilyName()); + } +} + +IMPL_LINK_NOARG(SvxCharacterMap, SearchFieldGetFocusHdl, weld::Widget&, void) +{ + m_xOKBtn->set_sensitive(false); +} + +IMPL_LINK_NOARG(SvxCharacterMap, SearchUpdateHdl, weld::Entry&, void) +{ + if (!m_xSearchText->get_text().isEmpty()) + { + m_xSearchSet->ClearPreviousData(); + OUString aKeyword = m_xSearchText->get_text(); + + toggleSearchView(true); + + FontCharMapRef xFontCharMap = m_xSearchSet->GetFontCharMap(); + + sal_UCS4 sChar = xFontCharMap->GetFirstChar(); + while(sChar != xFontCharMap->GetLastChar()) + { + UErrorCode errorCode = U_ZERO_ERROR; + char buffer[100]; + u_charName(sChar, U_UNICODE_CHAR_NAME, buffer, sizeof(buffer), &errorCode); + if (U_SUCCESS(errorCode)) + { + OUString sName = OUString::createFromAscii(buffer); + if(!sName.isEmpty() && sName.toAsciiLowerCase().indexOf(aKeyword.toAsciiLowerCase()) >= 0) + m_xSearchSet->AppendCharToList(sChar); + } + sChar = xFontCharMap->GetNextChar(sChar); + } + //for last char + UErrorCode errorCode = U_ZERO_ERROR; + char buffer[100]; + u_charName(sChar, U_UNICODE_CHAR_NAME, buffer, sizeof(buffer), &errorCode); + if (U_SUCCESS(errorCode)) + { + OUString sName = OUString::createFromAscii(buffer); + if(!sName.isEmpty() && sName.toAsciiLowerCase().indexOf(aKeyword.toAsciiLowerCase()) >= 0) + m_xSearchSet->AppendCharToList(sChar); + } + + m_xSearchSet->UpdateScrollRange(); + } + else + { + toggleSearchView(false); + } +} + + +IMPL_LINK(SvxCharacterMap, CharClickHdl, SvxCharView*, rView, void) +{ + rView->GrabFocus(); + + m_aShowChar.SetText( rView->GetText() ); + m_aShowChar.SetFont(rView->GetFont()); + m_aShowChar.Invalidate(); + + setFavButtonState(rView->GetText(), rView->GetFont().GetFamilyName());//check state + + // Get the hexadecimal code + OUString charValue = rView->GetText(); + sal_UCS4 cChar = charValue.iterateCodePoints(&o3tl::temporary(sal_Int32(1)), -1); + OUString aHexText = OUString::number(cChar, 16).toAsciiUpperCase(); + + // Get the decimal code + OUString aDecimalText = OUString::number(cChar); + + m_xHexCodeText->set_text(aHexText); + m_xDecimalCodeText->set_text(aDecimalText); + setCharName(cChar); + + rView->Invalidate(); + m_xOKBtn->set_sensitive(true); +} + +void SvxCharacterMap::insertSelectedCharacter(const SvxShowCharSet* pCharSet) +{ + assert(pCharSet); + sal_UCS4 cChar = pCharSet->GetSelectCharacter(); + // using the new UCS4 constructor + OUString aOUStr( &cChar, 1 ); + setFavButtonState(aOUStr, aFont.GetFamilyName()); + insertCharToDoc(aOUStr); +} + +IMPL_LINK(SvxCharacterMap, CharDoubleClickHdl, SvxShowCharSet*, pCharSet, void) +{ + insertSelectedCharacter(pCharSet); +} + +IMPL_LINK_NOARG(SvxCharacterMap, CharSelectHdl, SvxShowCharSet*, void) +{ + m_xOKBtn->set_sensitive(true); +} + +IMPL_LINK(SvxCharacterMap, ReturnKeypressOnCharHdl, SvxShowCharSet*, pCharSet, void) +{ + insertSelectedCharacter(pCharSet); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(SvxCharacterMap, InsertClickHdl, weld::Button&, void) +{ + OUString sChar = m_aShowChar.GetText(); + insertCharToDoc(sChar); + // Need to update recent character list, when OK button does not insert + if(!m_xFrame.is()) + m_aCharmapContents.updateRecentCharacterList(sChar, aFont.GetFamilyName()); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(SvxCharacterMap, FavSelectHdl, weld::Button&, void) +{ + if (m_xFavouritesBtn->get_label().match(CuiResId(RID_CUISTR_ADD_FAVORITES))) + { + m_aCharmapContents.updateFavCharacterList(m_aShowChar.GetText(), m_aShowChar.GetFont().GetFamilyName()); + setFavButtonState(m_aShowChar.GetText(), m_aShowChar.GetFont().GetFamilyName()); + } + else + { + m_aCharmapContents.deleteFavCharacterFromList(m_aShowChar.GetText(), m_aShowChar.GetFont().GetFamilyName()); + m_xFavouritesBtn->set_label(CuiResId(RID_CUISTR_ADD_FAVORITES)); + m_xFavouritesBtn->set_sensitive(false); + } + + m_aCharmapContents.updateFavCharControl(); +} + +IMPL_LINK_NOARG(SvxCharacterMap, FavClickHdl, SvxShowCharSet*, void) +{ + m_aCharmapContents.getFavCharacterList(); + m_aCharmapContents.updateFavCharControl(); +} + +IMPL_LINK_NOARG(SvxCharacterMap, CharHighlightHdl, SvxShowCharSet*, void) +{ + OUString aText; + sal_UCS4 cChar = m_xShowSet->GetSelectCharacter(); + bool bSelect = (cChar > 0); + + // show char sample + if ( bSelect ) + { + // using the new UCS4 constructor + aText = OUString( &cChar, 1 ); + // Get the hexadecimal code + OUString aHexText = OUString::number(cChar, 16).toAsciiUpperCase(); + // Get the decimal code + OUString aDecimalText = OUString::number(cChar); + setCharName(cChar); + + // Update the hex and decimal codes only if necessary + if (!m_xHexCodeText->get_text().equalsIgnoreAsciiCase(aHexText)) + m_xHexCodeText->set_text(aHexText); + if (m_xDecimalCodeText->get_text() != aDecimalText) + m_xDecimalCodeText->set_text( aDecimalText ); + + const Subset* pSubset = nullptr; + if( pSubsetMap ) + pSubset = pSubsetMap->GetSubsetByUnicode( cChar ); + if( pSubset ) + m_xSubsetLB->set_active_text(pSubset->GetName()); + else + m_xSubsetLB->set_active(-1); + } + + m_aShowChar.SetText( aText ); + m_aShowChar.SetFont( aFont ); + m_aShowChar.Invalidate(); + + setFavButtonState(aText, aFont.GetFamilyName()); +} + +IMPL_LINK_NOARG(SvxCharacterMap, SearchCharHighlightHdl, SvxShowCharSet*, void) +{ + OUString aText; + sal_UCS4 cChar = m_xSearchSet->GetSelectCharacter(); + bool bSelect = (cChar > 0); + + // show char sample + if ( bSelect ) + { + aText = OUString( &cChar, 1 ); + // Get the hexadecimal code + OUString aHexText = OUString::number(cChar, 16).toAsciiUpperCase(); + // Get the decimal code + OUString aDecimalText = OUString::number(cChar); + setCharName(cChar); + + // Update the hex and decimal codes only if necessary + if (!m_xHexCodeText->get_text().equalsIgnoreAsciiCase(aHexText)) + m_xHexCodeText->set_text(aHexText); + if (m_xDecimalCodeText->get_text() != aDecimalText) + m_xDecimalCodeText->set_text( aDecimalText ); + + const Subset* pSubset = nullptr; + if( pSubsetMap ) + pSubset = pSubsetMap->GetSubsetByUnicode( cChar ); + if( pSubset ) + m_xSubsetLB->set_active_text(pSubset->GetName()); + else + m_xSubsetLB->set_active(-1); + } + + if(m_xSearchSet->HasFocus()) + { + m_aShowChar.SetText( aText ); + m_aShowChar.SetFont( aFont ); + m_aShowChar.Invalidate(); + + setFavButtonState(aText, aFont.GetFamilyName()); + } +} + +void SvxCharacterMap::selectCharByCode(Radix radix) +{ + OUString aCodeString; + switch(radix) + { + case Radix::decimal: + aCodeString = m_xDecimalCodeText->get_text(); + break; + case Radix::hexadecimal: + aCodeString = m_xHexCodeText->get_text(); + break; + } + // Convert the code back to a character using the appropriate radix + sal_UCS4 cChar = aCodeString.toUInt32(static_cast (radix)); + // Use FontCharMap::HasChar(sal_UCS4 cChar) to see if the desired character is in the font + FontCharMapRef xFontCharMap = m_xShowSet->GetFontCharMap(); + if (xFontCharMap->HasChar(cChar)) + // Select the corresponding character + SetChar(cChar); + else { + m_xCharName->set_label(CuiResId(RID_CUISTR_MISSING_CHAR)); + m_aShowChar.SetText(" "); + switch(radix) + { + case Radix::decimal: + m_xHexCodeText->set_text(OUString::number(cChar, 16)); + break; + case Radix::hexadecimal: + m_xDecimalCodeText->set_text(OUString::number(cChar)); + break; + } + } +} + +IMPL_LINK_NOARG(SvxCharacterMap, DecimalCodeChangeHdl, weld::Entry&, void) +{ + selectCharByCode(Radix::decimal); +} + +IMPL_LINK_NOARG(SvxCharacterMap, HexCodeChangeHdl, weld::Entry&, void) +{ + selectCharByCode(Radix::hexadecimal); +} + +IMPL_LINK(SvxCharacterMap, CharPreSelectHdl, SvxShowCharSet*, pCharSet, void) +{ + assert(pCharSet); + // adjust subset selection + if( pSubsetMap ) + { + sal_UCS4 cChar = pCharSet->GetSelectCharacter(); + + setFavButtonState(OUString(&cChar, 1), aFont.GetFamilyName()); + const Subset* pSubset = pSubsetMap->GetSubsetByUnicode( cChar ); + if( pSubset ) + m_xSubsetLB->set_active_text(pSubset->GetName()); + } + + m_xOKBtn->set_sensitive(true); +} + +// class SvxShowText ===================================================== +SvxShowText::SvxShowText(const VclPtr& rVirDev) + : m_xVirDev(rVirDev) + , mnY(0) + , mbCenter(false) +{ +} + +void SvxShowText::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + vcl::Font aFont = m_xVirDev->GetFont(); + Size aFontSize(aFont.GetFontSize().Width() * 5, aFont.GetFontSize().Height() * 5); + aFont.SetFontSize(aFontSize); + m_xVirDev->Push(PUSH_ALLFONT); + m_xVirDev->SetFont(aFont); + pDrawingArea->set_size_request(m_xVirDev->approximate_digit_width() + 2 * 12, + m_xVirDev->LogicToPixel(aFontSize).Height() * 2); + m_xVirDev->Pop(); +} + +void SvxShowText::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.SetFont(m_aFont); + + Color aTextCol = rRenderContext.GetTextColor(); + Color aFillCol = rRenderContext.GetFillColor(); + Color aLineCol = rRenderContext.GetLineColor(); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + const Color aWindowTextColor(rStyleSettings.GetDialogTextColor()); + const Color aWindowColor(rStyleSettings.GetWindowColor()); + const Color aShadowColor(rStyleSettings.GetShadowColor()); + rRenderContext.SetTextColor(aWindowTextColor); + rRenderContext.SetFillColor(aWindowColor); + + const OUString aText = GetText(); + + Size aSize(GetOutputSizePixel()); + tools::Long nAvailWidth = aSize.Width(); + tools::Long nWinHeight = aSize.Height(); + + bool bGotBoundary = true; + bool bShrankFont = false; + vcl::Font aOrigFont(rRenderContext.GetFont()); + Size aFontSize(aOrigFont.GetFontSize()); + ::tools::Rectangle aBoundRect; + + for (tools::Long nFontHeight = aFontSize.Height(); nFontHeight > 0; nFontHeight -= 5) + { + if (!rRenderContext.GetTextBoundRect( aBoundRect, aText ) || aBoundRect.IsEmpty()) + { + bGotBoundary = false; + break; + } + if (!mbCenter) + break; + //only shrink in the single glyph large view mode + tools::Long nTextWidth = aBoundRect.GetWidth(); + if (nAvailWidth > nTextWidth) + break; + vcl::Font aFont(aOrigFont); + aFontSize.setHeight( nFontHeight ); + aFont.SetFontSize(aFontSize); + rRenderContext.SetFont(aFont); + mnY = (nWinHeight - rRenderContext.GetTextHeight()) / 2; + bShrankFont = true; + } + + Point aPoint(2, mnY); + // adjust position using ink boundary if possible + if (!bGotBoundary) + aPoint.setX( (aSize.Width() - rRenderContext.GetTextWidth(aText)) / 2 ); + else + { + // adjust position before it gets out of bounds + aBoundRect += aPoint; + + // shift back vertically if needed + int nYLDelta = aBoundRect.Top(); + int nYHDelta = aSize.Height() - aBoundRect.Bottom(); + if( nYLDelta <= 0 ) + aPoint.AdjustY( -(nYLDelta - 1) ); + else if( nYHDelta <= 0 ) + aPoint.AdjustY(nYHDelta - 1 ); + + if (mbCenter) + { + // move glyph to middle of cell + aPoint.setX( -aBoundRect.Left() + (aSize.Width() - aBoundRect.GetWidth()) / 2 ); + } + else + { + // shift back horizontally if needed + int nXLDelta = aBoundRect.Left(); + int nXHDelta = aSize.Width() - aBoundRect.Right(); + if( nXLDelta <= 0 ) + aPoint.AdjustX( -(nXLDelta - 1) ); + else if( nXHDelta <= 0 ) + aPoint.AdjustX(nXHDelta - 1 ); + } + } + + rRenderContext.SetLineColor(aShadowColor); + rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), aSize)); + rRenderContext.DrawText(aPoint, aText); + rRenderContext.SetTextColor(aTextCol); + rRenderContext.SetFillColor(aFillCol); + rRenderContext.SetLineColor(aLineCol); + if (bShrankFont) + rRenderContext.SetFont(aOrigFont); +} + +void SvxShowText::SetFont( const vcl::Font& rFont ) +{ + tools::Long nWinHeight = GetOutputSizePixel().Height(); + + m_aFont = rFont; + m_aFont.SetWeight(WEIGHT_NORMAL); + m_aFont.SetAlignment(ALIGN_TOP); + m_aFont.SetFontSize(m_xVirDev->PixelToLogic(Size(0, nWinHeight / 2))); + m_aFont.SetTransparent(true); + + m_xVirDev->Push(PUSH_ALLFONT); + m_xVirDev->SetFont(m_aFont); + mnY = (nWinHeight - m_xVirDev->GetTextHeight()) / 2; + m_xVirDev->Pop(); + + Invalidate(); +} + +void SvxShowText::Resize() +{ + SetFont(GetFont()); //force recalculation of size +} + +void SvxShowText::SetText(const OUString& rText) +{ + m_sText = rText; + Invalidate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/cuifmsearch.cxx b/cui/source/dialogs/cuifmsearch.cxx new file mode 100644 index 0000000000..bb3bfaf5ca --- /dev/null +++ b/cui/source/dialogs/cuifmsearch.cxx @@ -0,0 +1,764 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace css::uno; +using namespace css::i18n; +using namespace ::svxform; +using namespace css::sdbc; +using namespace css::util; + +#define MAX_HISTORY_ENTRIES 50 + +void FmSearchDialog::initCommon( const Reference< XResultSet >& _rxCursor ) +{ + // init the engine + DBG_ASSERT( m_pSearchEngine, "FmSearchDialog::initCommon: have no engine!" ); + m_pSearchEngine->SetProgressHandler(LINK(this, FmSearchDialog, OnSearchProgress)); + + // some layout changes according to available CJK options + if (!SvtCJKOptions::IsJapaneseFindEnabled()) + { + // hide the options for the japanese search + m_pSoundsLikeCJK->hide(); + m_pSoundsLikeCJKSettings->hide(); + } + + if (!SvtCJKOptions::IsCJKFontEnabled()) + { + m_pHalfFullFormsCJK->hide(); + + // never ignore the width (ignoring is expensive) if the option is not available at all + m_pSearchEngine->SetIgnoreWidthCJK( false ); + } + + // some initial record texts + m_pftRecord->set_label( OUString::number(_rxCursor->getRow()) ); + m_pbClose->set_tooltip_text(OUString()); +} + +FmSearchDialog::FmSearchDialog(weld::Window* pParent, const OUString& sInitialText, const std::vector< OUString >& _rContexts, sal_Int16 nInitialContext, + const Link& lnkContextSupplier) + : GenericDialogController(pParent, "cui/ui/fmsearchdialog.ui", "RecordSearchDialog") + , m_sCancel( GetStandardText( StandardButtonType::Cancel ) ) + , m_lnkContextSupplier(lnkContextSupplier) + , m_prbSearchForText(m_xBuilder->weld_radio_button("rbSearchForText")) + , m_prbSearchForNull(m_xBuilder->weld_radio_button("rbSearchForNull")) + , m_prbSearchForNotNull(m_xBuilder->weld_radio_button("rbSearchForNotNull")) + , m_pcmbSearchText(m_xBuilder->weld_combo_box("cmbSearchText")) + , m_pftForm(m_xBuilder->weld_label("ftForm")) + , m_plbForm(m_xBuilder->weld_combo_box("lbForm")) + , m_prbAllFields(m_xBuilder->weld_radio_button("rbAllFields")) + , m_prbSingleField(m_xBuilder->weld_radio_button("rbSingleField")) + , m_plbField(m_xBuilder->weld_combo_box("lbField")) + , m_pftPosition(m_xBuilder->weld_label("ftPosition")) + , m_plbPosition(m_xBuilder->weld_combo_box("lbPosition")) + , m_pcbUseFormat(m_xBuilder->weld_check_button("cbUseFormat")) + , m_pcbCase(m_xBuilder->weld_check_button("cbCase")) + , m_pcbBackwards(m_xBuilder->weld_check_button("cbBackwards")) + , m_pcbStartOver(m_xBuilder->weld_check_button("cbStartOver")) + , m_pcbWildCard(m_xBuilder->weld_check_button("cbWildCard")) + , m_pcbRegular(m_xBuilder->weld_check_button("cbRegular")) + , m_pcbApprox(m_xBuilder->weld_check_button("cbApprox")) + , m_ppbApproxSettings(m_xBuilder->weld_button("pbApproxSettings")) + , m_pHalfFullFormsCJK(m_xBuilder->weld_check_button("HalfFullFormsCJK")) + , m_pSoundsLikeCJK(m_xBuilder->weld_check_button("SoundsLikeCJK")) + , m_pSoundsLikeCJKSettings(m_xBuilder->weld_button("SoundsLikeCJKSettings")) + , m_pftRecord(m_xBuilder->weld_label("ftRecord")) + , m_pftHint(m_xBuilder->weld_label("ftHint")) + , m_pbSearchAgain(m_xBuilder->weld_button("pbSearchAgain")) + , m_pbClose(m_xBuilder->weld_button("close")) +{ + m_pcmbSearchText->set_size_request(m_pcmbSearchText->get_approximate_digit_width() * 38, -1); + m_plbForm->set_size_request(m_plbForm->get_approximate_digit_width() * 38, -1); + m_sSearch = m_pbSearchAgain->get_label(); + + DBG_ASSERT(m_lnkContextSupplier.IsSet(), "FmSearchDialog::FmSearchDialog : have no ContextSupplier !"); + + FmSearchContext fmscInitial; + fmscInitial.nContext = nInitialContext; + m_lnkContextSupplier.Call(fmscInitial); + DBG_ASSERT(fmscInitial.xCursor.is(), "FmSearchDialog::FmSearchDialog : invalid data supplied by ContextSupplier !"); + DBG_ASSERT(comphelper::string::getTokenCount(fmscInitial.strUsedFields, ';') == static_cast(fmscInitial.arrFields.size()), + "FmSearchDialog::FmSearchDialog : invalid data supplied by ContextSupplied !"); +#if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL + for (const Reference & arrField : fmscInitial.arrFields) + { + DBG_ASSERT(arrField.is(), "FmSearchDialog::FmSearchDialog : invalid data supplied by ContextSupplier !"); + } +#endif // (OSL_DEBUG_LEVEL > 1) || DBG_UTIL + + for ( std::vector< OUString >::const_iterator context = _rContexts.begin(); + context != _rContexts.end(); + ++context + ) + { + m_arrContextFields.emplace_back(); + m_plbForm->append_text(*context); + } + m_plbForm->set_active(nInitialContext); + + m_plbForm->connect_changed(LINK(this, FmSearchDialog, OnContextSelection)); + + if (m_arrContextFields.size() == 1) + { + // hide dispensable controls + m_pftForm->hide(); + m_plbForm->hide(); + } + + m_pSearchEngine.reset( new FmSearchEngine( + ::comphelper::getProcessComponentContext(), fmscInitial.xCursor, fmscInitial.strUsedFields, fmscInitial.arrFields ) ); + initCommon( fmscInitial.xCursor ); + + if ( !fmscInitial.sFieldDisplayNames.isEmpty() ) + { // use the display names if supplied + DBG_ASSERT(comphelper::string::getTokenCount(fmscInitial.sFieldDisplayNames, ';') == comphelper::string::getTokenCount(fmscInitial.strUsedFields, ';'), + "FmSearchDialog::FmSearchDialog : invalid initial context description !"); + Init(fmscInitial.sFieldDisplayNames, sInitialText); + } + else + Init(fmscInitial.strUsedFields, sInitialText); +} + +FmSearchDialog::~FmSearchDialog() +{ + SaveParams(); + + m_pConfig.reset(); + m_pSearchEngine.reset(); +} + +void FmSearchDialog::Init(std::u16string_view strVisibleFields, const OUString& sInitialText) +{ + //the initialization of all the Controls + m_prbSearchForText->connect_toggled(LINK(this, FmSearchDialog, OnToggledSearchRadio)); + m_prbSearchForNull->connect_toggled(LINK(this, FmSearchDialog, OnToggledSearchRadio)); + m_prbSearchForNotNull->connect_toggled(LINK(this, FmSearchDialog, OnToggledSearchRadio)); + + m_prbAllFields->connect_toggled(LINK(this, FmSearchDialog, OnToggledFieldRadios)); + m_prbSingleField->connect_toggled(LINK(this, FmSearchDialog, OnToggledFieldRadios)); + + m_pbSearchAgain->connect_clicked(LINK(this, FmSearchDialog, OnClickedSearchAgain)); + m_ppbApproxSettings->connect_clicked(LINK(this, FmSearchDialog, OnClickedSpecialSettings)); + m_pSoundsLikeCJKSettings->connect_clicked(LINK(this, FmSearchDialog, OnClickedSpecialSettings)); + + m_plbPosition->connect_changed(LINK(this, FmSearchDialog, OnPositionSelected)); + m_plbField->connect_changed(LINK(this, FmSearchDialog, OnFieldSelected)); + + m_pcmbSearchText->connect_changed(LINK(this, FmSearchDialog, OnSearchTextModified)); + m_pcmbSearchText->set_entry_completion(false); + m_pcmbSearchText->connect_focus_in(LINK(this, FmSearchDialog, OnFocusGrabbed)); + + m_pcbUseFormat->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled)); + m_pcbBackwards->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled)); + m_pcbStartOver->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled)); + m_pcbCase->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled)); + m_pcbWildCard->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled)); + m_pcbRegular->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled)); + m_pcbApprox->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled)); + m_pHalfFullFormsCJK->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled)); + m_pSoundsLikeCJK->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled)); + + // fill the listboxes + // method of field comparison + const TranslateId aResIds[] = { + RID_STR_SEARCH_ANYWHERE, + RID_STR_SEARCH_BEGINNING, + RID_STR_SEARCH_END, + RID_STR_SEARCH_WHOLE + }; + for (auto const & pResId : aResIds) + m_plbPosition->append_text(CuiResId(pResId)); + m_plbPosition->set_active(MATCHING_ANYWHERE); + + // the field listbox + if (!strVisibleFields.empty()) + { + sal_Int32 nPos {0}; + do { + m_plbField->append_text(OUString(o3tl::getToken(strVisibleFields, 0, ';', nPos))); + } while (nPos>=0); + } + + + m_pConfig.reset( new FmSearchConfigItem ); + LoadParams(); + + m_pcmbSearchText->set_entry_text(sInitialText); + // if the Edit-line has changed the text (e.g. because it contains + // control characters, as can be the case with memo fields), I use + // an empty OUString. + OUString sRealSetText = m_pcmbSearchText->get_active_text(); + if (sRealSetText != sInitialText) + m_pcmbSearchText->set_entry_text(OUString()); + OnSearchTextModified(*m_pcmbSearchText); + + // initial + EnableSearchUI(true); + + if ( m_prbSearchForText->get_active() ) + m_pcmbSearchText->grab_focus(); + +} + +short FmSearchDialog::run() +{ + short nRet = weld::GenericDialogController::run(); + m_pSearchEngine->CancelSearch(); + return nRet; +} + +IMPL_LINK(FmSearchDialog, OnToggledSearchRadio, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + EnableSearchForDependees(true); +} + +IMPL_LINK(FmSearchDialog, OnToggledFieldRadios, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + + // en- or disable field list box accordingly + if (m_prbSingleField->get_active()) + { + m_plbField->set_sensitive(true); + m_pSearchEngine->RebuildUsedFields(m_plbField->get_active()); + } + else + { + m_plbField->set_sensitive(false); + m_pSearchEngine->RebuildUsedFields(-1); + } +} + +IMPL_LINK_NOARG(FmSearchDialog, OnClickedSearchAgain, weld::Button&, void) +{ + if (m_pbClose->get_sensitive()) + { // the button has the function 'search' + OUString strThisRoundText = m_pcmbSearchText->get_active_text(); + // to history + m_pcmbSearchText->remove_text(strThisRoundText); + m_pcmbSearchText->insert_text(0, strThisRoundText); + // the remove/insert makes sure that a) the OUString does not appear twice and + // that b) the last searched strings are at the beginning and limit the list length + while (m_pcmbSearchText->get_count() > MAX_HISTORY_ENTRIES) + m_pcmbSearchText->remove(m_pcmbSearchText->get_count()-1); + + // take out the 'overflow' hint + m_pftHint->set_label(OUString()); + + if (m_pcbStartOver->get_active()) + { + m_pcbStartOver->set_active(false); + EnableSearchUI(false); + if (m_prbSearchForText->get_active()) + m_pSearchEngine->StartOver(strThisRoundText); + else + m_pSearchEngine->StartOverSpecial(m_prbSearchForNull->get_active()); + } + else + { + EnableSearchUI(false); + if (m_prbSearchForText->get_active()) + m_pSearchEngine->SearchNext(strThisRoundText); + else + m_pSearchEngine->SearchNextSpecial(m_prbSearchForNull->get_active()); + } + } + else + { // the button has the function 'cancel' + // the CancelButton is usually only disabled, when working in a thread or with reschedule + m_pSearchEngine->CancelSearch(); + // the ProgressHandler is called when it's really finished, here it's only a demand + } +} + +IMPL_LINK(FmSearchDialog, OnClickedSpecialSettings, weld::Button&, rButton, void) +{ + if (m_ppbApproxSettings.get() == &rButton) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + + VclPtr pDlg(pFact->CreateSvxSearchSimilarityDialog(m_xDialog.get(), m_pSearchEngine->GetLevRelaxed(), m_pSearchEngine->GetLevOther(), + m_pSearchEngine->GetLevShorter(), m_pSearchEngine->GetLevLonger() )); + pDlg->StartExecuteAsync([pDlg, this](sal_Int32 nResult){ + + if (nResult == RET_OK) + { + m_pSearchEngine->SetLevRelaxed( pDlg->IsRelaxed() ); + m_pSearchEngine->SetLevOther( pDlg->GetOther() ); + m_pSearchEngine->SetLevShorter(pDlg->GetShorter() ); + m_pSearchEngine->SetLevLonger( pDlg->GetLonger() ); + } + pDlg->disposeOnce(); + }); + } + else if (m_pSoundsLikeCJKSettings.get() == &rButton) + { + SfxItemSet aSet( SfxGetpApp()->GetPool() ); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr aDlg(pFact->CreateSvxJSearchOptionsDialog(m_xDialog.get(), aSet, m_pSearchEngine->GetTransliterationFlags() )); + aDlg->Execute(); + + TransliterationFlags nFlags = aDlg->GetTransliterationFlags(); + m_pSearchEngine->SetTransliterationFlags(nFlags); + + m_pcbCase->set_active(m_pSearchEngine->GetCaseSensitive()); + OnCheckBoxToggled( *m_pcbCase ); + m_pHalfFullFormsCJK->set_active( !m_pSearchEngine->GetIgnoreWidthCJK() ); + OnCheckBoxToggled( *m_pHalfFullFormsCJK ); + } +} + +IMPL_LINK_NOARG(FmSearchDialog, OnSearchTextModified, weld::ComboBox&, void) +{ + if ((!m_pcmbSearchText->get_active_text().isEmpty()) || !m_prbSearchForText->get_active()) + m_pbSearchAgain->set_sensitive(true); + else + m_pbSearchAgain->set_sensitive(false); + + m_pSearchEngine->InvalidatePreviousLoc(); +} + +IMPL_LINK_NOARG(FmSearchDialog, OnFocusGrabbed, weld::Widget&, void) +{ + m_pcmbSearchText->select_entry_region(0, -1); +} + +IMPL_LINK_NOARG(FmSearchDialog, OnPositionSelected, weld::ComboBox&, void) +{ + m_pSearchEngine->SetPosition(m_plbPosition->get_active()); +} + +IMPL_LINK_NOARG(FmSearchDialog, OnFieldSelected, weld::ComboBox&, void) +{ + m_pSearchEngine->RebuildUsedFields(m_prbAllFields->get_active() ? -1 : m_plbField->get_active()); + // calls m_pSearchEngine->InvalidatePreviousLoc too + + int nCurrentContext = m_plbForm->get_active(); + if (nCurrentContext != -1) + m_arrContextFields[nCurrentContext] = m_plbField->get_active_text(); +} + +IMPL_LINK(FmSearchDialog, OnCheckBoxToggled, weld::Toggleable&, rBox, void) +{ + bool bChecked = rBox.get_active(); + + // formatter or case -> pass on to the engine + if (&rBox == m_pcbUseFormat.get()) + m_pSearchEngine->SetFormatterUsing(bChecked); + else if (&rBox == m_pcbCase.get()) + m_pSearchEngine->SetCaseSensitive(bChecked); + // direction -> pass on and reset the checkbox-text for StartOver + else if (&rBox == m_pcbBackwards.get()) + { + m_pcbStartOver->set_label( CuiResId( bChecked ? RID_STR_FROM_BOTTOM : RID_STR_FROM_TOP ) ); + m_pSearchEngine->SetDirection(!bChecked); + } + // similarity-search or regular expression + else if ((&rBox == m_pcbApprox.get()) || (&rBox == m_pcbRegular.get()) || (&rBox == m_pcbWildCard.get())) + { + weld::CheckButton* pBoxes[] = { m_pcbWildCard.get(), m_pcbRegular.get(), m_pcbApprox.get() }; + for (weld::CheckButton* pBoxe : pBoxes) + { + if (pBoxe != &rBox) + { + if (bChecked) + pBoxe->set_sensitive(false); + else + pBoxe->set_sensitive(true); + } + } + + // pass on to the engine + m_pSearchEngine->SetWildcard(m_pcbWildCard->get_sensitive() && m_pcbWildCard->get_active()); + m_pSearchEngine->SetRegular(m_pcbRegular->get_sensitive() && m_pcbRegular->get_active()); + m_pSearchEngine->SetLevenshtein(m_pcbApprox->get_sensitive() && m_pcbApprox->get_active()); + // (disabled boxes have to be passed to the engine as sal_False) + + // adjust the Position-Listbox (which is not allowed during Wildcard-search) + if (&rBox == m_pcbWildCard.get()) + { + if (bChecked) + { + m_pftPosition->set_sensitive(false); + m_plbPosition->set_sensitive(false); + } + else + { + m_pftPosition->set_sensitive(true); + m_plbPosition->set_sensitive(true); + } + } + + // and the button for similarity-search + if (&rBox == m_pcbApprox.get()) + { + if (bChecked) + m_ppbApproxSettings->set_sensitive(true); + else + m_ppbApproxSettings->set_sensitive(false); + } + } + else if (&rBox == m_pHalfFullFormsCJK.get()) + { + // forward to the search engine + m_pSearchEngine->SetIgnoreWidthCJK( !bChecked ); + } + else if (&rBox == m_pSoundsLikeCJK.get()) + { + m_pSoundsLikeCJKSettings->set_sensitive(bChecked); + + // two other buttons which depend on this one + bool bEnable = ( m_prbSearchForText->get_active() + && !m_pSoundsLikeCJK->get_active() + ) + || !SvtCJKOptions::IsJapaneseFindEnabled(); + m_pcbCase->set_sensitive(bEnable); + m_pHalfFullFormsCJK->set_sensitive(bEnable); + + // forward to the search engine + m_pSearchEngine->SetTransliteration( bChecked ); + } +} + +void FmSearchDialog::InitContext(sal_Int16 nContext) +{ + FmSearchContext fmscContext; + fmscContext.nContext = nContext; + + sal_uInt32 nResult = m_lnkContextSupplier.Call(fmscContext); + DBG_ASSERT(nResult > 0, "FmSearchDialog::InitContext : ContextSupplier didn't give me any controls !"); + + // put the field names into the respective listbox + m_plbField->clear(); + + if (!fmscContext.sFieldDisplayNames.isEmpty()) + { + // use the display names if supplied + DBG_ASSERT(comphelper::string::getTokenCount(fmscContext.sFieldDisplayNames, ';') == comphelper::string::getTokenCount(fmscContext.strUsedFields, ';'), + "FmSearchDialog::InitContext : invalid context description supplied !"); + sal_Int32 nPos {0}; + do { + m_plbField->append_text(fmscContext.sFieldDisplayNames.getToken(0, ';', nPos)); + } while (nPos>=0); + } + else if (!fmscContext.strUsedFields.isEmpty()) + { + // else use the field names + sal_Int32 nPos {0}; + do { + m_plbField->append_text(fmscContext.strUsedFields.getToken(0, ';', nPos)); + } while (nPos>=0); + } + + if (nContext < static_cast(m_arrContextFields.size()) && !m_arrContextFields[nContext].isEmpty()) + { + m_plbField->set_active_text(m_arrContextFields[nContext]); + } + else + { + m_plbField->set_active(0); + if (m_prbSingleField->get_active() && (m_plbField->get_count() > 1)) + m_plbField->grab_focus(); + } + + m_pSearchEngine->SwitchToContext(fmscContext.xCursor, fmscContext.strUsedFields, fmscContext.arrFields, + m_prbAllFields->get_active() ? -1 : 0); + + m_pftRecord->set_label(OUString::number(fmscContext.xCursor->getRow())); +} + +IMPL_LINK(FmSearchDialog, OnContextSelection, weld::ComboBox&, rBox, void) +{ + InitContext(rBox.get_active()); +} + +void FmSearchDialog::EnableSearchUI(bool bEnable) +{ + // the search button has two functions -> adjust its text accordingly + OUString sButtonText( bEnable ? m_sSearch : m_sCancel ); + m_pbSearchAgain->set_label(sButtonText); + + m_prbSearchForText->set_sensitive(bEnable); + m_prbSearchForNull->set_sensitive(bEnable); + m_prbSearchForNotNull->set_sensitive(bEnable); + m_plbForm->set_sensitive(bEnable); + m_prbAllFields->set_sensitive(bEnable); + m_prbSingleField->set_sensitive(bEnable); + m_plbField->set_sensitive(bEnable && m_prbSingleField->get_active()); + m_pcbBackwards->set_sensitive(bEnable); + m_pcbStartOver->set_sensitive(bEnable); + m_pbClose->set_sensitive(bEnable); + EnableSearchForDependees(bEnable); + + if ( !bEnable ) + { // this means we're preparing for starting a search + // In this case, EnableSearchForDependees disabled the search button + // But as we're about to use it for cancelling the search, we really need to enable it, again + m_pbSearchAgain->set_sensitive(true); + } +} + +void FmSearchDialog::EnableSearchForDependees(bool bEnable) +{ + bool bSearchingForText = m_prbSearchForText->get_active(); + m_pbSearchAgain->set_sensitive(bEnable && (!bSearchingForText || (!m_pcmbSearchText->get_active_text().isEmpty()))); + + bEnable = bEnable && bSearchingForText; + + bool bEnableRedundants = !m_pSoundsLikeCJK->get_active() || !SvtCJKOptions::IsJapaneseFindEnabled(); + + m_pcmbSearchText->set_sensitive(bEnable); + m_pftPosition->set_sensitive(bEnable && !m_pcbWildCard->get_active()); + m_pcbWildCard->set_sensitive(bEnable && !m_pcbRegular->get_active() && !m_pcbApprox->get_active()); + m_pcbRegular->set_sensitive(bEnable && !m_pcbWildCard->get_active() && !m_pcbApprox->get_active()); + m_pcbApprox->set_sensitive(bEnable && !m_pcbWildCard->get_active() && !m_pcbRegular->get_active()); + m_ppbApproxSettings->set_sensitive(bEnable && m_pcbApprox->get_active()); + m_pHalfFullFormsCJK->set_sensitive(bEnable && bEnableRedundants); + m_pSoundsLikeCJK->set_sensitive(bEnable); + m_pSoundsLikeCJKSettings->set_sensitive(bEnable && m_pSoundsLikeCJK->get_active()); + m_plbPosition->set_sensitive(bEnable && !m_pcbWildCard->get_active()); + m_pcbUseFormat->set_sensitive(bEnable); + m_pcbCase->set_sensitive(bEnable && bEnableRedundants); +} + +void FmSearchDialog::OnFound(const css::uno::Any& aCursorPos, sal_Int16 nFieldPos) +{ + FmFoundRecordInformation friInfo; + friInfo.nContext = m_plbForm->get_active(); + // if I don't do a search in a context, this has an invalid value - but then it doesn't matter anyway + friInfo.aPosition = aCursorPos; + if (m_prbAllFields->get_active()) + friInfo.nFieldPos = nFieldPos; + else + friInfo.nFieldPos = m_plbField->get_active(); + // this of course implies that I have really searched in the field that is selected in the listbox, + // which is made sure in RebuildUsedFields + + m_lnkFoundHandler.Call(friInfo); + + m_pcmbSearchText->grab_focus(); +} + +IMPL_LINK(FmSearchDialog, OnSearchProgress, const FmSearchProgress*, pProgress, void) +{ + SolarMutexGuard aGuard; + // make this single method thread-safe (it's an overkill to block the whole application for this, + // but we don't have another safety concept at the moment) + + switch (pProgress->aSearchState) + { + case FmSearchProgress::State::Progress: + if (pProgress->bOverflow) + { + OUString sHint( CuiResId( m_pcbBackwards->get_active() ? RID_STR_OVERFLOW_BACKWARD : RID_STR_OVERFLOW_FORWARD ) ); + m_pftHint->set_label( sHint ); + } + + m_pftRecord->set_label(OUString::number(1 + pProgress->nCurrentRecord)); + break; + + case FmSearchProgress::State::ProgressCounting: + m_pftHint->set_label(CuiResId(RID_STR_SEARCH_COUNTING)); + m_pftRecord->set_label(OUString::number(pProgress->nCurrentRecord)); + break; + + case FmSearchProgress::State::Successful: + OnFound(pProgress->aBookmark, static_cast(pProgress->nFieldIndex)); + EnableSearchUI(true); + break; + + case FmSearchProgress::State::Error: + case FmSearchProgress::State::NothingFound: + { + TranslateId pErrorId = (FmSearchProgress::State::Error == pProgress->aSearchState) + ? RID_STR_SEARCH_GENERAL_ERROR + : RID_STR_SEARCH_NORECORD; + std::unique_ptr xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, CuiResId(pErrorId))); + xBox->run(); + [[fallthrough]]; + } + case FmSearchProgress::State::Canceled: + EnableSearchUI(true); + if (m_lnkCanceledNotFoundHdl.IsSet()) + { + FmFoundRecordInformation friInfo; + friInfo.nContext = m_plbForm->get_active(); + // if I don't do a search in a context, this has an invalid value - but then it doesn't matter anyway + friInfo.aPosition = pProgress->aBookmark; + m_lnkCanceledNotFoundHdl.Call(friInfo); + } + break; + } + + m_pftRecord->set_label(OUString::number(1 + pProgress->nCurrentRecord)); +} + +void FmSearchDialog::LoadParams() +{ + FmSearchParams aParams(m_pConfig->getParams()); + + const OUString* pHistory = aParams.aHistory.getConstArray(); + const OUString* pHistoryEnd = pHistory + aParams.aHistory.getLength(); + for (; pHistory != pHistoryEnd; ++pHistory) + m_pcmbSearchText->append_text( *pHistory ); + + // I do the settings at my UI-elements and then I simply call the respective change-handler, + // that way the data is handed on to the SearchEngine and all dependent settings are done + + // current field + int nInitialField = m_plbField->find_text( aParams.sSingleSearchField ); + if (nInitialField == -1) + nInitialField = 0; + m_plbField->set_active(nInitialField); + OnFieldSelected(*m_plbField); + // all fields/single field (AFTER selecting the field because OnToggledFieldRadios expects a valid value there) + if (aParams.bAllFields) + { + m_prbSingleField->set_active(false); + m_prbAllFields->set_active(true); + OnToggledFieldRadios(*m_prbAllFields); + // OnToggledFieldRadios also calls to RebuildUsedFields + } + else + { + m_prbAllFields->set_active(false); + m_prbSingleField->set_active(true); + OnToggledFieldRadios(*m_prbSingleField); + } + + m_plbPosition->set_active(aParams.nPosition); + OnPositionSelected(*m_plbPosition); + + // field formatting/case sensitivity/direction + m_pcbUseFormat->set_active(aParams.bUseFormatter); + m_pcbCase->set_active( aParams.isCaseSensitive() ); + m_pcbBackwards->set_active(aParams.bBackwards); + OnCheckBoxToggled(*m_pcbUseFormat); + OnCheckBoxToggled(*m_pcbCase); + OnCheckBoxToggled(*m_pcbBackwards); + + m_pHalfFullFormsCJK->set_active( !aParams.isIgnoreWidthCJK( ) ); // BEWARE: this checkbox has an inverse semantics! + m_pSoundsLikeCJK->set_active( aParams.bSoundsLikeCJK ); + OnCheckBoxToggled(*m_pHalfFullFormsCJK); + OnCheckBoxToggled(*m_pSoundsLikeCJK); + + m_pcbWildCard->set_active(false); + m_pcbRegular->set_active(false); + m_pcbApprox->set_active(false); + OnCheckBoxToggled(*m_pcbWildCard); + OnCheckBoxToggled(*m_pcbRegular); + OnCheckBoxToggled(*m_pcbApprox); + + weld::CheckButton* pToCheck = nullptr; + if (aParams.bWildcard) + pToCheck = m_pcbWildCard.get(); + if (aParams.bRegular) + pToCheck = m_pcbRegular.get(); + if (aParams.bApproxSearch) + pToCheck = m_pcbApprox.get(); + if (aParams.bSoundsLikeCJK) + pToCheck = m_pSoundsLikeCJK.get(); + if (pToCheck) + { + pToCheck->set_active(true); + OnCheckBoxToggled(*pToCheck); + } + + // set Levenshtein-parameters directly at the SearchEngine + m_pSearchEngine->SetLevRelaxed(aParams.bLevRelaxed); + m_pSearchEngine->SetLevOther(aParams.nLevOther); + m_pSearchEngine->SetLevShorter(aParams.nLevShorter); + m_pSearchEngine->SetLevLonger(aParams.nLevLonger); + + m_pSearchEngine->SetTransliterationFlags( aParams.getTransliterationFlags( ) ); + + m_prbSearchForText->set_active(false); + m_prbSearchForNull->set_active(false); + m_prbSearchForNotNull->set_active(false); + switch (aParams.nSearchForType) + { + case 1: m_prbSearchForNull->set_active(true); break; + case 2: m_prbSearchForNotNull->set_active(true); break; + default: m_prbSearchForText->set_active(true); break; + } + OnToggledFieldRadios(*m_prbSearchForText); +} + +void FmSearchDialog::SaveParams() const +{ + if (!m_pConfig) + return; + + FmSearchParams aCurrentSettings; + + int nCount = m_pcmbSearchText->get_count(); + aCurrentSettings.aHistory.realloc(nCount); + OUString* pHistory = aCurrentSettings.aHistory.getArray(); + for (int i = 0; i < nCount; ++i, ++pHistory) + *pHistory = m_pcmbSearchText->get_text(i); + + aCurrentSettings.sSingleSearchField = m_plbField->get_active_text(); + aCurrentSettings.bAllFields = m_prbAllFields->get_active(); + aCurrentSettings.nPosition = m_pSearchEngine->GetPosition(); + aCurrentSettings.bUseFormatter = m_pSearchEngine->GetFormatterUsing(); + aCurrentSettings.setCaseSensitive ( m_pSearchEngine->GetCaseSensitive() ); + aCurrentSettings.bBackwards = !m_pSearchEngine->GetDirection(); + aCurrentSettings.bWildcard = m_pSearchEngine->GetWildcard(); + aCurrentSettings.bRegular = m_pSearchEngine->GetRegular(); + aCurrentSettings.bApproxSearch = m_pSearchEngine->GetLevenshtein(); + aCurrentSettings.bLevRelaxed = m_pSearchEngine->GetLevRelaxed(); + aCurrentSettings.nLevOther = m_pSearchEngine->GetLevOther(); + aCurrentSettings.nLevShorter = m_pSearchEngine->GetLevShorter(); + aCurrentSettings.nLevLonger = m_pSearchEngine->GetLevLonger(); + + aCurrentSettings.bSoundsLikeCJK = m_pSearchEngine->GetTransliteration(); + aCurrentSettings.setTransliterationFlags ( m_pSearchEngine->GetTransliterationFlags() ); + + if (m_prbSearchForNull->get_active()) + aCurrentSettings.nSearchForType = 1; + else if (m_prbSearchForNotNull->get_active()) + aCurrentSettings.nSearchForType = 2; + else + aCurrentSettings.nSearchForType = 0; + + m_pConfig->setParams( aCurrentSettings ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/cuigaldlg.cxx b/cui/source/dialogs/cuigaldlg.cxx new file mode 100644 index 0000000000..9a1a2e26a4 --- /dev/null +++ b/cui/source/dialogs/cuigaldlg.cxx @@ -0,0 +1,1009 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::ucbhelper; +using namespace ::cppu; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::uno; + + +SearchThread::SearchThread(SearchProgress* pProgress, + TPGalleryThemeProperties* pBrowser, + INetURLObject aStartURL) + : Thread("cuiSearchThread") + , mpProgress(pProgress) + , mpBrowser(pBrowser) + , maStartURL(std::move(aStartURL)) +{ +} + +SearchThread::~SearchThread() +{ +} + +void SearchThread::execute() +{ + const OUString aFileType(mpBrowser->m_xCbbFileType->get_active_text()); + + if (!aFileType.isEmpty()) + { + const int nFileNumber = mpBrowser->m_xCbbFileType->find_text(aFileType); + sal_Int32 nBeginFormat, nEndFormat; + std::vector< OUString > aFormats; + + if( !nFileNumber || nFileNumber == -1) + { + nBeginFormat = 1; + nEndFormat = mpBrowser->m_xCbbFileType->get_count() - 1; + } + else + nBeginFormat = nEndFormat = nFileNumber; + + for (sal_Int32 i = nBeginFormat; i <= nEndFormat; ++i) + aFormats.push_back( mpBrowser->aFilterEntryList[ i ]->aFilterName.toAsciiLowerCase() ); + + ImplSearch( maStartURL, aFormats, mpBrowser->bSearchRecursive ); + } + + Application::PostUserEvent(LINK(mpProgress, SearchProgress, CleanUpHdl)); +} + + +void SearchThread::ImplSearch( const INetURLObject& rStartURL, + const std::vector< OUString >& rFormats, + bool bRecursive ) +{ + { + SolarMutexGuard aGuard; + + mpProgress->SetDirectory( rStartURL ); + } + + try + { + css::uno::Reference< XCommandEnvironment > xEnv; + Content aCnt( rStartURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext() ); + Sequence< OUString > aProps( 2 ); + + aProps.getArray()[ 0 ] = "IsFolder"; + aProps.getArray()[ 1 ] = "IsDocument"; + css::uno::Reference< XResultSet > xResultSet( + aCnt.createCursor( aProps ) ); + + if( xResultSet.is() ) + { + css::uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW ); + css::uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW ); + + while( xResultSet->next() && schedule() ) + { + INetURLObject aFoundURL( xContentAccess->queryContentIdentifierString() ); + DBG_ASSERT( aFoundURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" ); + + bool bFolder = xRow->getBoolean( 1 ); // property "IsFolder" + if ( xRow->wasNull() ) + bFolder = false; + + if( bRecursive && bFolder ) + ImplSearch( aFoundURL, rFormats, true ); + else + { + bool bDocument = xRow->getBoolean( 2 ); // property "IsDocument" + if ( xRow->wasNull() ) + bDocument = false; + + if( bDocument ) + { + GraphicDescriptor aDesc( aFoundURL ); + + if( ( aDesc.Detect() && + std::find( rFormats.begin(), + rFormats.end(), + GraphicDescriptor::GetImportFormatShortName( + aDesc.GetFileFormat() ).toAsciiLowerCase() ) + != rFormats.end() ) || + std::find( rFormats.begin(), + rFormats.end(), + aFoundURL.GetFileExtension().toAsciiLowerCase()) + != rFormats.end() ) + { + SolarMutexGuard aGuard; + + mpBrowser->aFoundList.push_back( + aFoundURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) + ); + mpBrowser->m_xLbxFound->insert_text( + mpBrowser->aFoundList.size() - 1, + GetReducedString(aFoundURL, 50)); + } + } + } + } + } + } + catch (const ContentCreationException&) + { + } + catch (const css::uno::RuntimeException&) + { + } + catch (const css::uno::Exception&) + { + } +} + +SearchProgress::SearchProgress(weld::Window* pParent, TPGalleryThemeProperties* pTabPage, INetURLObject aStartURL) + : GenericDialogController(pParent, "cui/ui/gallerysearchprogress.ui", "GallerySearchProgress") + , startUrl_(std::move(aStartURL)) + , m_pTabPage(pTabPage) + , m_xFtSearchDir(m_xBuilder->weld_label("dir")) + , m_xFtSearchType(m_xBuilder->weld_label("file")) + , m_xBtnCancel(m_xBuilder->weld_button("cancel")) +{ + m_xFtSearchType->set_size_request(m_xFtSearchType->get_preferred_size().Width(), -1); + m_xBtnCancel->connect_clicked(LINK(this, SearchProgress, ClickCancelBtn)); +} + +SearchProgress::~SearchProgress() +{ +} + +IMPL_LINK_NOARG(SearchProgress, ClickCancelBtn, weld::Button&, void) +{ + if (m_aSearchThread.is()) + m_aSearchThread->terminate(); +} + +IMPL_LINK_NOARG(SearchProgress, CleanUpHdl, void*, void) +{ + if (m_aSearchThread.is()) + m_aSearchThread->join(); + + m_xDialog->response(RET_OK); +} + +void SearchProgress::LaunchThread() +{ + assert(!m_aSearchThread.is()); + m_aSearchThread = new SearchThread(this, m_pTabPage, startUrl_); + m_aSearchThread->launch(); +} + +TakeThread::TakeThread( + TakeProgress* pProgress, + TPGalleryThemeProperties* pBrowser, + TokenList_impl& rTakenList +) : + Thread ( "cuiTakeThread" ), + mpProgress ( pProgress ), + mpBrowser ( pBrowser ), + mrTakenList ( rTakenList ) +{ +} + + +TakeThread::~TakeThread() +{ +} + +void TakeThread::execute() +{ + sal_Int32 nEntries; + GalleryTheme* pThm = mpBrowser->GetXChgData()->pTheme; + std::unique_ptr pStatusProgress; + + std::vector aSelectedRows; + + { + SolarMutexGuard aGuard; + pStatusProgress.reset(new GalleryProgress); + if (mpBrowser->bTakeAll) + nEntries = mpBrowser->m_xLbxFound->n_children(); + else + { + aSelectedRows = mpBrowser->m_xLbxFound->get_selected_rows(); + nEntries = aSelectedRows.size(); + } + pThm->LockBroadcaster(); + } + + for( sal_Int32 i = 0; i < nEntries && schedule(); ++i ) + { + const sal_Int32 nPos = mpBrowser->bTakeAll ? i : aSelectedRows[i]; + const INetURLObject aURL( mpBrowser->aFoundList[ nPos ]); + + mrTakenList.push_back( nPos ); + + { + SolarMutexGuard aGuard; + + mpProgress->SetFile( aURL ); + pStatusProgress->Update( i, nEntries - 1 ); + pThm->InsertURL( aURL ); + } + } + + { + SolarMutexGuard aGuard; + + pThm->UnlockBroadcaster(); + pStatusProgress.reset(); + } + + Application::PostUserEvent(LINK(mpProgress, TakeProgress, CleanUpHdl)); +} + +TakeProgress::TakeProgress(weld::Window* pParent, TPGalleryThemeProperties* pTabPage) + : GenericDialogController(pParent, "cui/ui/galleryapplyprogress.ui", + "GalleryApplyProgress") + , m_pParent(pParent) + , m_pTabPage(pTabPage) + , m_xFtTakeFile(m_xBuilder->weld_label("file")) + , m_xBtnCancel(m_xBuilder->weld_button("cancel")) +{ + m_xBtnCancel->connect_clicked(LINK(this, TakeProgress, ClickCancelBtn)); +} + +TakeProgress::~TakeProgress() +{ +} + +IMPL_LINK_NOARG(TakeProgress, ClickCancelBtn, weld::Button&, void) +{ + if (maTakeThread.is()) + maTakeThread->terminate(); +} + + +IMPL_LINK_NOARG(TakeProgress, CleanUpHdl, void*, void) +{ + if (maTakeThread.is()) + maTakeThread->join(); + + std::vector > aRemoveEntries(m_pTabPage->aFoundList.size(), false); + std::vector< OUString > aRemainingVector; + sal_uInt32 i, nCount; + + std::unique_ptr xWait(new weld::WaitObject(m_pParent)); + + m_pTabPage->m_xLbxFound->select(-1); + m_pTabPage->m_xLbxFound->freeze(); + + // mark all taken positions in aRemoveEntries + for( i = 0, nCount = maTakenList.size(); i < nCount; ++i ) + aRemoveEntries[ maTakenList[ i ] ] = true; + maTakenList.clear(); + + // refill found list + for( i = 0, nCount = aRemoveEntries.size(); i < nCount; ++i ) + if( !aRemoveEntries[ i ] ) + aRemainingVector.push_back( m_pTabPage->aFoundList[i] ); + + std::swap(m_pTabPage->aFoundList, aRemainingVector); + aRemainingVector.clear(); + + // refill list box + for( i = 0, nCount = aRemoveEntries.size(); i < nCount; ++i ) + if( !aRemoveEntries[ i ] ) + aRemainingVector.push_back(m_pTabPage->m_xLbxFound->get_text(i)); + + m_pTabPage->m_xLbxFound->clear(); + for( i = 0, nCount = aRemainingVector.size(); i < nCount; ++i ) + m_pTabPage->m_xLbxFound->append_text(aRemainingVector[i]); + aRemainingVector.clear(); + + m_pTabPage->m_xLbxFound->thaw(); + m_pTabPage->SelectFoundHdl( *m_pTabPage->m_xLbxFound ); + + xWait.reset(); + + m_xDialog->response(RET_OK); +} + +void TakeProgress::LaunchThread() +{ + assert(!maTakeThread.is()); + maTakeThread = new TakeThread(this, m_pTabPage, maTakenList); + maTakeThread->launch(); +} + +ActualizeProgress::ActualizeProgress(weld::Widget* pWindow, GalleryTheme* pThm) + : GenericDialogController(pWindow, "cui/ui/galleryupdateprogress.ui", + "GalleryUpdateProgress") + , pIdle(nullptr) + , pTheme(pThm) + , m_xFtActualizeFile(m_xBuilder->weld_label("file")) + , m_xBtnCancel(m_xBuilder->weld_button("cancel")) +{ + m_xBtnCancel->connect_clicked(LINK(this, ActualizeProgress, ClickCancelBtn)); +} + +ActualizeProgress::~ActualizeProgress() +{ +} + +short ActualizeProgress::run() +{ + pIdle = new Idle("ActualizeProgressTimeout"); + pIdle->SetInvokeHandler( LINK( this, ActualizeProgress, TimeoutHdl ) ); + pIdle->SetPriority( TaskPriority::LOWEST ); + pIdle->Start(); + + return GenericDialogController::run(); +} + +IMPL_LINK_NOARG(ActualizeProgress, ClickCancelBtn, weld::Button&, void) +{ + pTheme->AbortActualize(); + m_xDialog->response(RET_OK); +} + +IMPL_LINK( ActualizeProgress, TimeoutHdl, Timer*, _pTimer, void) +{ + if (_pTimer) + { + _pTimer->Stop(); + delete _pTimer; + } + + pTheme->Actualize(LINK(this, ActualizeProgress, ActualizeHdl), &aStatusProgress); + ClickCancelBtn(*m_xBtnCancel); +} + +IMPL_LINK( ActualizeProgress, ActualizeHdl, const INetURLObject&, rURL, void ) +{ + Application::Reschedule(true); + m_xFtActualizeFile->set_label(GetReducedString(rURL, 30)); +} + +TitleDialog::TitleDialog(weld::Widget* pParent, const OUString& rOldTitle) + : GenericDialogController(pParent, "cui/ui/gallerytitledialog.ui", "GalleryTitleDialog") + , m_xEdit(m_xBuilder->weld_entry("entry")) +{ + m_xEdit->set_text(rOldTitle); + m_xEdit->grab_focus(); +} + +TitleDialog::~TitleDialog() +{ +} + +GalleryIdDialog::GalleryIdDialog(weld::Widget* pParent, GalleryTheme* _pThm) + : GenericDialogController(pParent, "cui/ui/gallerythemeiddialog.ui", "GalleryThemeIDDialog") + , m_pThm(_pThm) + , m_xBtnOk(m_xBuilder->weld_button("ok")) + , m_xLbResName(m_xBuilder->weld_combo_box("entry")) +{ + m_xLbResName->append_text("!!! No Id !!!"); + + GalleryTheme::InsertAllThemes(*m_xLbResName); + + m_xLbResName->set_active(m_pThm->GetId()); + m_xLbResName->grab_focus(); + + m_xBtnOk->connect_clicked(LINK(this, GalleryIdDialog, ClickOkHdl)); +} + +GalleryIdDialog::~GalleryIdDialog() +{ +} + +IMPL_LINK_NOARG(GalleryIdDialog, ClickOkHdl, weld::Button&, void) +{ + Gallery* pGal = m_pThm->GetParent(); + const sal_uInt32 nId = GetId(); + bool bDifferentThemeExists = false; + + for( size_t i = 0, nCount = pGal->GetThemeCount(); i < nCount && !bDifferentThemeExists; i++ ) + { + const GalleryThemeEntry* pInfo = pGal->GetThemeInfo( i ); + + if ((pInfo->GetId() == nId) && (pInfo->GetThemeName() != m_pThm->GetName())) + { + OUString aStr = CuiResId( RID_CUISTR_GALLERY_ID_EXISTS ) + + " (" + pInfo->GetThemeName() + ")"; + + std::unique_ptr xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + aStr)); + xInfoBox->run(); + m_xLbResName->grab_focus(); + bDifferentThemeExists = true; + } + } + + if (!bDifferentThemeExists) + m_xDialog->response(RET_OK); +} + +GalleryThemeProperties::GalleryThemeProperties(weld::Widget* pParent, + ExchangeData* _pData, SfxItemSet const * pItemSet) + : SfxTabDialogController(pParent, "cui/ui/gallerythemedialog.ui", + "GalleryThemeDialog", pItemSet) + , pData(_pData) +{ + AddTabPage("general", TPGalleryThemeGeneral::Create, nullptr); + AddTabPage("files", TPGalleryThemeProperties::Create, nullptr); + if (pData->pTheme->IsReadOnly()) + RemoveTabPage("files"); + + OUString aText = m_xDialog->get_title().replaceFirst( "%1", pData->pTheme->GetName() ); + + if (pData->pTheme->IsReadOnly()) + aText += " " + CuiResId( RID_CUISTR_GALLERY_READONLY ); + + m_xDialog->set_title(aText); +} + +void GalleryThemeProperties::PageCreated(const OUString& rId, SfxTabPage &rPage) +{ + if (rId == "general") + static_cast( rPage ).SetXChgData( pData ); + else + static_cast( rPage ).SetXChgData( pData ); +} + +TPGalleryThemeGeneral::TPGalleryThemeGeneral(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "cui/ui/gallerygeneralpage.ui", "GalleryGeneralPage", &rSet) + , pData(nullptr) + , m_xFiMSImage(m_xBuilder->weld_image("image")) + , m_xEdtMSName(m_xBuilder->weld_entry("name")) + , m_xFtMSShowType(m_xBuilder->weld_label("type")) + , m_xFtMSShowPath(m_xBuilder->weld_label("location")) + , m_xFtMSShowContent(m_xBuilder->weld_label("contents")) + , m_xFtMSShowChangeDate(m_xBuilder->weld_label("modified")) +{ +} + +void TPGalleryThemeGeneral::SetXChgData( ExchangeData* _pData ) +{ + pData = _pData; + + GalleryTheme* pThm = pData->pTheme; + OUString aOutStr( OUString::number(pThm->GetObjectCount()) ); + OUString aObjStr( CuiResId( RID_CUISTR_GALLERYPROPS_OBJECT ) ); + OUString aAccess; + OUString aType( SvxResId( RID_SVXSTR_GALLERYPROPS_GALTHEME ) ); + bool bReadOnly = pThm->IsReadOnly(); + + m_xEdtMSName->set_text(pThm->GetName()); + m_xEdtMSName->set_editable(!bReadOnly); + m_xEdtMSName->set_sensitive(!bReadOnly); + + if( pThm->IsReadOnly() ) + aType += CuiResId( RID_CUISTR_GALLERY_READONLY ); + + m_xFtMSShowType->set_label(aType); + m_xFtMSShowPath->set_label(pThm->getThemeURL().GetMainURL(INetURLObject::DecodeMechanism::Unambiguous)); + + // singular or plural? + if ( 1 == pThm->GetObjectCount() ) + aObjStr = aObjStr.getToken( 0, ';' ); + else + aObjStr = aObjStr.getToken( 1, ';' ); + + aOutStr += " " + aObjStr; + + m_xFtMSShowContent->set_label(aOutStr); + + // get locale wrapper (singleton) + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& aLocaleData = aSysLocale.GetLocaleData(); + + // ChangeDate/Time + aAccess = aLocaleData.getDate( pData->aThemeChangeDate ) + ", " + aLocaleData.getTime( pData->aThemeChangeTime ); + m_xFtMSShowChangeDate->set_label(aAccess); + + // set image + OUString sId; + + if( pThm->IsReadOnly() ) + sId = RID_SVXBMP_THEME_READONLY_BIG; + else if( pThm->IsDefault() ) + sId = RID_SVXBMP_THEME_DEFAULT_BIG; + else + sId = RID_SVXBMP_THEME_NORMAL_BIG; + + m_xFiMSImage->set_from_icon_name(sId); +} + +bool TPGalleryThemeGeneral::FillItemSet( SfxItemSet* /*rSet*/ ) +{ + pData->aEditedTitle = m_xEdtMSName->get_text(); + return true; +} + +std::unique_ptr TPGalleryThemeGeneral::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique(pPage, pController, *rSet); +} + +TPGalleryThemeProperties::TPGalleryThemeProperties(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "cui/ui/galleryfilespage.ui", "GalleryFilesPage", &rSet) + , pData(nullptr) + , aPreviewTimer("cui TPGalleryThemeProperties aPreviewTimer") + , bEntriesFound(false) + , bInputAllowed(true) + , bTakeAll(false) + , bSearchRecursive(false) + , xDialogListener(new ::svt::DialogClosedListener()) + , m_xCbbFileType(m_xBuilder->weld_combo_box("filetype")) + , m_xLbxFound(m_xBuilder->weld_tree_view("files")) + , m_xBtnSearch(m_xBuilder->weld_button("findfiles")) + , m_xBtnTake(m_xBuilder->weld_button("add")) + , m_xBtnTakeAll(m_xBuilder->weld_button("addall")) + , m_xCbxPreview(m_xBuilder->weld_check_button("preview")) + , m_xWndPreview(new weld::CustomWeld(*m_xBuilder, "image", m_aWndPreview)) +{ + m_xLbxFound->set_size_request(m_xLbxFound->get_approximate_digit_width() * 35, + m_xLbxFound->get_height_rows(15)); + m_xLbxFound->set_selection_mode(SelectionMode::Multiple); + xDialogListener->SetDialogClosedLink( LINK( this, TPGalleryThemeProperties, DialogClosedHdl ) ); +} + +void TPGalleryThemeProperties::SetXChgData( ExchangeData* _pData ) +{ + pData = _pData; + + aPreviewTimer.SetInvokeHandler( LINK( this, TPGalleryThemeProperties, PreviewTimerHdl ) ); + aPreviewTimer.SetTimeout( 500 ); + m_xBtnSearch->connect_clicked(LINK(this, TPGalleryThemeProperties, ClickSearchHdl)); + m_xBtnTake->connect_clicked(LINK(this, TPGalleryThemeProperties, ClickTakeHdl)); + m_xBtnTakeAll->connect_clicked(LINK(this, TPGalleryThemeProperties, ClickTakeAllHdl)); + m_xCbxPreview->connect_toggled(LINK(this, TPGalleryThemeProperties, ClickPreviewHdl)); + m_xCbbFileType->connect_changed(LINK(this, TPGalleryThemeProperties, SelectFileTypeHdl)); + m_xLbxFound->connect_row_activated(LINK(this, TPGalleryThemeProperties, DClickFoundHdl)); + m_xLbxFound->connect_changed(LINK(this, TPGalleryThemeProperties, SelectFoundHdl)); + m_xLbxFound->append_text(CuiResId(RID_CUISTR_GALLERY_NOFILES)); + m_xLbxFound->show(); + + FillFilterList(); + + m_xBtnTake->set_sensitive(true); + m_xBtnTakeAll->set_sensitive(false); + m_xCbxPreview->set_sensitive(false); +} + +void TPGalleryThemeProperties::StartSearchFiles( std::u16string_view _rFolderURL, short _nDlgResult ) +{ + if ( RET_OK == _nDlgResult ) + { + aURL = INetURLObject( _rFolderURL ); + bSearchRecursive = true; // UI choice no longer possible, windows file picker allows no user controls + SearchFiles(); + } +} + +TPGalleryThemeProperties::~TPGalleryThemeProperties() +{ + xMediaPlayer.clear(); + xDialogListener.clear(); + aFilterEntryList.clear(); +} + +std::unique_ptr TPGalleryThemeProperties::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique(pPage, pController, *rSet); +} + +OUString TPGalleryThemeProperties::addExtension( const OUString& _rDisplayText, std::u16string_view _rExtension ) +{ + OUString sRet = _rDisplayText; + if ( sRet.indexOf( "(*.*)" ) == -1 ) + { + sRet += OUString::Concat(" (") + _rExtension + ")"; + } + return sRet; +} + +void TPGalleryThemeProperties::FillFilterList() +{ + GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter(); + OUString aExt; + OUString aName; + sal_uInt16 i, nKeyCount; + + // graphic filters + for( i = 0, nKeyCount = rFilter.GetImportFormatCount(); i < nKeyCount; i++ ) + { + aExt = rFilter.GetImportFormatShortName( i ); + aName = rFilter.GetImportFormatName( i ); + size_t entryIndex = 0; + FilterEntry* pTestEntry = aFilterEntryList.empty() ? nullptr : aFilterEntryList[ entryIndex ].get(); + bool bInList = false; + + OUString aExtensions; + int j = 0; + OUString sWildcard; + while( true ) + { + sWildcard = rFilter.GetImportWildcard( i, j++ ); + if ( sWildcard.isEmpty() ) + break; + if ( aExtensions.indexOf( sWildcard ) == -1 ) + { + if ( !aExtensions.isEmpty() ) + aExtensions += ";"; + aExtensions += sWildcard; + } + } + aName = addExtension( aName, aExtensions ); + + while( pTestEntry ) + { + if ( pTestEntry->aFilterName == aExt ) + { + bInList = true; + break; + } + pTestEntry = ( ++entryIndex < aFilterEntryList.size() ) + ? aFilterEntryList[ entryIndex ].get() : nullptr; + } + if ( !bInList ) + { + std::unique_ptr pFilterEntry(new FilterEntry); + pFilterEntry->aFilterName = aExt; + m_xCbbFileType->append_text(aName); + aFilterEntryList.push_back(std::move(pFilterEntry)); + } + } + +#if HAVE_FEATURE_AVMEDIA + // media filters + static constexpr OUString aWildcard = u"*."_ustr; + ::avmedia::FilterNameVector aFilters= ::avmedia::MediaWindow::getMediaFilters(); + + for(const std::pair & aFilter : aFilters) + { + for( sal_Int32 nIndex = 0; nIndex >= 0; ) + { + OUString aFilterWildcard( aWildcard ); + + std::unique_ptr pFilterEntry(new FilterEntry); + pFilterEntry->aFilterName = aFilter.second.getToken( 0, ';', nIndex ); + aFilterWildcard += pFilterEntry->aFilterName; + m_xCbbFileType->append_text(addExtension(aFilter.first, aFilterWildcard)); + aFilterEntryList.push_back( std::move(pFilterEntry) ); + } + } +#endif + + // 'All' filters + OUString aExtensions; + + // graphic filters + for ( i = 0; i < nKeyCount; ++i ) + { + int j = 0; + OUString sWildcard; + while( true ) + { + sWildcard = rFilter.GetImportWildcard( i, j++ ); + if ( sWildcard.isEmpty() ) + break; + if ( aExtensions.indexOf( sWildcard ) == -1 ) + { + if ( !aExtensions.isEmpty() ) + aExtensions += ";"; + + aExtensions += sWildcard; + } + } + } + +#if HAVE_FEATURE_AVMEDIA + // media filters + for(const std::pair & aFilter : aFilters) + { + for( sal_Int32 nIndex = 0; nIndex >= 0; ) + { + if ( !aExtensions.isEmpty() ) + aExtensions += ";"; + aExtensions += OUString::Concat(aWildcard) + o3tl::getToken(aFilter.second, 0, ';', nIndex ); + } + } +#endif + +#if defined(_WIN32) + if (aExtensions.getLength() > 240) + aExtensions = "*.*"; +#endif + + std::unique_ptr pFilterEntry(new FilterEntry); + pFilterEntry->aFilterName = CuiResId(RID_CUISTR_GALLERY_ALLFILES); + pFilterEntry->aFilterName = addExtension(pFilterEntry->aFilterName, aExtensions); + m_xCbbFileType->insert_text(0, pFilterEntry->aFilterName); + m_xCbbFileType->set_active(0); + aFilterEntryList.insert(aFilterEntryList.begin(), std::move(pFilterEntry)); +} + +IMPL_LINK_NOARG(TPGalleryThemeProperties, SelectFileTypeHdl, weld::ComboBox&, void) +{ + OUString aText(m_xCbbFileType->get_active_text()); + + if( bInputAllowed && ( aLastFilterName != aText ) ) + { + aLastFilterName = aText; + + std::unique_ptr xBuilder(Application::CreateBuilder(GetFrameWeld(), "cui/ui/queryupdategalleryfilelistdialog.ui")); + std::unique_ptr xQuery(xBuilder->weld_message_dialog("QueryUpdateFileListDialog")); + if (xQuery->run() == RET_YES) + SearchFiles(); + } +} + +void TPGalleryThemeProperties::SearchFiles() +{ + auto xProgress = std::make_shared(GetFrameWeld(), this, aURL); + + aFoundList.clear(); + m_xLbxFound->clear(); + + xProgress->SetFileType( m_xCbbFileType->get_active_text() ); + xProgress->SetDirectory( INetURLObject() ); + + xProgress->LaunchThread(); + weld::DialogController::runAsync(xProgress, [this](sal_Int32 nResult) { + EndSearchProgressHdl(nResult); + }); +} + +IMPL_LINK_NOARG(TPGalleryThemeProperties, ClickSearchHdl, weld::Button&, void) +{ + if( !bInputAllowed ) + return; + + try + { + // setup folder picker + css::uno::Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + xFolderPicker = sfx2::createFolderPicker(xContext, GetFrameWeld()); + + OUString aDlgPathName( SvtPathOptions().GetGraphicPath() ); + xFolderPicker->setDisplayDirectory(aDlgPathName); + + aPreviewTimer.Stop(); + + css::uno::Reference< XAsynchronousExecutableDialog > xAsyncDlg( xFolderPicker, UNO_QUERY ); + if ( xAsyncDlg.is() ) + xAsyncDlg->startExecuteModal( xDialogListener ); + else + { + if( xFolderPicker->execute() == RET_OK ) + { + aURL = INetURLObject( xFolderPicker->getDirectory() ); + bSearchRecursive = true; // UI choice no longer possible, windows file picker allows no user controls + SearchFiles(); + } + } + } + catch (const IllegalArgumentException&) + { + OSL_FAIL( "Folder picker failed with illegal arguments" ); + } +} + +void TPGalleryThemeProperties::TakeFiles() +{ + if (m_xLbxFound->count_selected_rows() || (bTakeAll && bEntriesFound)) + { + auto xTakeProgress = std::make_shared(GetFrameWeld(), this); + xTakeProgress->LaunchThread(); + weld::DialogController::runAsync(xTakeProgress, [](sal_Int32 /*nResult*/) { + /* no postprocessing needed, pTakeProgress + will be disposed in TakeProgress::CleanupHdl */ + }); + + } +} + +IMPL_LINK_NOARG(TPGalleryThemeProperties, ClickPreviewHdl, weld::Toggleable&, void) +{ + if ( !bInputAllowed ) + return; + + aPreviewTimer.Stop(); + aPreviewString.clear(); + + if (!m_xCbxPreview->get_active()) + { + xMediaPlayer.clear(); + m_aWndPreview.SetGraphic(Graphic()); + m_aWndPreview.Invalidate(); + } + else + DoPreview(); +} + +void TPGalleryThemeProperties::DoPreview() +{ + int nIndex = m_xLbxFound->get_selected_index(); + OUString aString(m_xLbxFound->get_text(nIndex)); + + if (aString == aPreviewString) + return; + + INetURLObject _aURL(aFoundList[nIndex]); + bInputAllowed = false; + + if (!m_aWndPreview.SetGraphic(_aURL)) + { + weld::WaitObject aWaitObject(GetFrameWeld()); + ErrorHandler::HandleError(ERRCODE_IO_NOTEXISTSPATH, GetFrameWeld()); + } +#if HAVE_FEATURE_AVMEDIA + else if( ::avmedia::MediaWindow::isMediaURL( _aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ), "" ) ) + { + xMediaPlayer = ::avmedia::MediaWindow::createPlayer( _aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), "" ); + if( xMediaPlayer.is() ) + xMediaPlayer->start(); + } +#endif + bInputAllowed = true; + aPreviewString = aString; +} + +IMPL_LINK_NOARG(TPGalleryThemeProperties, ClickTakeHdl, weld::Button&, void) +{ + if( !bInputAllowed ) + return; + + aPreviewTimer.Stop(); + + if (!m_xLbxFound->count_selected_rows() || !bEntriesFound) + { + SvxOpenGraphicDialog aDlg(CuiResId(RID_CUISTR_KEY_GALLERY_DIR), GetFrameWeld()); + aDlg.EnableLink(false); + aDlg.AsLink(false); + + if( !aDlg.Execute() ) + pData->pTheme->InsertURL( INetURLObject( aDlg.GetPath() ) ); + } + else + { + bTakeAll = false; + TakeFiles(); + } +} + +IMPL_LINK_NOARG(TPGalleryThemeProperties, ClickTakeAllHdl, weld::Button&, void) +{ + if( bInputAllowed ) + { + aPreviewTimer.Stop(); + bTakeAll = true; + TakeFiles(); + } +} + +IMPL_LINK_NOARG(TPGalleryThemeProperties, SelectFoundHdl, weld::TreeView&, void) +{ + if (!bInputAllowed) + return; + + bool bPreviewPossible = false; + + aPreviewTimer.Stop(); + + if( bEntriesFound ) + { + if (m_xLbxFound->count_selected_rows() == 1) + { + m_xCbxPreview->set_sensitive(true); + bPreviewPossible = true; + } + else + m_xCbxPreview->set_sensitive(false); + + if( !aFoundList.empty() ) + m_xBtnTakeAll->set_sensitive(true); + else + m_xBtnTakeAll->set_sensitive(false); + } + + if (bPreviewPossible && m_xCbxPreview->get_active()) + aPreviewTimer.Start(); +} + +IMPL_LINK_NOARG(TPGalleryThemeProperties, DClickFoundHdl, weld::TreeView&, bool) +{ + if( bInputAllowed ) + { + aPreviewTimer.Stop(); + + if (m_xLbxFound->count_selected_rows() == 1 && bEntriesFound) + ClickTakeHdl(*m_xBtnTake); + } + return true; +} + +IMPL_LINK_NOARG(TPGalleryThemeProperties, PreviewTimerHdl, Timer *, void) +{ + aPreviewTimer.Stop(); + DoPreview(); +} + +void TPGalleryThemeProperties::EndSearchProgressHdl(sal_Int32 /*nResult*/) +{ + if( !aFoundList.empty() ) + { + m_xLbxFound->select(0); + m_xBtnTakeAll->set_sensitive(true); + m_xCbxPreview->set_sensitive(true); + bEntriesFound = true; + } + else + { + m_xLbxFound->append_text(CuiResId(RID_CUISTR_GALLERY_NOFILES)); + m_xBtnTakeAll->set_sensitive(false); + m_xCbxPreview->set_sensitive(false); + bEntriesFound = false; + } +} + +IMPL_LINK( TPGalleryThemeProperties, DialogClosedHdl, css::ui::dialogs::DialogClosedEvent*, pEvt, void ) +{ + DBG_ASSERT( xFolderPicker.is(), "TPGalleryThemeProperties::DialogClosedHdl(): no folder picker" ); + + OUString sURL = xFolderPicker->getDirectory(); + StartSearchFiles( sURL, pEvt->DialogResult ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/cuigrfflt.cxx b/cui/source/dialogs/cuigrfflt.cxx new file mode 100644 index 0000000000..c29bc4947e --- /dev/null +++ b/cui/source/dialogs/cuigrfflt.cxx @@ -0,0 +1,469 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CuiGraphicPreviewWindow::CuiGraphicPreviewWindow() + : mpOrigGraphic(nullptr) + , mfScaleX(0.0) + , mfScaleY(0.0) +{ +} + +void CuiGraphicPreviewWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + OutputDevice &rDevice = pDrawingArea->get_ref_device(); + maOutputSizePixel = rDevice.LogicToPixel(Size(81, 73), MapMode(MapUnit::MapAppFont)); + pDrawingArea->set_size_request(maOutputSizePixel.Width(), maOutputSizePixel.Height()); +} + +void CuiGraphicPreviewWindow::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&) +{ + rRenderContext.SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetDialogColor())); + rRenderContext.Erase(); + + const Size aOutputSize(GetOutputSizePixel()); + + if (maPreview.IsAnimated()) + { + const Size aGraphicSize(rRenderContext.LogicToPixel(maPreview.GetPrefSize(), maPreview.GetPrefMapMode())); + const Point aGraphicPosition((aOutputSize.Width() - aGraphicSize.Width() ) >> 1, + (aOutputSize.Height() - aGraphicSize.Height() ) >> 1); + maPreview.StartAnimation(rRenderContext, aGraphicPosition, aGraphicSize); + } + else + { + const Size aGraphicSize(maPreview.GetSizePixel()); + const Point aGraphicPosition((aOutputSize.Width() - aGraphicSize.Width()) >> 1, + (aOutputSize.Height() - aGraphicSize.Height()) >> 1); + maPreview.Draw(rRenderContext, aGraphicPosition, aGraphicSize); + } +} + +void CuiGraphicPreviewWindow::SetPreview(const Graphic& rGraphic) +{ + maPreview = rGraphic; + Invalidate(); +} + +void CuiGraphicPreviewWindow::ScaleImageToFit() +{ + if (!mpOrigGraphic) + return; + + maScaledOrig = *mpOrigGraphic; + + const Size aPreviewSize(GetOutputSizePixel()); + Size aGrfSize(maOrigGraphicSizePixel); + + if( mpOrigGraphic->GetType() == GraphicType::Bitmap && + aPreviewSize.Width() && aPreviewSize.Height() && + aGrfSize.Width() && aGrfSize.Height() ) + { + const double fGrfWH = static_cast(aGrfSize.Width()) / aGrfSize.Height(); + const double fPreWH = static_cast(aPreviewSize.Width()) / aPreviewSize.Height(); + + if( fGrfWH < fPreWH ) + { + aGrfSize.setWidth( static_cast( aPreviewSize.Height() * fGrfWH ) ); + aGrfSize.setHeight( aPreviewSize.Height() ); + } + else + { + aGrfSize.setWidth( aPreviewSize.Width() ); + aGrfSize.setHeight( static_cast( aPreviewSize.Width() / fGrfWH ) ); + } + + mfScaleX = static_cast(aGrfSize.Width()) / maOrigGraphicSizePixel.Width(); + mfScaleY = static_cast(aGrfSize.Height()) / maOrigGraphicSizePixel.Height(); + + if( !mpOrigGraphic->IsAnimated() ) + { + BitmapEx aBmpEx( mpOrigGraphic->GetBitmapEx() ); + + if( aBmpEx.Scale( aGrfSize ) ) + maScaledOrig = aBmpEx; + } + } + + maModifyHdl.Call(nullptr); +} + +void CuiGraphicPreviewWindow::Resize() +{ + maOutputSizePixel = GetOutputSizePixel(); + ScaleImageToFit(); +} + +GraphicFilterDialog::GraphicFilterDialog(weld::Window* pParent, + const OUString& rUIXMLDescription, const OUString& rID, + const Graphic& rGraphic) + : GenericDialogController(pParent, rUIXMLDescription, rID) + , maTimer("cui GraphicFilterDialog maTimer") + , maModifyHdl(LINK(this, GraphicFilterDialog, ImplModifyHdl)) + , mxPreview(new weld::CustomWeld(*m_xBuilder, "preview", maPreview)) +{ + bIsBitmap = rGraphic.GetType() == GraphicType::Bitmap; + + maTimer.SetInvokeHandler(LINK(this, GraphicFilterDialog, ImplPreviewTimeoutHdl)); + maTimer.SetTimeout(5); + + maPreview.init(&rGraphic, maModifyHdl); +} + +IMPL_LINK_NOARG(GraphicFilterDialog, ImplPreviewTimeoutHdl, Timer *, void) +{ + maTimer.Stop(); + maPreview.SetPreview(GetFilteredGraphic(maPreview.GetScaledOriginal(), + maPreview.GetScaleX(), maPreview.GetScaleY())); +} + +IMPL_LINK_NOARG(GraphicFilterDialog, ImplModifyHdl, LinkParamNone*, void) +{ + if (bIsBitmap) + { + maTimer.Stop(); + maTimer.Start(); + } +} + +GraphicFilterMosaic::GraphicFilterMosaic(weld::Window* pParent, const Graphic& rGraphic, + sal_uInt16 nTileWidth, sal_uInt16 nTileHeight, bool bEnhanceEdges) + : GraphicFilterDialog(pParent, "cui/ui/mosaicdialog.ui", "MosaicDialog", rGraphic) + , mxMtrWidth(m_xBuilder->weld_metric_spin_button("width", FieldUnit::PIXEL)) + , mxMtrHeight(m_xBuilder->weld_metric_spin_button("height", FieldUnit::PIXEL)) + , mxCbxEdges(m_xBuilder->weld_check_button("edges")) +{ + mxMtrWidth->set_value(nTileWidth, FieldUnit::PIXEL); + mxMtrWidth->set_max(GetGraphicSizePixel().Width(), FieldUnit::PIXEL); + mxMtrWidth->connect_value_changed(LINK(this, GraphicFilterMosaic, EditModifyHdl)); + + mxMtrHeight->set_value(nTileHeight, FieldUnit::PIXEL); + mxMtrHeight->set_max(GetGraphicSizePixel().Height(), FieldUnit::PIXEL); + mxMtrHeight->connect_value_changed(LINK(this, GraphicFilterMosaic, EditModifyHdl)); + + mxCbxEdges->set_active(bEnhanceEdges); + mxCbxEdges->connect_toggled(LINK(this, GraphicFilterMosaic, CheckBoxModifyHdl)); + + mxMtrWidth->grab_focus(); +} + +IMPL_LINK_NOARG(GraphicFilterMosaic, CheckBoxModifyHdl, weld::Toggleable&, void) +{ + GetModifyHdl().Call(nullptr); +} + +IMPL_LINK_NOARG(GraphicFilterMosaic, EditModifyHdl, weld::MetricSpinButton&, void) +{ + GetModifyHdl().Call(nullptr); +} + +Graphic GraphicFilterMosaic::GetFilteredGraphic( const Graphic& rGraphic, + double fScaleX, double fScaleY ) +{ + Graphic aRet; + tools::Long nTileWidth = static_cast(mxMtrWidth->get_value(FieldUnit::PIXEL)); + tools::Long nTileHeight = static_cast(mxMtrHeight->get_value(FieldUnit::PIXEL)); + const Size aSize( std::max( FRound( nTileWidth * fScaleX ), tools::Long(1) ), + std::max( FRound( nTileHeight * fScaleY ), tools::Long(1) ) ); + + if( rGraphic.IsAnimated() ) + { + Animation aAnim( rGraphic.GetAnimation() ); + + if (BitmapFilter::Filter(aAnim, BitmapMosaicFilter(aSize.getWidth(), aSize.getHeight()))) + { + if( IsEnhanceEdges() ) + (void)BitmapFilter::Filter(aAnim, BitmapSharpenFilter()); + + aRet = aAnim; + } + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapMosaicFilter(aSize.getWidth(), aSize.getHeight()))) + { + if( IsEnhanceEdges() ) + BitmapFilter::Filter(aBmpEx, BitmapSharpenFilter()); + + aRet = aBmpEx; + } + } + + return aRet; +} + +GraphicFilterSmooth::GraphicFilterSmooth(weld::Window* pParent, const Graphic& rGraphic, double nRadius) + : GraphicFilterDialog(pParent, "cui/ui/smoothdialog.ui", "SmoothDialog", rGraphic) + , mxMtrRadius(m_xBuilder->weld_spin_button("radius")) +{ + mxMtrRadius->set_value(nRadius * 10); + mxMtrRadius->connect_value_changed(LINK(this, GraphicFilterSmooth, EditModifyHdl)); + mxMtrRadius->grab_focus(); +} + +IMPL_LINK_NOARG(GraphicFilterSmooth, EditModifyHdl, weld::SpinButton&, void) +{ + GetModifyHdl().Call(nullptr); +} + +Graphic GraphicFilterSmooth::GetFilteredGraphic( const Graphic& rGraphic, double, double ) +{ + Graphic aRet; + double nRadius = mxMtrRadius->get_value() / 10.0; + + if( rGraphic.IsAnimated() ) + { + Animation aAnim( rGraphic.GetAnimation() ); + + if (BitmapFilter::Filter(aAnim, BitmapSmoothenFilter(nRadius))) + { + aRet = aAnim; + } + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapSmoothenFilter(nRadius))) + { + aRet = aBmpEx; + } + } + + return aRet; +} + +GraphicFilterSolarize::GraphicFilterSolarize(weld::Window* pParent, const Graphic& rGraphic, + sal_uInt8 cGreyThreshold, bool bInvert) + : GraphicFilterDialog(pParent, "cui/ui/solarizedialog.ui", "SolarizeDialog", rGraphic) + , mxMtrThreshold(m_xBuilder->weld_metric_spin_button("value", FieldUnit::PERCENT)) + , mxCbxInvert(m_xBuilder->weld_check_button("invert")) +{ + mxMtrThreshold->set_value(FRound(cGreyThreshold / 2.55), FieldUnit::PERCENT); + mxMtrThreshold->connect_value_changed(LINK(this, GraphicFilterSolarize, EditModifyHdl)); + + mxCbxInvert->set_active(bInvert); + mxCbxInvert->connect_toggled(LINK(this, GraphicFilterSolarize, CheckBoxModifyHdl)); +} + +IMPL_LINK_NOARG(GraphicFilterSolarize, CheckBoxModifyHdl, weld::Toggleable&, void) +{ + GetModifyHdl().Call(nullptr); +} + +IMPL_LINK_NOARG(GraphicFilterSolarize, EditModifyHdl, weld::MetricSpinButton&, void) +{ + GetModifyHdl().Call(nullptr); +} + +Graphic GraphicFilterSolarize::GetFilteredGraphic( const Graphic& rGraphic, double, double ) +{ + Graphic aRet; + sal_uInt8 nGreyThreshold = static_cast(FRound(mxMtrThreshold->get_value(FieldUnit::PERCENT) * 2.55)); + + if( rGraphic.IsAnimated() ) + { + Animation aAnim( rGraphic.GetAnimation() ); + + if (BitmapFilter::Filter(aAnim, BitmapSolarizeFilter(nGreyThreshold))) + { + if( IsInvert() ) + aAnim.Invert(); + + aRet = aAnim; + } + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapSolarizeFilter(nGreyThreshold))) + { + if( IsInvert() ) + aBmpEx.Invert(); + + aRet = aBmpEx; + } + } + + return aRet; +} + +GraphicFilterSepia::GraphicFilterSepia(weld::Window* pParent, const Graphic& rGraphic, + sal_uInt16 nSepiaPercent) + : GraphicFilterDialog(pParent, "cui/ui/agingdialog.ui", "AgingDialog", rGraphic) + , mxMtrSepia(m_xBuilder->weld_metric_spin_button("value", FieldUnit::PERCENT)) +{ + mxMtrSepia->set_value(nSepiaPercent, FieldUnit::PERCENT); + mxMtrSepia->connect_value_changed(LINK(this, GraphicFilterSepia, EditModifyHdl)); +} + +IMPL_LINK_NOARG(GraphicFilterSepia, EditModifyHdl, weld::MetricSpinButton&, void) +{ + GetModifyHdl().Call(nullptr); +} + +Graphic GraphicFilterSepia::GetFilteredGraphic( const Graphic& rGraphic, double, double ) +{ + Graphic aRet; + sal_uInt16 nSepiaPct = sal::static_int_cast< sal_uInt16 >(mxMtrSepia->get_value(FieldUnit::PERCENT)); + + if( rGraphic.IsAnimated() ) + { + Animation aAnim( rGraphic.GetAnimation() ); + + if (BitmapFilter::Filter(aAnim, BitmapSepiaFilter(nSepiaPct))) + aRet = aAnim; + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapSepiaFilter(nSepiaPct))) + aRet = aBmpEx; + } + + return aRet; +} + +GraphicFilterPoster::GraphicFilterPoster(weld::Window* pParent, const Graphic& rGraphic, + sal_uInt16 nPosterCount) + : GraphicFilterDialog(pParent, "cui/ui/posterdialog.ui", "PosterDialog", rGraphic) + , mxNumPoster(m_xBuilder->weld_spin_button("value")) +{ + mxNumPoster->set_range(2, vcl::pixelFormatBitCount(rGraphic.GetBitmapEx().getPixelFormat())); + mxNumPoster->set_value(nPosterCount); + mxNumPoster->connect_value_changed(LINK(this, GraphicFilterPoster, EditModifyHdl)); +} + +IMPL_LINK_NOARG(GraphicFilterPoster, EditModifyHdl, weld::SpinButton&, void) +{ + GetModifyHdl().Call(nullptr); +} + +Graphic GraphicFilterPoster::GetFilteredGraphic( const Graphic& rGraphic, double, double ) +{ + Graphic aRet; + const sal_uInt16 nPosterCount = static_cast(mxNumPoster->get_value()); + + if( rGraphic.IsAnimated() ) + { + Animation aAnim( rGraphic.GetAnimation() ); + + if( aAnim.ReduceColors( nPosterCount ) ) + aRet = aAnim; + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapColorQuantizationFilter(nPosterCount))) + aRet = aBmpEx; + } + + return aRet; +} + +bool EmbossControl::MouseButtonDown( const MouseEvent& rEvt ) +{ + const RectPoint eOldRP = GetActualRP(); + + SvxRectCtl::MouseButtonDown( rEvt ); + + if( GetActualRP() != eOldRP ) + maModifyHdl.Call( nullptr ); + + return true; +} + +void EmbossControl::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + SvxRectCtl::SetDrawingArea(pDrawingArea); + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(77, 60), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); +} + +GraphicFilterEmboss::GraphicFilterEmboss(weld::Window* pParent, + const Graphic& rGraphic, RectPoint eLightSource) + : GraphicFilterDialog(pParent, "cui/ui/embossdialog.ui", "EmbossDialog", rGraphic) + , mxCtlLight(new weld::CustomWeld(*m_xBuilder, "lightsource", maCtlLight)) +{ + maCtlLight.SetActualRP(eLightSource); + maCtlLight.SetModifyHdl( GetModifyHdl() ); + maCtlLight.GrabFocus(); +} + +GraphicFilterEmboss::~GraphicFilterEmboss() +{ +} + +Graphic GraphicFilterEmboss::GetFilteredGraphic( const Graphic& rGraphic, double, double ) +{ + Graphic aRet; + Degree100 nAzim, nElev; + + switch (maCtlLight.GetActualRP()) + { + default: OSL_FAIL("svx::GraphicFilterEmboss::GetFilteredGraphic(), unknown Reference Point!" ); + [[fallthrough]]; + case RectPoint::LT: nAzim = 4500_deg100; nElev = 4500_deg100; break; + case RectPoint::MT: nAzim = 9000_deg100; nElev = 4500_deg100; break; + case RectPoint::RT: nAzim = 13500_deg100; nElev = 4500_deg100; break; + case RectPoint::LM: nAzim = 0_deg100; nElev = 4500_deg100; break; + case RectPoint::MM: nAzim = 0_deg100; nElev = 9000_deg100; break; + case RectPoint::RM: nAzim = 18000_deg100; nElev = 4500_deg100; break; + case RectPoint::LB: nAzim = 31500_deg100; nElev = 4500_deg100; break; + case RectPoint::MB: nAzim = 27000_deg100; nElev = 4500_deg100; break; + case RectPoint::RB: nAzim = 22500_deg100; nElev = 4500_deg100; break; + } + + if( rGraphic.IsAnimated() ) + { + Animation aAnim( rGraphic.GetAnimation() ); + + if (BitmapFilter::Filter(aAnim, BitmapEmbossGreyFilter(nAzim, nElev))) + aRet = aAnim; + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapEmbossGreyFilter(nAzim, nElev))) + aRet = aBmpEx; + } + + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/cuihyperdlg.cxx b/cui/source/dialogs/cuihyperdlg.cxx new file mode 100644 index 0000000000..4fec600441 --- /dev/null +++ b/cui/source/dialogs/cuihyperdlg.cxx @@ -0,0 +1,307 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::frame::XFrame; + + +//# # +//# Childwindow-Wrapper-Class # +//# # + + +SvxHlinkCtrl::SvxHlinkCtrl( sal_uInt16 _nId, SfxBindings & rBindings, SvxHpLinkDlg* pDlg ) + : SfxControllerItem ( _nId, rBindings ) + , aRdOnlyForwarder ( SID_READONLY_MODE, *this ) +{ + pParent = pDlg; +} + +void SvxHlinkCtrl::dispose() +{ + pParent = nullptr; + aRdOnlyForwarder.dispose(); + ::SfxControllerItem::dispose(); +} + +void SvxHlinkCtrl::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState ) +{ + if (!(eState == SfxItemState::DEFAULT && pParent)) + return; + + switch ( nSID ) + { + case SID_HYPERLINK_GETLINK : + { + pParent->SetPage( static_cast(pState) ); + } + break; + case SID_READONLY_MODE : + { + pParent->SetReadOnlyMode( static_cast(pState)->GetValue() ); + } + break; + } +} + +// tdf#90496 - remember last used view in hyperlink dialog +OUString SvxHpLinkDlg::msRememberedPageId("internet"); + +//# # +//# Hyperlink - Dialog # +//# # +SvxHpLinkDlg::SvxHpLinkDlg(SfxBindings* pBindings, SfxChildWindow* pChild, weld::Window* pParent) + : SfxModelessDialogController(pBindings, pChild, pParent, "cui/ui/hyperlinkdialog.ui", "HyperlinkDialog") + , pSet ( nullptr ) + , maCtrl ( SID_HYPERLINK_GETLINK, *pBindings, this ) + , mbIsHTMLDoc ( false ) + , m_xIconCtrl(m_xBuilder->weld_notebook("tabcontrol")) + , m_xOKBtn(m_xBuilder->weld_button("ok")) + , m_xApplyBtn(m_xBuilder->weld_button("apply")) + , m_xCancelBtn(m_xBuilder->weld_button("cancel")) + , m_xHelpBtn(m_xBuilder->weld_button("help")) + , m_xResetBtn(m_xBuilder->weld_button("reset")) +{ + m_xIconCtrl->connect_enter_page( LINK ( this, SvxHpLinkDlg, ChosePageHdl_Impl ) ); + m_xIconCtrl->show(); + + // ItemSet + if ( pSet ) + { + pExampleSet.reset(new SfxItemSet( *pSet )); + pOutSet.reset(new SfxItemSet( *pSet->GetPool(), pSet->GetRanges() )); + } + + // Buttons + m_xOKBtn->show(); + m_xCancelBtn->show(); + + if (comphelper::LibreOfficeKit::isActive()) + { + m_xApplyBtn->hide(); + m_xHelpBtn->hide(); + m_xResetBtn->hide(); + } + else + { + m_xApplyBtn->show(); + m_xHelpBtn->show(); + m_xResetBtn->show(); + } + + mbGrabFocus = true; + + // set OK/Cancel - button + m_xCancelBtn->set_label(CuiResId(RID_CUISTR_HYPDLG_CLOSEBUT)); + + // create itemset for tabpages + mpItemSet = std::make_unique>( SfxGetpApp()->GetPool()); + + SvxHyperlinkItem aItem(SID_HYPERLINK_GETLINK); + mpItemSet->Put(aItem); + + SetInputSet (mpItemSet.get()); + + // insert pages + AddTabPage("internet", SvxHyperlinkInternetTp::Create); + AddTabPage("mail", SvxHyperlinkMailTp::Create); + if (!comphelper::LibreOfficeKit::isActive()) + { + AddTabPage("document", SvxHyperlinkDocTp::Create); + AddTabPage("newdocument", SvxHyperlinkNewDocTp::Create); + } + + // tdf#90496 - remember last used view in hyperlink dialog + SetCurPageId(msRememberedPageId); + + // Init Dialog + Start(); + + GetBindings().Update(SID_HYPERLINK_GETLINK); + GetBindings().Update(SID_READONLY_MODE); + + m_xResetBtn->connect_clicked( LINK( this, SvxHpLinkDlg, ResetHdl ) ); + m_xOKBtn->connect_clicked( LINK ( this, SvxHpLinkDlg, ClickOkHdl_Impl ) ); + m_xApplyBtn->connect_clicked ( LINK ( this, SvxHpLinkDlg, ClickApplyHdl_Impl ) ); +} + +SvxHpLinkDlg::~SvxHpLinkDlg() +{ + mbGrabFocus = false; // don't do any grab if tear-down moves focus around during destruction + + // delete config item, so the base class (SfxModelessDialogController) can not load it on the next start + SvtViewOptions aViewOpt( EViewType::TabDialog, OUString::number(SID_HYPERLINK_DIALOG) ); + aViewOpt.Delete(); + + mpItemSet.reset(); + + maCtrl.dispose(); + + maPageList.clear(); + + pRanges.reset(); + pOutSet.reset(); +} + +void SvxHpLinkDlg::Activate() { + if (mbGrabFocus) { + static_cast(GetTabPage(GetCurPageId()))->SetInitFocus(); + mbGrabFocus = false; + } + SfxModelessDialogController::Activate(); +} + +void SvxHpLinkDlg::Close() +{ + if (IsClosing()) + return; + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + pViewFrame->ToggleChildWindow(SID_HYPERLINK_DIALOG); +} + +void SvxHpLinkDlg::Apply() +{ + SfxItemSetFixed aItemSet( SfxGetpApp()->GetPool() ); + + SvxHyperlinkTabPageBase* pCurrentPage = static_cast( + GetTabPage( GetCurPageId() ) ); + + pCurrentPage->FillItemSet( &aItemSet ); + + const SvxHyperlinkItem *aItem = aItemSet.GetItem(SID_HYPERLINK_SETLINK); + if ( !aItem->GetURL().isEmpty() ) + GetDispatcher()->ExecuteList(SID_HYPERLINK_SETLINK, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { aItem }); + + static_cast( GetTabPage( GetCurPageId() ) )->DoApply(); +} + +/// Click on OK button +IMPL_LINK_NOARG(SvxHpLinkDlg, ClickOkHdl_Impl, weld::Button&, void) +{ + Apply(); + m_xDialog->response(RET_OK); +} + +/************************************************************************* +|* +|* Click on Apply-button +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHpLinkDlg, ClickApplyHdl_Impl, weld::Button&, void) +{ + Apply(); +} + +/************************************************************************* +|* +|* Set Page +|* +|************************************************************************/ +void SvxHpLinkDlg::SetPage ( SvxHyperlinkItem const * pItem ) +{ + OUString sPageId("internet"); + + OUString aStrURL(pItem->GetURL()); + INetURLObject aURL(aStrURL); + INetProtocol eProtocolTyp = aURL.GetProtocol(); + + switch ( eProtocolTyp ) + { + case INetProtocol::Http : + case INetProtocol::Ftp : + sPageId = "internet"; + break; + case INetProtocol::File : + sPageId = "document"; + break; + case INetProtocol::Mailto : + sPageId = "mail"; + break; + default : + if (aStrURL.startsWith("#")) + sPageId = "document"; + else + { + // not valid + sPageId = GetCurPageId(); + } + break; + } + + ShowPage (sPageId); + + SvxHyperlinkTabPageBase* pCurrentPage = static_cast(GetTabPage( sPageId )); + + mbIsHTMLDoc = (pItem->GetInsertMode() & HLINK_HTMLMODE) != 0; + + IconChoicePage* pPage = GetTabPage (sPageId); + if(pPage) + { + SfxItemSet& aPageSet = const_cast(pPage->GetItemSet ()); + aPageSet.Put ( *pItem ); + + pCurrentPage->Reset( aPageSet ); + } +} + +/************************************************************************* +|* +|* Enable/Disable ReadOnly mode +|* +|************************************************************************/ +void SvxHpLinkDlg::SetReadOnlyMode( bool bRdOnly ) +{ + m_xOKBtn->set_sensitive(!bRdOnly); +} + +/************************************************************************* +|* +|* late-initialization of newly created pages +|* +|************************************************************************/ +void SvxHpLinkDlg::PageCreated(IconChoicePage& rPage) +{ + SvxHyperlinkTabPageBase& rHyperlinkPage = dynamic_cast< SvxHyperlinkTabPageBase& >( rPage ); + Reference< XFrame > xDocumentFrame = GetBindings().GetActiveFrame(); + OSL_ENSURE( xDocumentFrame.is(), "SvxHpLinkDlg::PageCreated: macro assignment functionality won't work with a proper frame!" ); + rHyperlinkPage.SetDocumentFrame( xDocumentFrame ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/cuiimapwnd.cxx b/cui/source/dialogs/cuiimapwnd.cxx new file mode 100644 index 0000000000..d613e1a804 --- /dev/null +++ b/cui/source/dialogs/cuiimapwnd.cxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +/************************************************************************* +|* +|* URLDlg +|* +\************************************************************************/ + +URLDlg::URLDlg(weld::Widget* pWindow, const OUString& rURL, const OUString& rAlternativeText, + const OUString& rDescription, const OUString& rTarget, const OUString& rName, + TargetList& rTargetList) + : GenericDialogController(pWindow, "cui/ui/cuiimapdlg.ui", "IMapDialog") + , m_xEdtURL(m_xBuilder->weld_entry("urlentry")) + , m_xCbbTargets(m_xBuilder->weld_combo_box("frameCB")) + , m_xEdtName(m_xBuilder->weld_entry("nameentry")) + , m_xEdtAlternativeText(m_xBuilder->weld_entry("textentry")) + , m_xEdtDescription(m_xBuilder->weld_text_view("descTV")) +{ + m_xEdtDescription->set_size_request(m_xEdtDescription->get_approximate_digit_width() * 51, + m_xEdtDescription->get_height_rows(5)); + + m_xEdtURL->set_text(rURL); + m_xEdtAlternativeText->set_text(rAlternativeText); + m_xEdtDescription->set_text(rDescription); + m_xEdtName->set_text(rName); + + for (const OUString& a : rTargetList) + m_xCbbTargets->append_text(a); + + if (rTarget.isEmpty()) + m_xCbbTargets->set_entry_text("_self"); + else + m_xCbbTargets->set_entry_text(rTarget); +} + +URLDlg::~URLDlg() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/cuitbxform.cxx b/cui/source/dialogs/cuitbxform.cxx new file mode 100644 index 0000000000..55d21325d5 --- /dev/null +++ b/cui/source/dialogs/cuitbxform.cxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +FmInputRecordNoDialog::FmInputRecordNoDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/recordnumberdialog.ui", "RecordNumberDialog") + , m_xRecordNo(m_xBuilder->weld_spin_button("entry")) +{ + m_xRecordNo->set_range(1, 0x7FFFFFFF); +} + +FmInputRecordNoDialog::~FmInputRecordNoDialog() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/dlgname.cxx b/cui/source/dialogs/dlgname.cxx new file mode 100644 index 0000000000..09b6158fde --- /dev/null +++ b/cui/source/dialogs/dlgname.cxx @@ -0,0 +1,284 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +/************************************************************************* +|* +|* Dialog for editing a name +|* +\************************************************************************/ + +SvxNameDialog::SvxNameDialog(weld::Window* pParent, const OUString& rName, const OUString& rDesc, + const OUString& rTitle) + : GenericDialogController(pParent, "cui/ui/namedialog.ui", "NameDialog") + , m_xEdtName(m_xBuilder->weld_entry("name_entry")) + , m_xFtDescription(m_xBuilder->weld_label("description_label")) + , m_xBtnOK(m_xBuilder->weld_button("ok")) +{ + m_xFtDescription->set_label(rDesc); + m_xEdtName->set_text(rName); + m_xEdtName->select_region(0, -1); + ModifyHdl(*m_xEdtName); + m_xEdtName->connect_changed(LINK(this, SvxNameDialog, ModifyHdl)); + if (!rTitle.isEmpty()) + set_title(rTitle); +} + +IMPL_LINK_NOARG(SvxNameDialog, ModifyHdl, weld::Entry&, void) +{ + // Do not allow empty names, unless custom CheckNameHdl is specified + bool bEnable; + if (m_aCheckNameHdl.IsSet()) + bEnable = m_aCheckNameHdl.Call(*this); + else + bEnable = !m_xEdtName->get_text().isEmpty(); + m_xBtnOK->set_sensitive(bEnable); + // tdf#129032: feedback on reason to disabled controls + m_xEdtName->set_message_type(bEnable ? weld::EntryMessageType::Normal + : weld::EntryMessageType::Error); + OUString rTip = ""; + if (!bEnable && m_aCheckNameTooltipHdl.IsSet()) + rTip = m_aCheckNameTooltipHdl.Call(*this); + m_xBtnOK->set_tooltip_text(rTip); + m_xEdtName->set_tooltip_text(rTip); +} + +SvxNumberDialog::SvxNumberDialog(weld::Window* pParent, const OUString& rDesc, sal_Int64 nValue, + sal_Int64 nMin, sal_Int64 nMax) + : GenericDialogController(pParent, "cui/ui/numberdialog.ui", "NumberDialog") + , m_xEdtNumber(m_xBuilder->weld_spin_button("number_spinbtn")) + , m_xFtDescription(m_xBuilder->weld_label("description_label")) +{ + m_xFtDescription->set_label(rDesc); + m_xEdtNumber->set_min(nMin); + m_xEdtNumber->set_max(nMax); + m_xEdtNumber->set_value(nValue); +} + +SvxDecimalNumberDialog::SvxDecimalNumberDialog(weld::Window* pParent, const OUString& rDesc, + double fValue) + : GenericDialogController(pParent, "cui/ui/numberdialog.ui", "NumberDialog") + , m_xEdtNumber(m_xBuilder->weld_formatted_spin_button("number_spinbtn")) + , m_xFtDescription(m_xBuilder->weld_label("description_label")) +{ + m_xFtDescription->set_label(rDesc); + m_xEdtNumber->GetFormatter().SetValue(fValue); +} + +// #i68101# +// Dialog for editing Object Name +// plus uniqueness-callback-linkHandler + +SvxObjectNameDialog::SvxObjectNameDialog(weld::Window* pParent, const OUString& rName) + : GenericDialogController(pParent, "cui/ui/objectnamedialog.ui", "ObjectNameDialog") + , m_xEdtName(m_xBuilder->weld_entry("object_name_entry")) + , m_xBtnOK(m_xBuilder->weld_button("ok")) +{ + // set name + m_xEdtName->set_text(rName); + m_xEdtName->select_region(0, -1); + + // activate name + ModifyHdl(*m_xEdtName); + m_xEdtName->connect_changed(LINK(this, SvxObjectNameDialog, ModifyHdl)); +} + +IMPL_LINK_NOARG(SvxObjectNameDialog, ModifyHdl, weld::Entry&, void) +{ + if (aCheckNameHdl.IsSet()) + { + m_xBtnOK->set_sensitive(aCheckNameHdl.Call(*this)); + } +} + +// #i68101# +// Dialog for editing Object Title and Description + +SvxObjectTitleDescDialog::SvxObjectTitleDescDialog(weld::Window* pParent, const OUString& rTitle, + const OUString& rDescription, + bool const isDecorative) + : GenericDialogController(pParent, "cui/ui/objecttitledescdialog.ui", "ObjectTitleDescDialog") + , m_xTitleFT(m_xBuilder->weld_label("object_title_label")) + , m_xEdtTitle(m_xBuilder->weld_entry("object_title_entry")) + , m_xDescriptionFT(m_xBuilder->weld_label("desc_label")) + , m_xEdtDescription(m_xBuilder->weld_text_view("desc_entry")) + , m_xDecorativeCB(m_xBuilder->weld_check_button("decorative")) +{ + //lock height to initial height + m_xEdtDescription->set_size_request(-1, m_xEdtDescription->get_text_height() * 5); + // set title & desc + m_xEdtTitle->set_text(rTitle); + m_xEdtDescription->set_text(rDescription); + + // activate title + m_xEdtTitle->select_region(0, -1); + + m_xDecorativeCB->set_active(isDecorative); + m_xDecorativeCB->connect_toggled(LINK(this, SvxObjectTitleDescDialog, DecorativeHdl)); + DecorativeHdl(*m_xDecorativeCB); +} + +IMPL_LINK_NOARG(SvxObjectTitleDescDialog, DecorativeHdl, weld::Toggleable&, void) +{ + bool const bEnable(!m_xDecorativeCB->get_active()); + m_xEdtTitle->set_sensitive(bEnable); + m_xTitleFT->set_sensitive(bEnable); + m_xEdtDescription->set_sensitive(bEnable); + m_xDescriptionFT->set_sensitive(bEnable); +} + +SvxListDialog::SvxListDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/listdialog.ui", "ListDialog") + , m_aMode(ListMode::String) + , m_xList(m_xBuilder->weld_tree_view("assignlist")) + , m_xAddBtn(m_xBuilder->weld_button("addbtn")) + , m_xRemoveBtn(m_xBuilder->weld_button("removebtn")) + , m_xEditBtn(m_xBuilder->weld_button("editbtn")) +{ + m_xList->set_size_request(m_xList->get_approximate_digit_width() * 54, + m_xList->get_height_rows(6)); + m_xAddBtn->connect_clicked(LINK(this, SvxListDialog, AddHdl_Impl)); + m_xRemoveBtn->connect_clicked(LINK(this, SvxListDialog, RemoveHdl_Impl)); + m_xEditBtn->connect_clicked(LINK(this, SvxListDialog, EditHdl_Impl)); + m_xList->connect_changed(LINK(this, SvxListDialog, SelectHdl_Impl)); + m_xList->connect_row_activated(LINK(this, SvxListDialog, DblClickHdl_Impl)); + + SelectionChanged(); +} + +SvxListDialog::~SvxListDialog() {} + +IMPL_LINK_NOARG(SvxListDialog, AddHdl_Impl, weld::Button&, void) +{ + SvxNameDialog aNameDlg(m_xDialog.get(), "", ""); + + if (!aNameDlg.run()) + return; + OUString sNewText = comphelper::string::strip(aNameDlg.GetName(), ' '); + if (!sNewText.isEmpty()) + { + m_xList->insert_text(-1, sNewText); + m_xList->select(-1); + } +} + +IMPL_LINK_NOARG(SvxListDialog, EditHdl_Impl, weld::Button&, void) { EditEntry(); } + +IMPL_LINK_NOARG(SvxListDialog, SelectHdl_Impl, weld::TreeView&, void) { SelectionChanged(); } + +IMPL_LINK_NOARG(SvxListDialog, DblClickHdl_Impl, weld::TreeView&, bool) +{ + EditEntry(); + return true; +} + +IMPL_LINK_NOARG(SvxListDialog, RemoveHdl_Impl, weld::Button&, void) +{ + int nPos = m_xList->get_selected_index(); + if (nPos == -1) + return; + m_xList->remove(nPos); + int nCount = m_xList->n_children(); + if (nCount) + { + if (nPos >= nCount) + nPos = nCount - 1; + m_xList->select(nPos); + } + SelectionChanged(); +} + +void SvxListDialog::SelectionChanged() +{ + bool bEnable = m_xList->get_selected_index() != -1; + m_xRemoveBtn->set_sensitive(bEnable); + m_xEditBtn->set_sensitive(bEnable); +} + +std::vector SvxListDialog::GetEntries() +{ + int nCount = m_xList->n_children(); + std::vector aList; + aList.reserve(nCount); + for (int i = 0; i < nCount; ++i) + aList.push_back(m_xList->get_text(i)); + return aList; +} + +void SvxListDialog::SetEntries(std::vector const& rEntries) +{ + m_xList->clear(); + for (auto const& sEntry : rEntries) + { + m_xList->append_text(sEntry); + } + SelectionChanged(); +} + +void SvxListDialog::EditEntry() +{ + int nPos = m_xList->get_selected_index(); + if (nPos == -1) + return; + + OUString sOldText(m_xList->get_selected_text()); + OUString sNewText; + + if (m_aMode == ListMode::String) + { + SvxNameDialog aNameDlg(m_xDialog.get(), sOldText, ""); + if (!aNameDlg.run()) + return; + sNewText = comphelper::string::strip(aNameDlg.GetName(), ' '); + } + else if (m_aMode == ListMode::Int16 || m_aMode == ListMode::Int32 || m_aMode == ListMode::Int64) + { + sal_Int64 nMin = m_aMode == ListMode::Int16 + ? SAL_MIN_INT16 + : m_aMode == ListMode::Int32 ? SAL_MIN_INT32 : SAL_MIN_INT64; + sal_Int64 nMax = m_aMode == ListMode::Int16 + ? SAL_MAX_INT16 + : m_aMode == ListMode::Int32 ? SAL_MAX_INT32 : SAL_MAX_INT64; + SvxNumberDialog aNumberDlg(m_xDialog.get(), "", sOldText.toInt64(), nMin, nMax); + if (!aNumberDlg.run()) + return; + sNewText = OUString::number(aNumberDlg.GetNumber()); + } + else if (m_aMode == ListMode::Double) + { + SvxDecimalNumberDialog aNumberDlg(m_xDialog.get(), "", sOldText.toDouble()); + if (!aNumberDlg.run()) + return; + sNewText = OUString::number(aNumberDlg.GetNumber()); + } + + if (!sNewText.isEmpty() && sNewText != sOldText) + { + m_xList->remove(nPos); + m_xList->insert_text(nPos, sNewText); + m_xList->select(nPos); + } +} + +void SvxListDialog::SetMode(ListMode aMode) { m_aMode = aMode; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/fileextcheckdlg.cxx b/cui/source/dialogs/fileextcheckdlg.cxx new file mode 100644 index 0000000000..732f836743 --- /dev/null +++ b/cui/source/dialogs/fileextcheckdlg.cxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include + +FileExtCheckDialog::FileExtCheckDialog(weld::Window* pParent, const OUString& sTitle, + const OUString& sMsg) + : GenericDialogController(pParent, "cui/ui/fileextcheckdialog.ui", "FileExtCheckDialog") + , m_pText(m_xBuilder->weld_label("lbText")) + , m_pPerformCheck(m_xBuilder->weld_check_button("cbPerformCheck")) + , m_pOk(m_xBuilder->weld_button("btnOk")) +{ + m_pPerformCheck->set_active(true); + m_pOk->connect_clicked(LINK(this, FileExtCheckDialog, OnOkClick)); + m_xDialog->set_title(sTitle); + m_pText->set_label(sMsg); +} + +FileExtCheckDialog::~FileExtCheckDialog() +{ + std::shared_ptr xChanges( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Misc::PerformFileExtCheck::set(m_pPerformCheck->get_active(), + xChanges); + xChanges->commit(); +} + +IMPL_LINK_NOARG(FileExtCheckDialog, OnOkClick, weld::Button&, void) +{ + vcl::fileregistration::LaunchRegistrationUI(); + FileExtCheckDialog::response(RET_OK); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/cui/source/dialogs/hangulhanjadlg.cxx b/cui/source/dialogs/hangulhanjadlg.cxx new file mode 100644 index 0000000000..fb25df938e --- /dev/null +++ b/cui/source/dialogs/hangulhanjadlg.cxx @@ -0,0 +1,1508 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define HHC editeng::HangulHanjaConversion +#define LINE_CNT static_cast< sal_uInt16 >(2) +#define MAXNUM_SUGGESTIONS 50 + + +namespace svx +{ + + using namespace ::com::sun::star; + using namespace css::uno; + using namespace css::linguistic2; + using namespace css::lang; + using namespace css::container; + + + namespace + { + class FontSwitch + { + private: + OutputDevice& m_rDev; + + public: + FontSwitch( OutputDevice& _rDev, const vcl::Font& _rTemporaryFont ) + :m_rDev( _rDev ) + { + m_rDev.Push( vcl::PushFlags::FONT ); + m_rDev.SetFont( _rTemporaryFont ); + } + ~FontSwitch() COVERITY_NOEXCEPT_FALSE + { + m_rDev.Pop(); + } + }; + + /** a class which allows to draw two texts in a pseudo-ruby way (which basically + means one text above or below the other, and a little bit smaller) + */ + class PseudoRubyText + { + public: + enum RubyPosition + { + eAbove, eBelow + }; + + protected: + OUString m_sPrimaryText; + OUString m_sSecondaryText; + RubyPosition m_ePosition; + + public: + PseudoRubyText(); + void init( const OUString& rPrimaryText, const OUString& rSecondaryText, const RubyPosition& rPosition ); + const OUString& getPrimaryText() const { return m_sPrimaryText; } + const OUString& getSecondaryText() const { return m_sSecondaryText; } + + public: + void Paint( vcl::RenderContext& _rDevice, const ::tools::Rectangle& _rRect, + ::tools::Rectangle* _pPrimaryLocation, ::tools::Rectangle* _pSecondaryLocation ); + }; + + } + + PseudoRubyText::PseudoRubyText() + : m_ePosition(eAbove) + { + } + + void PseudoRubyText::init( const OUString& rPrimaryText, const OUString& rSecondaryText, const RubyPosition& rPosition ) + { + m_sPrimaryText = rPrimaryText; + m_sSecondaryText = rSecondaryText; + m_ePosition = rPosition; + } + + + void PseudoRubyText::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& _rRect, + ::tools::Rectangle* _pPrimaryLocation, ::tools::Rectangle* _pSecondaryLocation ) + { + // calculate the text flags for the painting + constexpr DrawTextFlags nTextStyle = DrawTextFlags::Mnemonic | + DrawTextFlags::Left | + DrawTextFlags::VCenter; + + Size aPlaygroundSize(_rRect.GetSize()); + + // the font for the secondary text: + vcl::Font aSmallerFont(rRenderContext.GetFont()); + // heuristic: 80% of the original size + aSmallerFont.SetFontHeight( static_cast( 0.8 * aSmallerFont.GetFontHeight() ) ); + + // let's calculate the size of our two texts + ::tools::Rectangle aPrimaryRect = rRenderContext.GetTextRect( _rRect, m_sPrimaryText, nTextStyle ); + ::tools::Rectangle aSecondaryRect; + { + FontSwitch aFontRestore(rRenderContext, aSmallerFont); + aSecondaryRect = rRenderContext.GetTextRect(_rRect, m_sSecondaryText, nTextStyle); + } + + // position these rectangles properly + // x-axis: + sal_Int32 nCombinedWidth = std::max( aSecondaryRect.GetWidth(), aPrimaryRect.GetWidth() ); + // the rectangle where both texts will reside is as high as possible, and as wide as the + // widest of both text rects + aPrimaryRect.SetLeft( _rRect.Left() ); + aSecondaryRect.SetLeft( aPrimaryRect.Left() ); + aPrimaryRect.SetRight( _rRect.Left() + nCombinedWidth ); + aSecondaryRect.SetRight( aPrimaryRect.Right() ); + + // y-axis: + sal_Int32 nCombinedHeight = aPrimaryRect.GetHeight() + aSecondaryRect.GetHeight(); + // align to the top, for the moment + aPrimaryRect.Move( 0, _rRect.Top() - aPrimaryRect.Top() ); + aSecondaryRect.Move( 0, aPrimaryRect.Top() + aPrimaryRect.GetHeight() - aSecondaryRect.Top() ); + // move the rects to the bottom + aPrimaryRect.Move( 0, ( aPlaygroundSize.Height() - nCombinedHeight ) / 2 ); + aSecondaryRect.Move( 0, ( aPlaygroundSize.Height() - nCombinedHeight ) / 2 ); + + // 'til here, everything we did assumes that the secondary text is painted _below_ the primary + // text. If this isn't the case, we need to correct the rectangles + if (eAbove == m_ePosition) + { + sal_Int32 nVertDistance = aSecondaryRect.Top() - aPrimaryRect.Top(); + aSecondaryRect.Move( 0, -nVertDistance ); + aPrimaryRect.Move( 0, nCombinedHeight - nVertDistance ); + } + + // now draw the texts + // as we already calculated the precise rectangles for the texts, we don't want to + // use the alignment flags given - within its rect, every text is centered + DrawTextFlags nDrawTextStyle( nTextStyle ); + nDrawTextStyle &= ~DrawTextFlags( DrawTextFlags::Right | DrawTextFlags::Left | DrawTextFlags::Bottom | DrawTextFlags::Top ); + nDrawTextStyle |= DrawTextFlags::Center | DrawTextFlags::VCenter; + + rRenderContext.DrawText( aPrimaryRect, m_sPrimaryText, nDrawTextStyle ); + { + FontSwitch aFontRestore(rRenderContext, aSmallerFont); + rRenderContext.DrawText( aSecondaryRect, m_sSecondaryText, nDrawTextStyle ); + } + + // outta here + if (_pPrimaryLocation) + *_pPrimaryLocation = aPrimaryRect; + if (_pSecondaryLocation) + *_pSecondaryLocation = aSecondaryRect; + } + + class RubyRadioButton + { + public: + RubyRadioButton(std::unique_ptr xControl, std::unique_ptr xImage); + void init(const OUString& rPrimaryText, const OUString& rSecondaryText, const PseudoRubyText::RubyPosition& rPosition); + + void set_sensitive(bool sensitive) + { + m_xControl->set_sensitive(sensitive); + m_xImage->set_sensitive(sensitive); + } + void set_active(bool active) { m_xControl->set_active(active); } + bool get_active() const { return m_xControl->get_active(); } + + void connect_toggled(const Link& rLink) { m_xControl->connect_toggled(rLink); } + + private: + Size GetOptimalSize() const; + void Paint(vcl::RenderContext& rRenderContext); + + ScopedVclPtr m_xVirDev; + std::unique_ptr m_xControl; + std::unique_ptr m_xImage; + PseudoRubyText m_aRubyText; + }; + + RubyRadioButton::RubyRadioButton(std::unique_ptr xControl, std::unique_ptr xImage) + : m_xVirDev(xControl->create_virtual_device()) + , m_xControl(std::move(xControl)) + , m_xImage(std::move(xImage)) + { + // expand the point size of the desired font to the equivalent pixel size + weld::SetPointFont(*m_xVirDev, m_xControl->get_font()); + } + + void RubyRadioButton::init( const OUString& rPrimaryText, const OUString& rSecondaryText, const PseudoRubyText::RubyPosition& rPosition ) + { + m_aRubyText.init(rPrimaryText, rSecondaryText, rPosition); + + m_xVirDev->SetOutputSizePixel(GetOptimalSize()); + + Paint(*m_xVirDev); + + m_xImage->set_image(m_xVirDev.get()); + } + + void RubyRadioButton::Paint(vcl::RenderContext& rRenderContext) + { + ::tools::Rectangle aOverallRect(Point(0, 0), rRenderContext.GetOutputSizePixel()); + // inflate the rect a little bit (because the VCL radio button does the same) + ::tools::Rectangle aTextRect( aOverallRect ); + aTextRect.AdjustLeft( 1 ); aTextRect.AdjustRight( -1 ); + aTextRect.AdjustTop( 1 ); aTextRect.AdjustBottom( -1 ); + + // paint the ruby text + ::tools::Rectangle aPrimaryTextLocation; + ::tools::Rectangle aSecondaryTextLocation; + + m_aRubyText.Paint(rRenderContext, aTextRect, &aPrimaryTextLocation, &aSecondaryTextLocation); + } + + Size RubyRadioButton::GetOptimalSize() const + { + vcl::Font aSmallerFont(m_xVirDev->GetFont()); + aSmallerFont.SetFontHeight( static_cast( 0.8 * aSmallerFont.GetFontHeight() ) ); + ::tools::Rectangle rect( Point(), Size( SAL_MAX_INT32, SAL_MAX_INT32 ) ); + + Size aPrimarySize = m_xVirDev->GetTextRect( rect, m_aRubyText.getPrimaryText() ).GetSize(); + Size aSecondarySize; + { + FontSwitch aFontRestore(*m_xVirDev, aSmallerFont); + aSecondarySize = m_xVirDev->GetTextRect( rect, m_aRubyText.getSecondaryText() ).GetSize(); + } + + Size minimumSize; + minimumSize.setHeight( aPrimarySize.Height() + aSecondarySize.Height() + 5 ); + minimumSize.setWidth(std::max(aPrimarySize.Width(), aSecondarySize.Width()) + 5 ); + return minimumSize; + } + + SuggestionSet::SuggestionSet(std::unique_ptr xScrolledWindow) + : ValueSet(std::move(xScrolledWindow)) + + { + } + + void SuggestionSet::UserDraw( const UserDrawEvent& rUDEvt ) + { + vcl::RenderContext* pDev = rUDEvt.GetRenderContext(); + ::tools::Rectangle aRect = rUDEvt.GetRect(); + sal_uInt16 nItemId = rUDEvt.GetItemId(); + + OUString sText = *static_cast< OUString* >( GetItemData( nItemId ) ); + pDev->DrawText( aRect, sText, DrawTextFlags::Center | DrawTextFlags::VCenter ); + } + + SuggestionDisplay::SuggestionDisplay(weld::Builder& rBuilder) + : m_bDisplayListBox( true ) + , m_bInSelectionUpdate( false ) + , m_xValueSet(new SuggestionSet(rBuilder.weld_scrolled_window("scrollwin", true))) + , m_xValueSetWin(new weld::CustomWeld(rBuilder, "valueset", *m_xValueSet)) + , m_xListBox(rBuilder.weld_tree_view("listbox")) + { + m_xValueSet->SetSelectHdl( LINK( this, SuggestionDisplay, SelectSuggestionValueSetHdl ) ); + m_xListBox->connect_changed( LINK( this, SuggestionDisplay, SelectSuggestionListBoxHdl ) ); + + m_xValueSet->SetLineCount( LINE_CNT ); + m_xValueSet->SetStyle( m_xValueSet->GetStyle() | WB_ITEMBORDER | WB_VSCROLL ); + + auto nItemWidth = 2 * m_xListBox->get_pixel_size("AU").Width(); + m_xValueSet->SetItemWidth( nItemWidth ); + + Size aSize(m_xListBox->get_approximate_digit_width() * 42, m_xListBox->get_text_height() * 5); + m_xValueSet->set_size_request(aSize.Width(), aSize.Height()); + m_xListBox->set_size_request(aSize.Width(), aSize.Height()); + + implUpdateDisplay(); + } + + void SuggestionDisplay::implUpdateDisplay() + { + m_xListBox->set_visible(m_bDisplayListBox); + if (!m_bDisplayListBox) + m_xValueSetWin->show(); + else + m_xValueSetWin->hide(); + } + + weld::Widget& SuggestionDisplay::implGetCurrentControl() + { + if (m_bDisplayListBox) + return *m_xListBox; + return *m_xValueSet->GetDrawingArea(); + } + + void SuggestionDisplay::DisplayListBox( bool bDisplayListBox ) + { + if( m_bDisplayListBox == bDisplayListBox ) + return; + + weld::Widget& rOldControl = implGetCurrentControl(); + bool bHasFocus = rOldControl.has_focus(); + + m_bDisplayListBox = bDisplayListBox; + + if( bHasFocus ) + { + weld::Widget& rNewControl = implGetCurrentControl(); + rNewControl.grab_focus(); + } + + implUpdateDisplay(); + } + + IMPL_LINK_NOARG(SuggestionDisplay, SelectSuggestionValueSetHdl, ValueSet*, void) + { + SelectSuggestionHdl(false); + } + + IMPL_LINK_NOARG(SuggestionDisplay, SelectSuggestionListBoxHdl, weld::TreeView&, void) + { + SelectSuggestionHdl(true); + } + + void SuggestionDisplay::SelectSuggestionHdl(bool bListBox) + { + if( m_bInSelectionUpdate ) + return; + + m_bInSelectionUpdate = true; + if (bListBox) + { + sal_uInt16 nPos = m_xListBox->get_selected_index(); + m_xValueSet->SelectItem( nPos+1 ); //itemid == pos+1 (id 0 has special meaning) + } + else + { + sal_uInt16 nPos = m_xValueSet->GetSelectedItemId()-1; //itemid == pos+1 (id 0 has special meaning) + m_xListBox->select(nPos); + } + m_bInSelectionUpdate = false; + m_aSelectLink.Call( *this ); + } + + void SuggestionDisplay::SetSelectHdl( const Link& rLink ) + { + m_aSelectLink = rLink; + } + + void SuggestionDisplay::Clear() + { + m_xListBox->clear(); + m_xValueSet->Clear(); + } + + void SuggestionDisplay::InsertEntry( const OUString& rStr ) + { + m_xListBox->append_text(rStr); + sal_uInt16 nItemId = m_xListBox->n_children(); //itemid == pos+1 (id 0 has special meaning) + m_xValueSet->InsertItem( nItemId ); + OUString* pItemData = new OUString( rStr ); + m_xValueSet->SetItemData( nItemId, pItemData ); + } + + void SuggestionDisplay::SelectEntryPos( sal_uInt16 nPos ) + { + m_xListBox->select(nPos); + m_xValueSet->SelectItem( nPos+1 ); //itemid == pos+1 (id 0 has special meaning) + } + + sal_uInt16 SuggestionDisplay::GetEntryCount() const + { + return m_xListBox->n_children(); + } + + OUString SuggestionDisplay::GetEntry( sal_uInt16 nPos ) const + { + return m_xListBox->get_text( nPos ); + } + + OUString SuggestionDisplay::GetSelectedEntry() const + { + return m_xListBox->get_selected_text(); + } + + void SuggestionDisplay::SetHelpIds() + { + m_xValueSet->SetHelpId(HID_HANGULDLG_SUGGESTIONS_GRID); + m_xListBox->set_help_id(HID_HANGULDLG_SUGGESTIONS_LIST); + } + + HangulHanjaConversionDialog::HangulHanjaConversionDialog(weld::Widget* pParent) + : GenericDialogController(pParent, "cui/ui/hangulhanjaconversiondialog.ui", "HangulHanjaConversionDialog") + , m_bDocumentMode( true ) + , m_xFind(m_xBuilder->weld_button("find")) + , m_xIgnore(m_xBuilder->weld_button("ignore")) + , m_xIgnoreAll(m_xBuilder->weld_button("ignoreall")) + , m_xReplace(m_xBuilder->weld_button("replace")) + , m_xReplaceAll(m_xBuilder->weld_button("replaceall")) + , m_xOptions(m_xBuilder->weld_button("options")) + , m_xSuggestions(new SuggestionDisplay(*m_xBuilder)) + , m_xSimpleConversion(m_xBuilder->weld_radio_button("simpleconversion")) + , m_xHangulBracketed(m_xBuilder->weld_radio_button("hangulbracket")) + , m_xHanjaBracketed(m_xBuilder->weld_radio_button("hanjabracket")) + , m_xWordInput(m_xBuilder->weld_entry("wordinput")) + , m_xOriginalWord(m_xBuilder->weld_label("originalword")) + , m_xHanjaAbove(new RubyRadioButton(m_xBuilder->weld_radio_button("hanja_above"), + m_xBuilder->weld_image("hanja_above_img"))) + , m_xHanjaBelow(new RubyRadioButton(m_xBuilder->weld_radio_button("hanja_below"), + m_xBuilder->weld_image("hanja_below_img"))) + , m_xHangulAbove(new RubyRadioButton(m_xBuilder->weld_radio_button("hangul_above"), + m_xBuilder->weld_image("hangul_above_img"))) + , m_xHangulBelow(new RubyRadioButton(m_xBuilder->weld_radio_button("hangul_below"), + m_xBuilder->weld_image("hangul_below_img"))) + , m_xHangulOnly(m_xBuilder->weld_check_button("hangulonly")) + , m_xHanjaOnly(m_xBuilder->weld_check_button("hanjaonly")) + , m_xReplaceByChar(m_xBuilder->weld_check_button("replacebychar")) + { + m_xSuggestions->set_size_request(m_xOriginalWord->get_approximate_digit_width() * 42, + m_xOriginalWord->get_text_height() * 5); + + const OUString sHangul(CuiResId(RID_CUISTR_HANGUL)); + const OUString sHanja(CuiResId(RID_CUISTR_HANJA)); + m_xHanjaAbove->init( sHangul, sHanja, PseudoRubyText::eAbove ); + m_xHanjaBelow->init( sHangul, sHanja, PseudoRubyText::eBelow ); + m_xHangulAbove->init( sHanja, sHangul, PseudoRubyText::eAbove ); + m_xHangulBelow->init( sHanja, sHangul, PseudoRubyText::eBelow ); + + m_xWordInput->connect_changed( LINK( this, HangulHanjaConversionDialog, OnSuggestionModified ) ); + m_xSuggestions->SetSelectHdl( LINK( this, HangulHanjaConversionDialog, OnSuggestionSelected ) ); + m_xReplaceByChar->connect_toggled( LINK( this, HangulHanjaConversionDialog, ClickByCharacterHdl ) ); + m_xHangulOnly->connect_toggled( LINK( this, HangulHanjaConversionDialog, OnConversionDirectionClicked ) ); + m_xHanjaOnly->connect_toggled( LINK( this, HangulHanjaConversionDialog, OnConversionDirectionClicked ) ); + m_xOptions->connect_clicked(LINK(this, HangulHanjaConversionDialog, OnOption)); + + // initial focus + FocusSuggestion( ); + + // initial control values + m_xSimpleConversion->set_active(true); + + m_xSuggestions->SetHelpIds(); + } + + HangulHanjaConversionDialog::~HangulHanjaConversionDialog() + { + } + + void HangulHanjaConversionDialog::FillSuggestions( const css::uno::Sequence< OUString >& _rSuggestions ) + { + m_xSuggestions->Clear(); + for ( auto const & suggestion : _rSuggestions ) + m_xSuggestions->InsertEntry( suggestion ); + + // select the first suggestion, and fill in the suggestion edit field + OUString sFirstSuggestion; + if ( m_xSuggestions->GetEntryCount() ) + { + sFirstSuggestion = m_xSuggestions->GetEntry( 0 ); + m_xSuggestions->SelectEntryPos( 0 ); + } + m_xWordInput->set_text( sFirstSuggestion ); + m_xWordInput->save_value(); + OnSuggestionModified( *m_xWordInput ); + } + + void HangulHanjaConversionDialog::SetOptionsChangedHdl(const Link& rHdl) + { + m_aOptionsChangedLink = rHdl; + } + + void HangulHanjaConversionDialog::SetIgnoreHdl(const Link& rHdl) + { + m_xIgnore->connect_clicked(rHdl); + } + + void HangulHanjaConversionDialog::SetIgnoreAllHdl(const Link& rHdl) + { + m_xIgnoreAll->connect_clicked(rHdl); + } + + void HangulHanjaConversionDialog::SetChangeHdl(const Link& rHdl ) + { + m_xReplace->connect_clicked(rHdl); + } + + void HangulHanjaConversionDialog::SetChangeAllHdl(const Link& rHdl) + { + m_xReplaceAll->connect_clicked(rHdl); + } + + void HangulHanjaConversionDialog::SetFindHdl(const Link& rHdl) + { + m_xFind->connect_clicked(rHdl); + } + + void HangulHanjaConversionDialog::SetConversionFormatChangedHdl( const Link& rHdl ) + { + m_xSimpleConversion->connect_toggled( rHdl ); + m_xHangulBracketed->connect_toggled( rHdl ); + m_xHanjaBracketed->connect_toggled( rHdl ); + m_xHanjaAbove->connect_toggled( rHdl ); + m_xHanjaBelow->connect_toggled( rHdl ); + m_xHangulAbove->connect_toggled( rHdl ); + m_xHangulBelow->connect_toggled( rHdl ); + } + + void HangulHanjaConversionDialog::SetClickByCharacterHdl( const Link& _rHdl ) + { + m_aClickByCharacterLink = _rHdl; + } + + IMPL_LINK_NOARG( HangulHanjaConversionDialog, OnSuggestionSelected, SuggestionDisplay&, void ) + { + m_xWordInput->set_text(m_xSuggestions->GetSelectedEntry()); + OnSuggestionModified( *m_xWordInput ); + } + + IMPL_LINK_NOARG( HangulHanjaConversionDialog, OnSuggestionModified, weld::Entry&, void ) + { + m_xFind->set_sensitive(m_xWordInput->get_value_changed_from_saved()); + + bool bSameLen = m_xWordInput->get_text().getLength() == m_xOriginalWord->get_label().getLength(); + m_xReplace->set_sensitive( m_bDocumentMode && bSameLen ); + m_xReplaceAll->set_sensitive( m_bDocumentMode && bSameLen ); + } + + IMPL_LINK(HangulHanjaConversionDialog, ClickByCharacterHdl, weld::Toggleable&, rBox, void) + { + m_aClickByCharacterLink.Call(rBox); + bool bByCharacter = rBox.get_active(); + m_xSuggestions->DisplayListBox( !bByCharacter ); + } + + IMPL_LINK(HangulHanjaConversionDialog, OnConversionDirectionClicked, weld::Toggleable&, rBox, void) + { + weld::CheckButton* pOtherBox = nullptr; + if (&rBox == m_xHangulOnly.get()) + pOtherBox = m_xHanjaOnly.get(); + else + pOtherBox = m_xHangulOnly.get(); + bool bBoxChecked = rBox.get_active(); + if (bBoxChecked) + pOtherBox->set_active(false); + pOtherBox->set_sensitive(!bBoxChecked); + } + + IMPL_LINK_NOARG(HangulHanjaConversionDialog, OnOption, weld::Button&, void) + { + HangulHanjaOptionsDialog aOptDlg(m_xDialog.get()); + aOptDlg.run(); + m_aOptionsChangedLink.Call( nullptr ); + } + + OUString HangulHanjaConversionDialog::GetCurrentString( ) const + { + return m_xOriginalWord->get_label(); + } + + void HangulHanjaConversionDialog::FocusSuggestion( ) + { + m_xWordInput->grab_focus(); + } + + void HangulHanjaConversionDialog::SetCurrentString( const OUString& _rNewString, + const Sequence< OUString >& _rSuggestions, bool _bOriginatesFromDocument ) + { + m_xOriginalWord->set_label(_rNewString); + + bool bOldDocumentMode = m_bDocumentMode; + m_bDocumentMode = _bOriginatesFromDocument; // before FillSuggestions! + FillSuggestions( _rSuggestions ); + + m_xIgnoreAll->set_sensitive( m_bDocumentMode ); + + // switch the def button depending if we're working for document text + if (bOldDocumentMode == m_bDocumentMode) + return; + + weld::Widget* pOldDefButton = nullptr; + weld::Widget* pNewDefButton = nullptr; + if (m_bDocumentMode) + { + pOldDefButton = m_xFind.get(); + pNewDefButton = m_xReplace.get(); + } + else + { + pOldDefButton = m_xReplace.get(); + pNewDefButton = m_xFind.get(); + } + + m_xDialog->change_default_widget(pOldDefButton, pNewDefButton); + } + + OUString HangulHanjaConversionDialog::GetCurrentSuggestion( ) const + { + return m_xWordInput->get_text(); + } + + void HangulHanjaConversionDialog::SetByCharacter( bool _bByCharacter ) + { + m_xReplaceByChar->set_active( _bByCharacter ); + m_xSuggestions->DisplayListBox( !_bByCharacter ); + } + + void HangulHanjaConversionDialog::SetConversionDirectionState( + bool _bTryBothDirections, + HHC::ConversionDirection ePrimaryConversionDirection ) + { + // default state: try both direction + m_xHangulOnly->set_active( false ); + m_xHangulOnly->set_sensitive(true); + m_xHanjaOnly->set_active( false ); + m_xHanjaOnly->set_sensitive(true); + + if (!_bTryBothDirections) + { + weld::CheckButton* pBox = ePrimaryConversionDirection == HHC::eHangulToHanja ? + m_xHangulOnly.get() : m_xHanjaOnly.get(); + pBox->set_active(true); + OnConversionDirectionClicked(*pBox); + } + } + + bool HangulHanjaConversionDialog::GetUseBothDirections( ) const + { + return !m_xHangulOnly->get_active() && !m_xHanjaOnly->get_active(); + } + + HHC::ConversionDirection HangulHanjaConversionDialog::GetDirection( + HHC::ConversionDirection eDefaultDirection ) const + { + HHC::ConversionDirection eDirection = eDefaultDirection; + if (m_xHangulOnly->get_active() && !m_xHanjaOnly->get_active()) + eDirection = HHC::eHangulToHanja; + else if (!m_xHangulOnly->get_active() && m_xHanjaOnly->get_active()) + eDirection = HHC::eHanjaToHangul; + return eDirection; + } + + void HangulHanjaConversionDialog::SetConversionFormat( HHC::ConversionFormat _eType ) + { + switch ( _eType ) + { + case HHC::eSimpleConversion: m_xSimpleConversion->set_active(true); break; + case HHC::eHangulBracketed: m_xHangulBracketed->set_active(true); break; + case HHC::eHanjaBracketed: m_xHanjaBracketed->set_active(true); break; + case HHC::eRubyHanjaAbove: m_xHanjaAbove->set_active(true); break; + case HHC::eRubyHanjaBelow: m_xHanjaBelow->set_active(true); break; + case HHC::eRubyHangulAbove: m_xHangulAbove->set_active(true); break; + case HHC::eRubyHangulBelow: m_xHangulBelow->set_active(true); break; + default: + OSL_FAIL( "HangulHanjaConversionDialog::SetConversionFormat: unknown type!" ); + } + } + + HHC::ConversionFormat HangulHanjaConversionDialog::GetConversionFormat( ) const + { + if ( m_xSimpleConversion->get_active() ) + return HHC::eSimpleConversion; + if ( m_xHangulBracketed->get_active() ) + return HHC::eHangulBracketed; + if ( m_xHanjaBracketed->get_active() ) + return HHC::eHanjaBracketed; + if ( m_xHanjaAbove->get_active() ) + return HHC::eRubyHanjaAbove; + if ( m_xHanjaBelow->get_active() ) + return HHC::eRubyHanjaBelow; + if ( m_xHangulAbove->get_active() ) + return HHC::eRubyHangulAbove; + if ( m_xHangulBelow->get_active() ) + return HHC::eRubyHangulBelow; + + OSL_FAIL( "HangulHanjaConversionDialog::GetConversionFormat: no radio checked?" ); + return HHC::eSimpleConversion; + } + + void HangulHanjaConversionDialog::EnableRubySupport( bool bVal ) + { + m_xHanjaAbove->set_sensitive( bVal ); + m_xHanjaBelow->set_sensitive( bVal ); + m_xHangulAbove->set_sensitive( bVal ); + m_xHangulBelow->set_sensitive( bVal ); + } + + void HangulHanjaOptionsDialog::Init() + { + if( !m_xConversionDictionaryList.is() ) + { + m_xConversionDictionaryList = ConversionDictionaryList::create( ::comphelper::getProcessComponentContext() ); + } + + m_aDictList.clear(); + m_xDictsLB->clear(); + + Reference< XNameContainer > xNameCont = m_xConversionDictionaryList->getDictionaryContainer(); + if( xNameCont.is() ) + { + Sequence< OUString > aDictNames( xNameCont->getElementNames() ); + + const OUString* pDic = aDictNames.getConstArray(); + sal_Int32 nCount = aDictNames.getLength(); + + sal_Int32 i; + for( i = 0 ; i < nCount ; ++i ) + { + Any aAny( xNameCont->getByName( pDic[ i ] ) ); + Reference< XConversionDictionary > xDic; + if( ( aAny >>= xDic ) && xDic.is() ) + { + if( LANGUAGE_KOREAN == LanguageTag( xDic->getLocale() ).getLanguageType() ) + { + m_aDictList.push_back( xDic ); + AddDict( xDic->getName(), xDic->isActive() ); + } + } + } + } + if (m_xDictsLB->n_children()) + m_xDictsLB->select(0); + } + + IMPL_LINK_NOARG(HangulHanjaOptionsDialog, OkHdl, weld::Button&, void) + { + sal_uInt32 nCnt = m_aDictList.size(); + sal_uInt32 n = 0; + sal_uInt32 nActiveDics = 0; + Sequence< OUString > aActiveDics; + + aActiveDics.realloc( nCnt ); + OUString* pActActiveDic = aActiveDics.getArray(); + + while( nCnt ) + { + Reference< XConversionDictionary > xDict = m_aDictList[ n ]; + + DBG_ASSERT( xDict.is(), "-HangulHanjaOptionsDialog::OkHdl(): someone is evaporated..." ); + + bool bActive = m_xDictsLB->get_toggle(n) == TRISTATE_TRUE; + xDict->setActive( bActive ); + Reference< util::XFlushable > xFlush( xDict, uno::UNO_QUERY ); + if( xFlush.is() ) + xFlush->flush(); + + if( bActive ) + { + pActActiveDic[ nActiveDics ] = xDict->getName(); + ++nActiveDics; + } + + ++n; + --nCnt; + } + + // save configuration + aActiveDics.realloc( nActiveDics ); + Any aTmp; + SvtLinguConfig aLngCfg; + aTmp <<= aActiveDics; + aLngCfg.SetProperty( UPH_ACTIVE_CONVERSION_DICTIONARIES, aTmp ); + + aTmp <<= m_xIgnorepostCB->get_active(); + aLngCfg.SetProperty( UPH_IS_IGNORE_POST_POSITIONAL_WORD, aTmp ); + + aTmp <<= m_xShowrecentlyfirstCB->get_active(); + aLngCfg.SetProperty( UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST, aTmp ); + + aTmp <<= m_xAutoreplaceuniqueCB->get_active(); + aLngCfg.SetProperty( UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES, aTmp ); + + m_xDialog->response(RET_OK); + } + + IMPL_LINK_NOARG(HangulHanjaOptionsDialog, DictsLB_SelectHdl, weld::TreeView&, void) + { + bool bSel = m_xDictsLB->get_selected_index() != -1; + + m_xEditPB->set_sensitive(bSel); + m_xDeletePB->set_sensitive(bSel); + } + + IMPL_LINK_NOARG(HangulHanjaOptionsDialog, NewDictHdl, weld::Button&, void) + { + OUString aName; + HangulHanjaNewDictDialog aNewDlg(m_xDialog.get()); + aNewDlg.run(); + if (!aNewDlg.GetName(aName)) + return; + + if( !m_xConversionDictionaryList.is() ) + return; + + try + { + Reference< XConversionDictionary > xDic = + m_xConversionDictionaryList->addNewDictionary( aName, LanguageTag::convertToLocale( LANGUAGE_KOREAN ), ConversionDictionaryType::HANGUL_HANJA ); + + if( xDic.is() ) + { + //adapt local caches: + m_aDictList.push_back( xDic ); + AddDict( xDic->getName(), xDic->isActive() ); + } + } + catch( const ElementExistException& ) + { + } + catch( const NoSupportException& ) + { + } + } + + IMPL_LINK_NOARG(HangulHanjaOptionsDialog, EditDictHdl, weld::Button&, void) + { + int nEntry = m_xDictsLB->get_selected_index(); + DBG_ASSERT(nEntry != -1, "+HangulHanjaEditDictDialog::EditDictHdl(): call of edit should not be possible with no selection!"); + if (nEntry != -1) + { + HangulHanjaEditDictDialog aEdDlg(m_xDialog.get(), m_aDictList, nEntry); + aEdDlg.run(); + } + } + + IMPL_LINK_NOARG(HangulHanjaOptionsDialog, DeleteDictHdl, weld::Button&, void) + { + int nSelPos = m_xDictsLB->get_selected_index(); + if (nSelPos == -1) + return; + + Reference< XConversionDictionary > xDic( m_aDictList[ nSelPos ] ); + if( !(m_xConversionDictionaryList.is() && xDic.is()) ) + return; + + Reference< XNameContainer > xNameCont = m_xConversionDictionaryList->getDictionaryContainer(); + if( !xNameCont.is() ) + return; + + try + { + xNameCont->removeByName( xDic->getName() ); + + //adapt local caches: + m_aDictList.erase(m_aDictList.begin()+nSelPos ); + m_xDictsLB->remove(nSelPos); + } + catch( const ElementExistException& ) + { + } + catch( const NoSupportException& ) + { + } + } + + HangulHanjaOptionsDialog::HangulHanjaOptionsDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/hangulhanjaoptdialog.ui", "HangulHanjaOptDialog") + , m_xDictsLB(m_xBuilder->weld_tree_view("dicts")) + , m_xIgnorepostCB(m_xBuilder->weld_check_button("ignorepost")) + , m_xShowrecentlyfirstCB(m_xBuilder->weld_check_button("showrecentfirst")) + , m_xAutoreplaceuniqueCB(m_xBuilder->weld_check_button("autoreplaceunique")) + , m_xNewPB(m_xBuilder->weld_button("new")) + , m_xEditPB(m_xBuilder->weld_button("edit")) + , m_xDeletePB(m_xBuilder->weld_button("delete")) + , m_xOkPB(m_xBuilder->weld_button("ok")) + { + m_xDictsLB->set_size_request(m_xDictsLB->get_approximate_digit_width() * 32, + m_xDictsLB->get_height_rows(5)); + + m_xDictsLB->enable_toggle_buttons(weld::ColumnToggleType::Check); + + m_xDictsLB->connect_changed( LINK( this, HangulHanjaOptionsDialog, DictsLB_SelectHdl ) ); + + m_xOkPB->connect_clicked( LINK( this, HangulHanjaOptionsDialog, OkHdl ) ); + m_xNewPB->connect_clicked( LINK( this, HangulHanjaOptionsDialog, NewDictHdl ) ); + m_xEditPB->connect_clicked( LINK( this, HangulHanjaOptionsDialog, EditDictHdl ) ); + m_xDeletePB->connect_clicked( LINK( this, HangulHanjaOptionsDialog, DeleteDictHdl ) ); + + SvtLinguConfig aLngCfg; + Any aTmp; + bool bVal = bool(); + aTmp = aLngCfg.GetProperty( UPH_IS_IGNORE_POST_POSITIONAL_WORD ); + if( aTmp >>= bVal ) + m_xIgnorepostCB->set_active( bVal ); + + aTmp = aLngCfg.GetProperty( UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST ); + if( aTmp >>= bVal ) + m_xShowrecentlyfirstCB->set_active( bVal ); + + aTmp = aLngCfg.GetProperty( UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES ); + if( aTmp >>= bVal ) + m_xAutoreplaceuniqueCB->set_active( bVal ); + + Init(); + } + + HangulHanjaOptionsDialog::~HangulHanjaOptionsDialog() + { + } + + void HangulHanjaOptionsDialog::AddDict(const OUString& rName, bool bChecked) + { + m_xDictsLB->append(); + int nRow = m_xDictsLB->n_children() - 1; + m_xDictsLB->set_toggle(nRow, bChecked ? TRISTATE_TRUE : TRISTATE_FALSE); + m_xDictsLB->set_text(nRow, rName, 0); + m_xDictsLB->set_id(nRow, rName); + } + + IMPL_LINK_NOARG(HangulHanjaNewDictDialog, OKHdl, weld::Button&, void) + { + OUString aName(comphelper::string::stripEnd(m_xDictNameED->get_text(), ' ')); + + m_bEntered = !aName.isEmpty(); + if (m_bEntered) + m_xDictNameED->set_text(aName); // do this in case of trailing chars have been deleted + + m_xDialog->response(RET_OK); + } + + IMPL_LINK_NOARG(HangulHanjaNewDictDialog, ModifyHdl, weld::Entry&, void) + { + OUString aName(comphelper::string::stripEnd(m_xDictNameED->get_text(), ' ')); + + m_xOkBtn->set_sensitive(!aName.isEmpty()); + } + + HangulHanjaNewDictDialog::HangulHanjaNewDictDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/hangulhanjaadddialog.ui", "HangulHanjaAddDialog") + , m_bEntered(false) + , m_xOkBtn(m_xBuilder->weld_button("ok")) + , m_xDictNameED(m_xBuilder->weld_entry("entry")) + { + m_xOkBtn->connect_clicked( LINK( this, HangulHanjaNewDictDialog, OKHdl ) ); + m_xDictNameED->connect_changed( LINK( this, HangulHanjaNewDictDialog, ModifyHdl ) ); + } + + HangulHanjaNewDictDialog::~HangulHanjaNewDictDialog() + { + } + + bool HangulHanjaNewDictDialog::GetName( OUString& _rRetName ) const + { + if( m_bEntered ) + _rRetName = comphelper::string::stripEnd(m_xDictNameED->get_text(), ' '); + + return m_bEntered; + } + + class SuggestionList + { + private: + protected: + std::vector m_vElements; + sal_uInt16 m_nNumOfEntries; + // index of the internal iterator, used for First() and Next() methods + sal_uInt16 m_nAct; + + const OUString* Next_(); + public: + SuggestionList(); + ~SuggestionList(); + + void Set( const OUString& _rElement, sal_uInt16 _nNumOfElement ); + void Reset( sal_uInt16 _nNumOfElement ); + const OUString & Get( sal_uInt16 _nNumOfElement ) const; + void Clear(); + + const OUString* First(); + const OUString* Next(); + + sal_uInt16 GetCount() const { return m_nNumOfEntries; } + }; + + SuggestionList::SuggestionList() : + m_vElements(MAXNUM_SUGGESTIONS) + { + m_nAct = m_nNumOfEntries = 0; + } + + SuggestionList::~SuggestionList() + { + Clear(); + } + + void SuggestionList::Set( const OUString& _rElement, sal_uInt16 _nNumOfElement ) + { + m_vElements[_nNumOfElement] = _rElement; + ++m_nNumOfEntries; + } + + void SuggestionList::Reset( sal_uInt16 _nNumOfElement ) + { + m_vElements[_nNumOfElement].clear(); + --m_nNumOfEntries; + } + + const OUString& SuggestionList::Get( sal_uInt16 _nNumOfElement ) const + { + return m_vElements[_nNumOfElement]; + } + + void SuggestionList::Clear() + { + if( m_nNumOfEntries ) + { + for (auto & vElement : m_vElements) + vElement.clear(); + m_nNumOfEntries = m_nAct = 0; + } + } + + const OUString* SuggestionList::Next_() + { + while( m_nAct < m_vElements.size() ) + { + auto & s = m_vElements[ m_nAct ]; + if (!s.isEmpty()) + return &s; + ++m_nAct; + } + + return nullptr; + } + + const OUString* SuggestionList::First() + { + m_nAct = 0; + return Next_(); + } + + const OUString* SuggestionList::Next() + { + const OUString* pRet; + + if( m_nAct < m_nNumOfEntries ) + { + ++m_nAct; + pRet = Next_(); + } + else + pRet = nullptr; + + return pRet; + } + + + bool SuggestionEdit::ShouldScroll( bool _bUp ) const + { + bool bRet = false; + + if( _bUp ) + { + if( !m_pPrev ) + bRet = m_pScrollBar->vadjustment_get_value() > m_pScrollBar->vadjustment_get_lower(); + } + else + { + if( !m_pNext ) + bRet = m_pScrollBar->vadjustment_get_value() < ( m_pScrollBar->vadjustment_get_upper() - 4 ); + } + + return bRet; + } + + void SuggestionEdit::DoJump( bool _bUp ) + { + m_pScrollBar->vadjustment_set_value( m_pScrollBar->vadjustment_get_value() + ( _bUp? -1 : 1 ) ); + m_pParent->UpdateScrollbar(); + } + + SuggestionEdit::SuggestionEdit(std::unique_ptr xEntry, HangulHanjaEditDictDialog* pParent) + : m_pParent(pParent) + , m_pPrev(nullptr) + , m_pNext(nullptr) + , m_pScrollBar(nullptr) + , m_xEntry(std::move(xEntry)) + { + m_xEntry->connect_key_press(LINK(this, SuggestionEdit, KeyInputHdl)); + } + + IMPL_LINK(SuggestionEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool) + { + bool bHandled = false; + + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nMod = rKeyCode.GetModifier(); + sal_uInt16 nCode = rKeyCode.GetCode(); + if( nCode == KEY_TAB && ( !nMod || KEY_SHIFT == nMod ) ) + { + bool bUp = KEY_SHIFT == nMod; + if( ShouldScroll( bUp ) ) + { + DoJump( bUp ); + m_xEntry->select_region(0, -1); + // Tab-travel doesn't really happen, so emulate it by setting a selection manually + bHandled = true; + } + } + else if( KEY_UP == nCode || KEY_DOWN == nCode ) + { + bool bUp = KEY_UP == nCode; + if( ShouldScroll( bUp ) ) + { + DoJump( bUp ); + bHandled = true; + } + else if( bUp ) + { + if( m_pPrev ) + { + m_pPrev->grab_focus(); + bHandled = true; + } + } + else if( m_pNext ) + { + m_pNext->grab_focus(); + bHandled = true; + } + } + + return bHandled; + } + + void SuggestionEdit::init(weld::ScrolledWindow* pScrollBar, SuggestionEdit* pPrev, SuggestionEdit* pNext) + { + m_pScrollBar = pScrollBar; + m_pPrev = pPrev; + m_pNext = pNext; + } + + namespace + { + bool GetConversions( const Reference< XConversionDictionary >& _xDict, + const OUString& _rOrg, + Sequence< OUString >& _rEntries ) + { + bool bRet = false; + if( _xDict.is() && !_rOrg.isEmpty() ) + { + try + { + _rEntries = _xDict->getConversions( _rOrg, + 0, + _rOrg.getLength(), + ConversionDirection_FROM_LEFT, + css::i18n::TextConversionOption::NONE ); + bRet = _rEntries.hasElements(); + } + catch( const IllegalArgumentException& ) + { + } + } + + return bRet; + } + } + + IMPL_LINK_NOARG( HangulHanjaEditDictDialog, ScrollHdl, weld::ScrolledWindow&, void ) + { + UpdateScrollbar(); + } + + IMPL_LINK_NOARG( HangulHanjaEditDictDialog, OriginalModifyHdl, weld::ComboBox&, void ) + { + m_bModifiedOriginal = true; + m_aOriginal = comphelper::string::stripEnd( m_xOriginalLB->get_active_text(), ' ' ); + + UpdateSuggestions(); + UpdateButtonStates(); + } + + IMPL_LINK( HangulHanjaEditDictDialog, EditModifyHdl1, weld::Entry&, rEdit, void ) + { + EditModify( &rEdit, 0 ); + } + + IMPL_LINK( HangulHanjaEditDictDialog, EditModifyHdl2, weld::Entry&, rEdit, void ) + { + EditModify( &rEdit, 1 ); + } + + IMPL_LINK( HangulHanjaEditDictDialog, EditModifyHdl3, weld::Entry&, rEdit, void ) + { + EditModify( &rEdit, 2 ); + } + + IMPL_LINK( HangulHanjaEditDictDialog, EditModifyHdl4, weld::Entry&, rEdit, void ) + { + EditModify( &rEdit, 3 ); + } + + IMPL_LINK_NOARG( HangulHanjaEditDictDialog, BookLBSelectHdl, weld::ComboBox&, void ) + { + InitEditDictDialog( m_xBookLB->get_active() ); + } + + IMPL_LINK_NOARG( HangulHanjaEditDictDialog, NewPBPushHdl, weld::Button&, void ) + { + DBG_ASSERT( m_xSuggestions, "-HangulHanjaEditDictDialog::NewPBPushHdl(): no suggestions... search in hell..." ); + Reference< XConversionDictionary > xDict = m_rDictList[ m_nCurrentDict ]; + if( xDict.is() && m_xSuggestions ) + { + //delete old entry + bool bRemovedSomething = DeleteEntryFromDictionary( xDict ); + + OUString aLeft( m_aOriginal ); + const OUString* pRight = m_xSuggestions->First(); + bool bAddedSomething = false; + while( pRight ) + { + try + { + //add new entry + xDict->addEntry( aLeft, *pRight ); + bAddedSomething = true; + } + catch( const IllegalArgumentException& ) + { + } + catch( const ElementExistException& ) + { + } + + pRight = m_xSuggestions->Next(); + } + + if( bAddedSomething || bRemovedSomething ) + InitEditDictDialog( m_nCurrentDict ); + } + else + { + SAL_INFO( "cui.dialogs", "dictionary faded away..." ); + } + } + + bool HangulHanjaEditDictDialog::DeleteEntryFromDictionary( const Reference< XConversionDictionary >& xDict ) + { + bool bRemovedSomething = false; + if( xDict.is() ) + { + OUString aOrg( m_aOriginal ); + Sequence< OUString > aEntries; + GetConversions( xDict, m_aOriginal, aEntries ); + + sal_uInt32 n = aEntries.getLength(); + OUString* pEntry = aEntries.getArray(); + while( n ) + { + try + { + xDict->removeEntry( aOrg, *pEntry ); + bRemovedSomething = true; + } + catch( const NoSuchElementException& ) + { // can not be... + } + + ++pEntry; + --n; + } + } + return bRemovedSomething; + } + + IMPL_LINK_NOARG( HangulHanjaEditDictDialog, DeletePBPushHdl, weld::Button&, void ) + { + if( DeleteEntryFromDictionary( m_rDictList[ m_nCurrentDict ] ) ) + { + m_aOriginal.clear(); + m_bModifiedOriginal = true; + InitEditDictDialog( m_nCurrentDict ); + } + } + + void HangulHanjaEditDictDialog::InitEditDictDialog( sal_uInt32 nSelDict ) + { + if( m_xSuggestions ) + m_xSuggestions->Clear(); + + if( m_nCurrentDict != nSelDict ) + { + m_nCurrentDict = nSelDict; + m_aOriginal.clear(); + m_bModifiedOriginal = true; + } + + UpdateOriginalLB(); + + m_xOriginalLB->set_entry_text( !m_aOriginal.isEmpty() ? m_aOriginal : m_aEditHintText); + m_xOriginalLB->select_entry_region(0, -1); + m_xOriginalLB->grab_focus(); + + UpdateSuggestions(); + UpdateButtonStates(); + } + + void HangulHanjaEditDictDialog::UpdateOriginalLB() + { + m_xOriginalLB->clear(); + Reference< XConversionDictionary > xDict = m_rDictList[ m_nCurrentDict ]; + if( xDict.is() ) + { + Sequence< OUString > aEntries = xDict->getConversionEntries( ConversionDirection_FROM_LEFT ); + sal_uInt32 n = aEntries.getLength(); + OUString* pEntry = aEntries.getArray(); + while( n ) + { + m_xOriginalLB->append_text( *pEntry ); + + ++pEntry; + --n; + } + } + else + { + SAL_INFO( "cui.dialogs", "dictionary faded away..." ); + } + } + + void HangulHanjaEditDictDialog::UpdateButtonStates() + { + bool bHaveValidOriginalString = !m_aOriginal.isEmpty() && m_aOriginal != m_aEditHintText; + bool bNew = bHaveValidOriginalString && m_xSuggestions && m_xSuggestions->GetCount() > 0; + bNew = bNew && ( m_bModifiedSuggestions || m_bModifiedOriginal ); + + m_xNewPB->set_sensitive( bNew ); + m_xDeletePB->set_sensitive(!m_bModifiedOriginal && bHaveValidOriginalString); + } + + void HangulHanjaEditDictDialog::UpdateSuggestions() + { + Sequence< OUString > aEntries; + bool bFound = GetConversions( m_rDictList[ m_nCurrentDict ], m_aOriginal, aEntries ); + if( bFound ) + { + m_bModifiedOriginal = false; + + if( m_xSuggestions ) + m_xSuggestions->Clear(); + + //fill found entries into boxes + sal_uInt32 nCnt = aEntries.getLength(); + if( nCnt ) + { + if( !m_xSuggestions ) + m_xSuggestions.reset(new SuggestionList); + + const OUString* pSugg = aEntries.getConstArray(); + sal_uInt32 n = 0; + while( nCnt ) + { + m_xSuggestions->Set( pSugg[ n ], sal_uInt16( n ) ); + ++n; + --nCnt; + } + } + m_bModifiedSuggestions = false; + } + + m_xScrollSB->vadjustment_set_value( 0 ); + UpdateScrollbar(); // will force edits to be filled new + } + + void HangulHanjaEditDictDialog::SetEditText(SuggestionEdit& rEdit, sal_uInt16 nEntryNum) + { + OUString aStr; + if( m_xSuggestions ) + { + aStr = m_xSuggestions->Get(nEntryNum); + } + + rEdit.set_text(aStr); + } + + void HangulHanjaEditDictDialog::EditModify(const weld::Entry* pEdit, sal_uInt8 _nEntryOffset) + { + m_bModifiedSuggestions = true; + + OUString aTxt( pEdit->get_text() ); + sal_uInt16 nEntryNum = m_nTopPos + _nEntryOffset; + if( aTxt.isEmpty() ) + { + //reset suggestion + if( m_xSuggestions ) + m_xSuggestions->Reset( nEntryNum ); + } + else + { + //set suggestion + if( !m_xSuggestions ) + m_xSuggestions.reset(new SuggestionList); + m_xSuggestions->Set( aTxt, nEntryNum ); + } + + UpdateButtonStates(); + } + + HangulHanjaEditDictDialog::HangulHanjaEditDictDialog(weld::Window* pParent, HHDictList& _rDictList, sal_uInt32 nSelDict) + : GenericDialogController(pParent, "cui/ui/hangulhanjaeditdictdialog.ui", "HangulHanjaEditDictDialog") + , m_aEditHintText ( CuiResId(RID_CUISTR_EDITHINT) ) + , m_rDictList ( _rDictList ) + , m_nCurrentDict ( 0xFFFFFFFF ) + , m_nTopPos ( 0 ) + , m_bModifiedSuggestions ( false ) + , m_bModifiedOriginal ( false ) + , m_xBookLB(m_xBuilder->weld_combo_box("book")) + , m_xOriginalLB(m_xBuilder->weld_combo_box("original")) + , m_xEdit1(new SuggestionEdit(m_xBuilder->weld_entry("edit1"), this)) + , m_xEdit2(new SuggestionEdit(m_xBuilder->weld_entry("edit2"), this)) + , m_xEdit3(new SuggestionEdit(m_xBuilder->weld_entry("edit3"), this)) + , m_xEdit4(new SuggestionEdit(m_xBuilder->weld_entry("edit4"), this)) + , m_xContents(m_xBuilder->weld_widget("box")) + , m_xScrollSB(m_xBuilder->weld_scrolled_window("scrollbar", true)) + , m_xNewPB(m_xBuilder->weld_button("new")) + , m_xDeletePB(m_xBuilder->weld_button("delete")) + { + Size aSize(m_xContents->get_preferred_size()); + m_xScrollSB->set_size_request(-1, aSize.Height()); + + m_xEdit1->init( m_xScrollSB.get(), nullptr, m_xEdit2.get() ); + m_xEdit2->init( m_xScrollSB.get(), m_xEdit1.get(), m_xEdit3.get() ); + m_xEdit3->init( m_xScrollSB.get(), m_xEdit2.get(), m_xEdit4.get() ); + m_xEdit4->init( m_xScrollSB.get(), m_xEdit3.get(), nullptr ); + + m_xOriginalLB->connect_changed( LINK( this, HangulHanjaEditDictDialog, OriginalModifyHdl ) ); + + m_xNewPB->connect_clicked( LINK( this, HangulHanjaEditDictDialog, NewPBPushHdl ) ); + m_xNewPB->set_sensitive( false ); + + m_xDeletePB->connect_clicked( LINK( this, HangulHanjaEditDictDialog, DeletePBPushHdl ) ); + m_xDeletePB->set_sensitive( false ); + + static_assert(MAXNUM_SUGGESTIONS >= 5, "number of suggestions should not under-run the value of 5"); + + // 4 here, because we have 4 edits / page + m_xScrollSB->vadjustment_configure(0, 0, MAXNUM_SUGGESTIONS, 1, 4, 4); + m_xScrollSB->connect_vadjustment_changed(LINK(this, HangulHanjaEditDictDialog, ScrollHdl)); + + m_xEdit1->connect_changed( LINK( this, HangulHanjaEditDictDialog, EditModifyHdl1 ) ); + m_xEdit2->connect_changed( LINK( this, HangulHanjaEditDictDialog, EditModifyHdl2 ) ); + m_xEdit3->connect_changed( LINK( this, HangulHanjaEditDictDialog, EditModifyHdl3 ) ); + m_xEdit4->connect_changed( LINK( this, HangulHanjaEditDictDialog, EditModifyHdl4 ) ); + + m_xBookLB->connect_changed( LINK( this, HangulHanjaEditDictDialog, BookLBSelectHdl ) ); + sal_uInt32 nDictCnt = m_rDictList.size(); + for( sal_uInt32 n = 0 ; n < nDictCnt ; ++n ) + { + Reference< XConversionDictionary > xDic( m_rDictList[n] ); + OUString aName; + if( xDic.is() ) + aName = xDic->getName(); + m_xBookLB->append_text( aName ); + } + m_xBookLB->set_active(nSelDict); + + InitEditDictDialog(nSelDict); + } + + HangulHanjaEditDictDialog::~HangulHanjaEditDictDialog() + { + } + + void HangulHanjaEditDictDialog::UpdateScrollbar() + { + sal_uInt16 nPos = m_xScrollSB->vadjustment_get_value(); + m_nTopPos = nPos; + + SetEditText( *m_xEdit1, nPos++ ); + SetEditText( *m_xEdit2, nPos++ ); + SetEditText( *m_xEdit3, nPos++ ); + SetEditText( *m_xEdit4, nPos ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/hldocntp.cxx b/cui/source/dialogs/hldocntp.cxx new file mode 100644 index 0000000000..3b3352315d --- /dev/null +++ b/cui/source/dialogs/hldocntp.cxx @@ -0,0 +1,449 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::uno; + +using namespace ::com::sun::star; + +/************************************************************************* +|* +|* Data-struct for documenttypes in listbox +|* +|************************************************************************/ + +namespace { + +struct DocumentTypeData +{ + OUString aStrURL; + OUString aStrExt; + DocumentTypeData (OUString aURL, OUString aExt) : aStrURL(std::move(aURL)), aStrExt(std::move(aExt)) + {} +}; + +} + +bool SvxHyperlinkNewDocTp::ImplGetURLObject( const OUString& rPath, std::u16string_view rBase, INetURLObject& aURLObject ) const +{ + bool bIsValidURL = !rPath.isEmpty(); + if ( bIsValidURL ) + { + aURLObject.SetURL( rPath ); + if ( aURLObject.GetProtocol() == INetProtocol::NotValid ) // test if the source is already a valid url + { // if not we have to create a url from a physical file name + bool wasAbs; + INetURLObject base(rBase); + base.setFinalSlash(); + aURLObject = base.smartRel2Abs( + rPath, wasAbs, true, INetURLObject::EncodeMechanism::All, + RTL_TEXTENCODING_UTF8, true); + } + bIsValidURL = aURLObject.GetProtocol() != INetProtocol::NotValid; + if ( bIsValidURL ) + { + OUString aBase( aURLObject.getName( INetURLObject::LAST_SEGMENT, false ) ); + if ( aBase.isEmpty() || ( aBase[0] == '.' ) ) + bIsValidURL = false; + } + if ( bIsValidURL ) + { + sal_Int32 nPos = m_xLbDocTypes->get_selected_index(); + if (nPos != -1) + aURLObject.SetExtension(weld::fromId(m_xLbDocTypes->get_id(nPos))->aStrExt); + } + + } + return bIsValidURL; +} + +/************************************************************************* +|* +|* Constructor / Destructor +|* +|************************************************************************/ + +SvxHyperlinkNewDocTp::SvxHyperlinkNewDocTp(weld::Container* pParent, SvxHpLinkDlg* pDlg, const SfxItemSet* pItemSet) + : SvxHyperlinkTabPageBase(pParent, pDlg, "cui/ui/hyperlinknewdocpage.ui", "HyperlinkNewDocPage", pItemSet) + , m_xRbtEditNow(xBuilder->weld_radio_button("editnow")) + , m_xRbtEditLater(xBuilder->weld_radio_button("editlater")) + , m_xCbbPath(new SvxHyperURLBox(xBuilder->weld_combo_box("path"))) + , m_xBtCreate(xBuilder->weld_button("create")) + , m_xLbDocTypes(xBuilder->weld_tree_view("types")) +{ + m_xCbbPath->SetSmartProtocol(INetProtocol::File); + m_xLbDocTypes->set_size_request(-1, m_xLbDocTypes->get_height_rows(5)); + + InitStdControls(); + + SetExchangeSupport (); + + m_xCbbPath->show(); + m_xCbbPath->SetBaseURL(SvtPathOptions().GetWorkPath()); + + // set defaults + m_xRbtEditNow->set_active(true); + + m_xBtCreate->connect_clicked(LINK(this, SvxHyperlinkNewDocTp, ClickNewHdl_Impl)); + + FillDocumentList (); +} + +SvxHyperlinkNewDocTp::~SvxHyperlinkNewDocTp () +{ + if (m_xLbDocTypes) + { + for (sal_Int32 n = 0, nEntryCount = m_xLbDocTypes->n_children(); n < nEntryCount; ++n) + delete weld::fromId(m_xLbDocTypes->get_id(n)); + m_xLbDocTypes = nullptr; + } +} + +/************************************************************************* +|* +|* Fill the all dialog-controls except controls in groupbox "more..." +|* +|************************************************************************/ + + +void SvxHyperlinkNewDocTp::FillDlgFields(const OUString& /*rStrURL*/) +{ +} + +void SvxHyperlinkNewDocTp::FillDocumentList() +{ + weld::WaitObject aWaitObj(mpDialog->getDialog()); + + std::vector aDynamicMenuEntries( SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu ) ); + + for ( const SvtDynMenuEntry & rDynamicMenuEntry : aDynamicMenuEntries ) + { + OUString aDocumentUrl = rDynamicMenuEntry.sURL; + OUString aTitle = rDynamicMenuEntry.sTitle; + + //#i96822# business cards, labels and database should not be inserted here + if( aDocumentUrl == "private:factory/swriter?slot=21051" || + aDocumentUrl == "private:factory/swriter?slot=21052" || + aDocumentUrl == "private:factory/sdatabase?Interactive" ) + continue; + + // Insert into listbox + if ( !aDocumentUrl.isEmpty() ) + { + if ( aDocumentUrl == "private:factory/simpress?slot=6686" ) // SJ: #106216# do not start + aDocumentUrl = "private:factory/simpress"; // the AutoPilot for impress + + // insert private-url and default-extension as user-data + std::shared_ptr pFilter = SfxFilter::GetDefaultFilterFromFactory( aDocumentUrl ); + if ( pFilter ) + { + // insert doc-name and image + OUString aTitleName = aTitle.replaceFirst( "~", "" ); + + OUString aStrDefExt(pFilter->GetDefaultExtension()); + DocumentTypeData *pTypeData = new DocumentTypeData(aDocumentUrl, aStrDefExt.copy(2)); + OUString sId(weld::toId(pTypeData)); + m_xLbDocTypes->append(sId, aTitleName); + } + } + } + m_xLbDocTypes->select(0); +} + +/************************************************************************* +|* +|* retrieve and prepare data from dialog-fields +|* +|************************************************************************/ + +void SvxHyperlinkNewDocTp::GetCurrentItemData ( OUString& rStrURL, OUString& aStrName, + OUString& aStrIntName, OUString& aStrFrame, + SvxLinkInsertMode& eMode ) +{ + // get data from dialog-controls + rStrURL = m_xCbbPath->get_active_text(); + INetURLObject aURL; + if ( ImplGetURLObject( rStrURL, m_xCbbPath->GetBaseURL(), aURL ) ) + { + rStrURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + + GetDataFromCommonFields( aStrName, aStrIntName, aStrFrame, eMode ); +} + +/************************************************************************* +|* +|* static method to create Tabpage +|* +|************************************************************************/ + +std::unique_ptr SvxHyperlinkNewDocTp::Create(weld::Container* pWindow, SvxHpLinkDlg* pDlg, const SfxItemSet* pItemSet) +{ + return std::make_unique(pWindow, pDlg, pItemSet); +} + +/************************************************************************* +|* +|* Set initial focus +|* +|************************************************************************/ +void SvxHyperlinkNewDocTp::SetInitFocus() +{ + m_xCbbPath->grab_focus(); +} + +namespace +{ + struct ExecuteInfo + { + bool bRbtEditLater; + bool bRbtEditNow; + INetURLObject aURL; + OUString aStrDocName; + // current document + css::uno::Reference xFrame; + SfxDispatcher* pDispatcher; + }; +} + +IMPL_STATIC_LINK(SvxHyperlinkNewDocTp, DispatchDocument, void*, p, void) +{ + std::unique_ptr xExecuteInfo(static_cast(p)); + if (!xExecuteInfo->xFrame.is()) + return; + try + { + //if it throws dispatcher is invalid + css::uno::Reference(xExecuteInfo->xFrame->getContainerWindow(), css::uno::UNO_QUERY_THROW); + + SfxViewFrame *pViewFrame = nullptr; + + // create items + SfxStringItem aName( SID_FILE_NAME, xExecuteInfo->aStrDocName ); + SfxStringItem aReferer( SID_REFERER, "private:user" ); + SfxStringItem aFrame( SID_TARGETNAME, "_blank"); + + OUString aStrFlags('S'); + if (xExecuteInfo->bRbtEditLater) + { + aStrFlags += "H"; + } + SfxStringItem aFlags (SID_OPTIONS, aStrFlags); + + // open url + const SfxPoolItemHolder aResult(xExecuteInfo->pDispatcher->ExecuteList( + SID_OPENDOC, SfxCallMode::SYNCHRON, + { &aName, &aFlags, &aFrame, &aReferer })); + + // save new doc + const SfxViewFrameItem *pItem = dynamic_cast(aResult.getItem()); // aResult is NULL if the Hyperlink + if ( pItem ) // creation is cancelled #106216# + { + pViewFrame = pItem->GetFrame(); + if (pViewFrame) + { + SfxStringItem aNewName( SID_FILE_NAME, xExecuteInfo->aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + SfxUnoFrameItem aDocFrame( SID_FILLFRAME, pViewFrame->GetFrame().GetFrameInterface() ); + pViewFrame->GetDispatcher()->ExecuteList( + SID_SAVEASDOC, SfxCallMode::SYNCHRON, + { &aNewName }, { &aDocFrame }); + } + } + + if (xExecuteInfo->bRbtEditNow) + { + css::uno::Reference xWindow(xExecuteInfo->xFrame->getContainerWindow(), css::uno::UNO_QUERY); + if (xWindow.is()) //will be false if the frame was exited while the document was loading (e.g. we waited for warning dialogs) + xWindow->toFront(); + } + + if (pViewFrame && xExecuteInfo->bRbtEditLater) + { + SfxObjectShell* pObjShell = pViewFrame->GetObjectShell(); + pObjShell->DoClose(); + } + } + catch (...) + { + } +} + +/************************************************************************* +|* +|* Any action to do after apply-button is pressed +|* +\************************************************************************/ +void SvxHyperlinkNewDocTp::DoApply() +{ + weld::WaitObject aWait(mpDialog->getDialog()); + + // get data from dialog-controls + OUString aStrNewName = m_xCbbPath->get_active_text(); + + if ( aStrNewName.isEmpty() ) + aStrNewName = maStrInitURL; + + // create a real URL-String + INetURLObject aURL; + if ( !ImplGetURLObject( aStrNewName, m_xCbbPath->GetBaseURL(), aURL ) ) + return; + + // create Document + aStrNewName = aURL.GetURLPath( INetURLObject::DecodeMechanism::NONE ); + bool bCreate = true; + try + { + // check if file exists, warn before we overwrite it + std::unique_ptr pIStm = ::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ); + + bool bOk = pIStm && ( pIStm->GetError() == ERRCODE_NONE); + + pIStm.reset(); + + if( bOk ) + { + std::unique_ptr xWarn(Application::CreateMessageDialog(mpDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::YesNo, + CuiResId(RID_CUISTR_HYPERDLG_QUERYOVERWRITE))); + bCreate = xWarn->run() == RET_YES; + } + } + catch (const uno::Exception&) + { + } + + if (!bCreate || aStrNewName.isEmpty()) + return; + + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + + pExecuteInfo->bRbtEditLater = m_xRbtEditLater->get_active(); + pExecuteInfo->bRbtEditNow = m_xRbtEditNow->get_active(); + // get private-url + sal_Int32 nPos = m_xLbDocTypes->get_selected_index(); + if (nPos == -1) + nPos = 0; + pExecuteInfo->aURL = aURL; + pExecuteInfo->aStrDocName = weld::fromId(m_xLbDocTypes->get_id(nPos))->aStrURL; + + // current document + pExecuteInfo->xFrame = GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface(); + pExecuteInfo->pDispatcher = GetDispatcher(); + + Application::PostUserEvent(LINK(nullptr, SvxHyperlinkNewDocTp, DispatchDocument), pExecuteInfo); +} + +/************************************************************************* +|* +|* Click on imagebutton : new +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkNewDocTp, ClickNewHdl_Impl, weld::Button&, void) +{ + DisableClose( true ); + uno::Reference < XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference < XFolderPicker2 > xFolderPicker = sfx2::createFolderPicker(xContext, mpDialog->getDialog()); + + OUString aStrURL; + OUString aTempStrURL( m_xCbbPath->get_active_text() ); + osl::FileBase::getFileURLFromSystemPath( aTempStrURL, aStrURL ); + + OUString aStrPath = aStrURL; + bool bZeroPath = aStrPath.isEmpty(); + bool bHandleFileName = bZeroPath; // when path has length of 0, then the rest should always be handled + // as file name, otherwise we do not yet know + + if( bZeroPath ) + aStrPath = SvtPathOptions().GetWorkPath(); + else if( !::utl::UCBContentHelper::IsFolder( aStrURL ) ) + bHandleFileName = true; + + xFolderPicker->setDisplayDirectory( aStrPath ); + sal_Int16 nResult = xFolderPicker->execute(); + DisableClose( false ); + if( ExecutableDialogResults::OK != nResult ) + return; + + char const sSlash[] = "/"; + + INetURLObject aURL( aStrURL, INetProtocol::File ); + OUString aStrName; + if( bHandleFileName ) + aStrName = bZeroPath? aTempStrURL : aURL.getName(); + + m_xCbbPath->SetBaseURL( xFolderPicker->getDirectory() ); + OUString aStrTmp( xFolderPicker->getDirectory() ); + + if( aStrTmp[ aStrTmp.getLength() - 1 ] != sSlash[0] ) + aStrTmp += sSlash; + + // append old file name + if( bHandleFileName ) + aStrTmp += aStrName; + + INetURLObject aNewURL( aStrTmp ); + + if (!aStrName.isEmpty() && !aNewURL.getExtension().isEmpty() && + m_xLbDocTypes->get_selected_index() != -1) + { + // get private-url + const sal_Int32 nPos = m_xLbDocTypes->get_selected_index(); + aNewURL.setExtension(weld::fromId(m_xLbDocTypes->get_id(nPos))->aStrExt); + } + + if( aNewURL.GetProtocol() == INetProtocol::File ) + { + osl::FileBase::getSystemPathFromFileURL(aNewURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aStrTmp); + } + else + { + aStrTmp = aNewURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ); + } + + m_xCbbPath->set_entry_text( aStrTmp ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/hldoctp.cxx b/cui/source/dialogs/hldoctp.cxx new file mode 100644 index 0000000000..a1c1454b69 --- /dev/null +++ b/cui/source/dialogs/hldoctp.cxx @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include + +char const sHash[] = "#"; + +/************************************************************************* +|* +|* Constructor / Destructor +|* +|************************************************************************/ + +SvxHyperlinkDocTp::SvxHyperlinkDocTp(weld::Container* pParent, SvxHpLinkDlg* pDlg, const SfxItemSet* pItemSet) + : SvxHyperlinkTabPageBase(pParent, pDlg, "cui/ui/hyperlinkdocpage.ui", "HyperlinkDocPage", pItemSet) + , m_xCbbPath(new SvxHyperURLBox(xBuilder->weld_combo_box("path"))) + , m_xBtFileopen(xBuilder->weld_button("fileopen")) + , m_xEdTarget(xBuilder->weld_entry("target")) + , m_xFtFullURL(xBuilder->weld_label("url")) + , m_xBtBrowse(xBuilder->weld_button("browse")) + , m_bMarkWndOpen(false) +{ + m_xCbbPath->SetSmartProtocol(INetProtocol::File); + + InitStdControls(); + + m_xCbbPath->show(); + m_xCbbPath->SetBaseURL(INET_FILE_SCHEME); + + SetExchangeSupport(); + + // set handlers + m_xBtFileopen->connect_clicked( LINK ( this, SvxHyperlinkDocTp, ClickFileopenHdl_Impl ) ); + m_xBtBrowse->connect_clicked( LINK ( this, SvxHyperlinkDocTp, ClickTargetHdl_Impl ) ); + m_xCbbPath->connect_changed( LINK ( this, SvxHyperlinkDocTp, ModifiedPathHdl_Impl ) ); + m_xEdTarget->connect_changed( LINK ( this, SvxHyperlinkDocTp, ModifiedTargetHdl_Impl ) ); + + m_xCbbPath->connect_focus_out( LINK ( this, SvxHyperlinkDocTp, LostFocusPathHdl_Impl ) ); + + maTimer.SetInvokeHandler ( LINK ( this, SvxHyperlinkDocTp, TimeoutHdl_Impl ) ); +} + +SvxHyperlinkDocTp::~SvxHyperlinkDocTp() +{ +} + +/************************************************************************* +|* +|* Fill all dialog-controls except controls in groupbox "more..." +|* +|************************************************************************/ +void SvxHyperlinkDocTp::FillDlgFields(const OUString& rStrURL) +{ + sal_Int32 nPos = rStrURL.indexOf(sHash); + // path + m_xCbbPath->set_entry_text( rStrURL.copy( 0, ( nPos == -1 ? rStrURL.getLength() : nPos ) ) ); + + // set target in document at editfield + OUString aStrMark; + if ( nPos != -1 && nPos < rStrURL.getLength()-1 ) + aStrMark = rStrURL.copy( nPos+1 ); + m_xEdTarget->set_text( aStrMark ); + + ModifiedPathHdl_Impl(*m_xCbbPath->getWidget()); +} + +/************************************************************************* +|* +|* retrieve current url-string +|* +|************************************************************************/ +OUString SvxHyperlinkDocTp::GetCurrentURL () const +{ + // get data from dialog-controls + OUString aStrURL; + OUString aStrPath( m_xCbbPath->get_active_text() ); + OUString aStrMark( m_xEdTarget->get_text() ); + + if ( !aStrPath.isEmpty() ) + { + INetURLObject aURL( aStrPath ); + if ( aURL.GetProtocol() != INetProtocol::NotValid ) // maybe the path is already a valid + aStrURL = aStrPath; // hyperlink, then we can use this path directly + else + { + osl::FileBase::getFileURLFromSystemPath( aStrPath, aStrURL ); + aStrURL = INetURLObject::decode(aStrURL, INetURLObject::DecodeMechanism::ToIUri, RTL_TEXTENCODING_UTF8); + } + + //#105788# always create a URL even if it is not valid + if( aStrURL.isEmpty() ) + aStrURL = aStrPath; + } + + if( !aStrMark.isEmpty() ) + { + aStrURL += sHash + aStrMark; + } + + return aStrURL; +} + +/************************************************************************* +|* +|* retrieve and prepare data from dialog-fields +|* +|************************************************************************/ +void SvxHyperlinkDocTp::GetCurrentItemData ( OUString& rStrURL, OUString& aStrName, + OUString& aStrIntName, OUString& aStrFrame, + SvxLinkInsertMode& eMode ) +{ + // get data from standard-fields + rStrURL = GetCurrentURL(); + + if( rStrURL.equalsIgnoreAsciiCase( INET_FILE_SCHEME ) ) + rStrURL.clear(); + + GetDataFromCommonFields( aStrName, aStrIntName, aStrFrame, eMode ); +} + +/************************************************************************* +|* +|* static method to create Tabpage +|* +|************************************************************************/ +std::unique_ptr SvxHyperlinkDocTp::Create(weld::Container* pWindow, SvxHpLinkDlg* pDlg, const SfxItemSet* pItemSet) +{ + return std::make_unique(pWindow, pDlg, pItemSet); +} + +/************************************************************************* +|* +|* Set initial focus +|* +|************************************************************************/ +void SvxHyperlinkDocTp::SetInitFocus() +{ + m_xCbbPath->grab_focus(); +} + +/************************************************************************* +|* +|* Click on imagebutton : fileopen +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkDocTp, ClickFileopenHdl_Impl, weld::Button&, void) +{ + DisableClose( true ); + // Open Fileopen-Dialog + sfx2::FileDialogHelper aDlg( + css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, + mpDialog->getDialog() ); + OUString aOldURL( GetCurrentURL() ); + if( aOldURL.startsWithIgnoreAsciiCase( INET_FILE_SCHEME ) ) + { + OUString aPath; + osl::FileBase::getSystemPathFromFileURL(aOldURL, aPath); + aDlg.SetDisplayFolder( aPath ); + } + + ErrCode nError = aDlg.Execute(); + DisableClose( false ); + + if ( ERRCODE_NONE != nError ) + return; + + OUString aURL( aDlg.GetPath() ); + OUString aPath; + + osl::FileBase::getSystemPathFromFileURL(aURL, aPath); + + m_xCbbPath->SetBaseURL( aURL ); + m_xCbbPath->set_entry_text(aPath); + + if ( aOldURL != GetCurrentURL() ) + ModifiedPathHdl_Impl(*m_xCbbPath->getWidget()); +} + +/************************************************************************* +|* +|* Click on imagebutton : target +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkDocTp, ClickTargetHdl_Impl, weld::Button&, void) +{ + ShowMarkWnd(); + + if ( GetPathType ( maStrURL ) == EPathType::ExistsFile || + maStrURL.isEmpty() || + maStrURL.equalsIgnoreAsciiCase( INET_FILE_SCHEME ) || + maStrURL.startsWith( sHash ) ) + { + mxMarkWnd->SetError( LERR_NOERROR ); + + weld::WaitObject aWait(mpDialog->getDialog()); + + if ( maStrURL.equalsIgnoreAsciiCase( INET_FILE_SCHEME ) ) + mxMarkWnd->RefreshTree ( "" ); + else + mxMarkWnd->RefreshTree ( maStrURL ); + } + else + mxMarkWnd->SetError( LERR_DOCNOTOPEN ); +} + +/************************************************************************* +|* +|* Contents of combobox "Path" modified +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkDocTp, ModifiedPathHdl_Impl, weld::ComboBox&, void) +{ + maStrURL = GetCurrentURL(); + + maTimer.SetTimeout( 2500 ); + maTimer.Start(); + + m_xFtFullURL->set_label( maStrURL ); +} + +/************************************************************************* +|* +|* If path-field was modify, to browse the new doc after timeout +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkDocTp, TimeoutHdl_Impl, Timer *, void) +{ + if ( IsMarkWndVisible() && ( GetPathType( maStrURL )== EPathType::ExistsFile || + maStrURL.isEmpty() || + maStrURL.equalsIgnoreAsciiCase( INET_FILE_SCHEME ) ) ) + { + weld::WaitObject aWait(mpDialog->getDialog()); + + if ( maStrURL.equalsIgnoreAsciiCase( INET_FILE_SCHEME ) ) + mxMarkWnd->RefreshTree ( "" ); + else + mxMarkWnd->RefreshTree ( maStrURL ); + } +} + +/************************************************************************* +|* +|* Contents of editfield "Target" modified +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkDocTp, ModifiedTargetHdl_Impl, weld::Entry&, void) +{ + maStrURL = GetCurrentURL(); + + if (IsMarkWndVisible()) + mxMarkWnd->SelectEntry(m_xEdTarget->get_text()); + + m_xFtFullURL->set_label( maStrURL ); +} + +/************************************************************************* +|* +|* editfield "Target" lost focus +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkDocTp, LostFocusPathHdl_Impl, weld::Widget&, void) +{ + maStrURL = GetCurrentURL(); + + m_xFtFullURL->set_label( maStrURL ); +} + +/************************************************************************* +|* +|* Get String from Bookmark-Wnd +|* +|************************************************************************/ +void SvxHyperlinkDocTp::SetMarkStr ( const OUString& aStrMark ) +{ + m_xEdTarget->set_text(aStrMark); + + ModifiedTargetHdl_Impl ( *m_xEdTarget ); +} + +/************************************************************************* +|* +|* retrieve kind of pathstr +|* +|************************************************************************/ +SvxHyperlinkDocTp::EPathType SvxHyperlinkDocTp::GetPathType ( std::u16string_view rStrPath ) +{ + INetURLObject aURL( rStrPath, INetProtocol::File ); + + if( aURL.HasError() ) + return EPathType::Invalid; + else + return EPathType::ExistsFile; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/hlinettp.cxx b/cui/source/dialogs/hlinettp.cxx new file mode 100644 index 0000000000..c74d6ae106 --- /dev/null +++ b/cui/source/dialogs/hlinettp.cxx @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include + + +/************************************************************************* +|* +|* Constructor / Destructor +|* +|************************************************************************/ +SvxHyperlinkInternetTp::SvxHyperlinkInternetTp(weld::Container* pParent, + SvxHpLinkDlg* pDlg, + const SfxItemSet* pItemSet) + : SvxHyperlinkTabPageBase(pParent, pDlg, "cui/ui/hyperlinkinternetpage.ui", "HyperlinkInternetPage", + pItemSet) + , m_bMarkWndOpen(false) + , m_xCbbTarget(new SvxHyperURLBox(xBuilder->weld_combo_box("target"))) + , m_xFtTarget(xBuilder->weld_label("target_label")) +{ + // gtk_size_group_set_ignore_hidden, "Measuring the size of hidden widgets + // ... they will report a size of 0 nowadays, and thus, their size will + // not affect the other size group members", which is unfortunate. So here + // before we hide the labels, take the size group width and set it as + // explicit preferred size on a label that won't be hidden + auto nLabelWidth = m_xFtTarget->get_preferred_size().Width(); + m_xFtTarget->set_size_request(nLabelWidth, -1); + + m_xCbbTarget->SetSmartProtocol(INetProtocol::Http); + + InitStdControls(); + + m_xCbbTarget->show(); + + SetExchangeSupport (); + + // set handlers + m_xCbbTarget->connect_focus_out( LINK ( this, SvxHyperlinkInternetTp, LostFocusTargetHdl_Impl ) ); + m_xCbbTarget->connect_changed( LINK ( this, SvxHyperlinkInternetTp, ModifiedTargetHdl_Impl ) ); + maTimer.SetInvokeHandler ( LINK ( this, SvxHyperlinkInternetTp, TimeoutHdl_Impl ) ); +} + +SvxHyperlinkInternetTp::~SvxHyperlinkInternetTp() +{ +} + +/************************************************************************* +|* +|* Fill the all dialog-controls except controls in groupbox "more..." +|* +|************************************************************************/ +void SvxHyperlinkInternetTp::FillDlgFields(const OUString& rStrURL) +{ + INetURLObject aURL(rStrURL); + OUString aStrScheme(GetSchemeFromURL(rStrURL)); + + // set URL-field + // Show the scheme, #72740 + if ( aURL.GetProtocol() != INetProtocol::NotValid ) + m_xCbbTarget->set_entry_text( aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) ); + else + m_xCbbTarget->set_entry_text(rStrURL); + + SetScheme(aStrScheme); +} + +/************************************************************************* +|* +|* retrieve and prepare data from dialog-fields +|* +|************************************************************************/ + +void SvxHyperlinkInternetTp::GetCurrentItemData ( OUString& rStrURL, OUString& aStrName, + OUString& aStrIntName, OUString& aStrFrame, + SvxLinkInsertMode& eMode ) +{ + rStrURL = CreateAbsoluteURL(); + GetDataFromCommonFields( aStrName, aStrIntName, aStrFrame, eMode ); +} + +OUString SvxHyperlinkInternetTp::CreateAbsoluteURL() const +{ + // erase leading and trailing whitespaces + OUString aStrURL(m_xCbbTarget->get_active_text().trim()); + + INetURLObject aURL(aStrURL, GetSmartProtocolFromButtons()); + + if ( aURL.GetProtocol() != INetProtocol::NotValid ) + return aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + else //#105788# always create a URL even if it is not valid + return aStrURL; +} + +/************************************************************************* +|* +|* static method to create Tabpage +|* +|************************************************************************/ + +std::unique_ptr SvxHyperlinkInternetTp::Create(weld::Container* pWindow, SvxHpLinkDlg* pDlg, const SfxItemSet* pItemSet) +{ + return std::make_unique(pWindow, pDlg, pItemSet); +} + +/************************************************************************* +|* +|* Set initial focus +|* +|************************************************************************/ +void SvxHyperlinkInternetTp::SetInitFocus() +{ + m_xCbbTarget->grab_focus(); +} + +/************************************************************************* +|* +|* Contents of editfield "Target" modified +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkInternetTp, ModifiedTargetHdl_Impl, weld::ComboBox&, void) +{ + OUString aScheme = GetSchemeFromURL( m_xCbbTarget->get_active_text() ); + if( !aScheme.isEmpty() ) + SetScheme( aScheme ); + + // start timer + maTimer.SetTimeout( 2500 ); + maTimer.Start(); +} + +/************************************************************************* +|* +|* If target-field was modify, to browse the new doc after timeout +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkInternetTp, TimeoutHdl_Impl, Timer *, void) +{ + RefreshMarkWindow(); +} + +void SvxHyperlinkInternetTp::SetScheme(std::u16string_view rScheme) +{ + //update target: + RemoveImproperProtocol(rScheme); + m_xCbbTarget->SetSmartProtocol( GetSmartProtocolFromButtons() ); + + //update 'link target in document'-window and opening-button + if (o3tl::starts_with(rScheme, INET_HTTP_SCHEME) || rScheme.empty()) + { + if ( m_bMarkWndOpen ) + ShowMarkWnd (); + } + else + { + //disable for https and ftp + if ( m_bMarkWndOpen ) + HideMarkWnd (); + } +} + +/************************************************************************* +|* +|* Remove protocol if it does not fit to the current button selection +|* +|************************************************************************/ + +void SvxHyperlinkInternetTp::RemoveImproperProtocol(std::u16string_view aProperScheme) +{ + OUString aStrURL ( m_xCbbTarget->get_active_text() ); + if ( !aStrURL.isEmpty() ) + { + OUString aStrScheme(GetSchemeFromURL(aStrURL)); + if ( !aStrScheme.isEmpty() && aStrScheme != aProperScheme ) + { + aStrURL = aStrURL.copy( aStrScheme.getLength() ); + m_xCbbTarget->set_entry_text( aStrURL ); + } + } +} + +OUString SvxHyperlinkInternetTp::GetSchemeFromButtons() +{ + return INET_HTTP_SCHEME; +} + +INetProtocol SvxHyperlinkInternetTp::GetSmartProtocolFromButtons() +{ + return INetProtocol::Http; +} + +/************************************************************************* +|* +|* Combobox Target lost the focus +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkInternetTp, LostFocusTargetHdl_Impl, weld::Widget&, void) +{ + RefreshMarkWindow(); +} + +void SvxHyperlinkInternetTp::RefreshMarkWindow() +{ + if (IsMarkWndVisible()) + { + weld::WaitObject aWait(mpDialog->getDialog()); + OUString aStrURL( CreateAbsoluteURL() ); + if ( !aStrURL.isEmpty() ) + mxMarkWnd->RefreshTree ( aStrURL ); + else + mxMarkWnd->SetError( LERR_DOCNOTOPEN ); + } +} + +/************************************************************************* +|* +|* Get String from Bookmark-Wnd +|* +|************************************************************************/ +void SvxHyperlinkInternetTp::SetMarkStr ( const OUString& aStrMark ) +{ + OUString aStrURL(m_xCbbTarget->get_active_text()); + + const sal_Unicode sUHash = '#'; + sal_Int32 nPos = aStrURL.lastIndexOf( sUHash ); + + if( nPos != -1 ) + aStrURL = aStrURL.copy(0, nPos); + + aStrURL += OUStringChar(sUHash) + aStrMark; + + m_xCbbTarget->set_entry_text(aStrURL); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/hlmailtp.cxx b/cui/source/dialogs/hlmailtp.cxx new file mode 100644 index 0000000000..2d25bed917 --- /dev/null +++ b/cui/source/dialogs/hlmailtp.cxx @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include + +#include + +using namespace ::com::sun::star; + +/************************************************************************* +|* +|* Constructor / Destructor +|* +|************************************************************************/ +SvxHyperlinkMailTp::SvxHyperlinkMailTp(weld::Container* pParent, SvxHpLinkDlg* pDlg, const SfxItemSet* pItemSet) + : SvxHyperlinkTabPageBase(pParent, pDlg, "cui/ui/hyperlinkmailpage.ui", "HyperlinkMailPage", pItemSet) + , m_xCbbReceiver(new SvxHyperURLBox(xBuilder->weld_combo_box("receiver"))) + , m_xBtAdrBook(xBuilder->weld_button("addressbook")) + , m_xEdSubject(xBuilder->weld_entry("subject")) +{ + m_xCbbReceiver->SetSmartProtocol(INetProtocol::Mailto); + + InitStdControls(); + + m_xCbbReceiver->show(); + + SetExchangeSupport (); + + // set handlers + m_xBtAdrBook->connect_clicked( LINK ( this, SvxHyperlinkMailTp, ClickAdrBookHdl_Impl ) ); + m_xCbbReceiver->connect_changed( LINK ( this, SvxHyperlinkMailTp, ModifiedReceiverHdl_Impl) ); + + if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) || + comphelper::LibreOfficeKit::isActive() ) + m_xBtAdrBook->hide(); +} + +SvxHyperlinkMailTp::~SvxHyperlinkMailTp() +{ +} + +/************************************************************************* +|* +|* Fill the all dialog-controls except controls in groupbox "more..." +|* +|************************************************************************/ + +void SvxHyperlinkMailTp::FillDlgFields(const OUString& rStrURL) +{ + OUString aStrScheme = GetSchemeFromURL(rStrURL); + + // set URL-field and additional controls + OUString aStrURLc (rStrURL); + // set additional controls for EMail: + if ( aStrScheme.startsWith( INET_MAILTO_SCHEME ) ) + { + // Find mail-subject + OUString aStrSubject, aStrTmp( aStrURLc ); + + sal_Int32 nPos = aStrTmp.toAsciiLowerCase().indexOf( "subject" ); + + if ( nPos != -1 ) + nPos = aStrTmp.indexOf( '=', nPos ); + + if ( nPos != -1 ) + aStrSubject = aStrURLc.copy( nPos+1 ); + + nPos = aStrURLc.indexOf( '?' ); + + if ( nPos != -1 ) + aStrURLc = aStrURLc.copy( 0, nPos ); + + m_xEdSubject->set_text( aStrSubject ); + } + else + { + m_xEdSubject->set_text(""); + } + + m_xCbbReceiver->set_entry_text(aStrURLc); + + SetScheme( aStrScheme ); +} + +/************************************************************************* +|* +|* retrieve and prepare data from dialog-fields +|* +|************************************************************************/ +void SvxHyperlinkMailTp::GetCurrentItemData ( OUString& rStrURL, OUString& aStrName, + OUString& aStrIntName, OUString& aStrFrame, + SvxLinkInsertMode& eMode ) +{ + rStrURL = CreateAbsoluteURL(); + GetDataFromCommonFields( aStrName, aStrIntName, aStrFrame, eMode ); +} + +OUString SvxHyperlinkMailTp::CreateAbsoluteURL() const +{ + OUString aStrURL = m_xCbbReceiver->get_active_text(); + INetURLObject aURL(aStrURL, INetProtocol::Mailto); + + // subject for EMail-url + if( aURL.GetProtocol() == INetProtocol::Mailto ) + { + if (!m_xEdSubject->get_text().isEmpty()) + { + OUString aQuery = "subject=" + m_xEdSubject->get_text(); + aURL.SetParam(aQuery); + } + } + + if ( aURL.GetProtocol() != INetProtocol::NotValid ) + return aURL.GetMainURL( INetURLObject::DecodeMechanism::WithCharset ); + else //#105788# always create a URL even if it is not valid + return aStrURL; +} + +/************************************************************************* +|* +|* static method to create Tabpage +|* +|************************************************************************/ + +std::unique_ptr SvxHyperlinkMailTp::Create(weld::Container* pWindow, SvxHpLinkDlg* pDlg, const SfxItemSet* pItemSet) +{ + return std::make_unique(pWindow, pDlg, pItemSet); +} + +/************************************************************************* +|* +|* Set initial focus +|* +|************************************************************************/ +void SvxHyperlinkMailTp::SetInitFocus() +{ + m_xCbbReceiver->grab_focus(); +} + +/************************************************************************* +|************************************************************************/ +void SvxHyperlinkMailTp::SetScheme(std::u16string_view rScheme) +{ + //update target: + RemoveImproperProtocol(rScheme); + m_xCbbReceiver->SetSmartProtocol( INetProtocol::Mailto ); + + //show/hide special fields for MAIL: + m_xBtAdrBook->set_sensitive(true); + m_xEdSubject->set_sensitive(true); +} + +/************************************************************************* +|* +|* Remove protocol if it does not fit to the current button selection +|* +|************************************************************************/ +void SvxHyperlinkMailTp::RemoveImproperProtocol(std::u16string_view aProperScheme) +{ + OUString aStrURL(m_xCbbReceiver->get_active_text()); + if ( !aStrURL.isEmpty() ) + { + OUString aStrScheme = GetSchemeFromURL( aStrURL ); + if ( !aStrScheme.isEmpty() && aStrScheme != aProperScheme ) + { + aStrURL = aStrURL.copy( aStrScheme.getLength() ); + m_xCbbReceiver->set_entry_text(aStrURL); + } + } +} + +/************************************************************************* +|* +|* Contents of editfield "receiver" modified +|* +|************************************************************************/ +IMPL_LINK_NOARG(SvxHyperlinkMailTp, ModifiedReceiverHdl_Impl, weld::ComboBox&, void) +{ + OUString aScheme = GetSchemeFromURL( m_xCbbReceiver->get_active_text() ); + if(!aScheme.isEmpty()) + SetScheme( aScheme ); +} + +/************************************************************************* +|* +|* Click on imagebutton : addressbook +|* +|************************************************************************/ +IMPL_STATIC_LINK_NOARG(SvxHyperlinkMailTp, ClickAdrBookHdl_Impl, weld::Button&, void) +{ + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + { + SfxItemPool &rPool = pViewFrame->GetPool(); + SfxRequest aReq(SID_VIEW_DATA_SOURCE_BROWSER, SfxCallMode::SLOT, rPool); + pViewFrame->ExecuteSlot( aReq, true ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/hlmarkwn.cxx b/cui/source/dialogs/hlmarkwn.cxx new file mode 100644 index 0000000000..cf90450450 --- /dev/null +++ b/cui/source/dialogs/hlmarkwn.cxx @@ -0,0 +1,532 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +// UNO-Stuff +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; + +namespace { + +// Userdata-struct for tree-entries +struct TargetData +{ + OUString aUStrLinkname; + bool bIsTarget; + + TargetData (const OUString& aUStrLName, bool bTarget) + : bIsTarget(bTarget) + { + if (bIsTarget) + aUStrLinkname = aUStrLName; + } +}; + +} + +//*** Window-Class *** +// Constructor / Destructor +SvxHlinkDlgMarkWnd::SvxHlinkDlgMarkWnd(weld::Window* pParentDialog, SvxHyperlinkTabPageBase *pParentPage) + : GenericDialogController(pParentDialog, "cui/ui/hyperlinkmarkdialog.ui", "HyperlinkMark") + , mpParent(pParentPage) + , mnError(LERR_NOERROR) + , mxBtApply(m_xBuilder->weld_button("ok")) + , mxBtClose(m_xBuilder->weld_button("close")) + , mxLbTree(m_xBuilder->weld_tree_view("TreeListBox")) + , mxError(m_xBuilder->weld_label("error")) +{ + mxLbTree->set_size_request(mxLbTree->get_approximate_digit_width() * 25, + mxLbTree->get_height_rows(12)); + mxBtApply->connect_clicked( LINK ( this, SvxHlinkDlgMarkWnd, ClickApplyHdl_Impl ) ); + mxBtClose->connect_clicked( LINK ( this, SvxHlinkDlgMarkWnd, ClickCloseHdl_Impl ) ); + mxLbTree->connect_row_activated( LINK ( this, SvxHlinkDlgMarkWnd, DoubleClickApplyHdl_Impl ) ); + + // tdf#149935 - remember last used position and size + SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id()); + if (aDlgOpt.Exists()) + m_xDialog->set_window_state(aDlgOpt.GetWindowState()); +} + +SvxHlinkDlgMarkWnd::~SvxHlinkDlgMarkWnd() +{ + ClearTree(); + // tdf#149935 - remember last used position and size + SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id()); + aDlgOpt.SetWindowState(m_xDialog->get_window_state(vcl::WindowDataMask::PosSize)); +} + +void SvxHlinkDlgMarkWnd::ErrorChanged() +{ + if (mnError == LERR_NOENTRIES) + { + OUString aStrMessage = CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_NOENTRIES ); + mxError->set_label(aStrMessage); + mxError->show(); + mxLbTree->hide(); + } + else if (mnError == LERR_DOCNOTOPEN) + { + OUString aStrMessage = CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_DOCNOTOPEN ); + mxError->set_label(aStrMessage); + mxError->show(); + mxLbTree->hide(); + } + else + { + mxLbTree->show(); + mxError->hide(); + } +} + +// Set an errorstatus +sal_uInt16 SvxHlinkDlgMarkWnd::SetError( sal_uInt16 nError) +{ + sal_uInt16 nOldError = mnError; + mnError = nError; + + if( mnError != LERR_NOERROR ) + ClearTree(); + + ErrorChanged(); + + return nOldError; +} + +// Move window +void SvxHlinkDlgMarkWnd::MoveTo(const Point& rNewPos) +{ + // tdf#149935 - remember last used position and size + SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id()); + if (aDlgOpt.Exists()) + m_xDialog->set_window_state(aDlgOpt.GetWindowState()); + else + m_xDialog->window_move(rNewPos.X(), rNewPos.Y()); +} + +namespace +{ + void SelectPath(weld::TreeIter* pEntry, weld::TreeView& rLbTree, + std::deque &rLastSelectedPath) + { + OUString sTitle(rLastSelectedPath.front()); + rLastSelectedPath.pop_front(); + if (sTitle.isEmpty()) + return; + while (pEntry) + { + if (sTitle == rLbTree.get_text(*pEntry)) + { + rLbTree.select(*pEntry); + rLbTree.scroll_to_row(*pEntry); + if (!rLastSelectedPath.empty()) + { + rLbTree.expand_row(*pEntry); + if (!rLbTree.iter_children(*pEntry)) + pEntry = nullptr; + SelectPath(pEntry, rLbTree, rLastSelectedPath); + } + break; + } + if (!rLbTree.iter_next_sibling(*pEntry)) + pEntry = nullptr; + } + } +} + +constexpr OUString TG_SETTING_MANAGER = u"TargetInDocument"_ustr; +constexpr OUString TG_SETTING_LASTMARK = u"LastSelectedMark"_ustr; +constexpr OUString TG_SETTING_LASTPATH = u"LastSelectedPath"_ustr; + +void SvxHlinkDlgMarkWnd::RestoreLastSelection() +{ + bool bSelectedEntry = false; + + OUString sLastSelectedMark; + std::deque aLastSelectedPath; + SvtViewOptions aViewSettings( EViewType::Dialog, TG_SETTING_MANAGER ); + if (aViewSettings.Exists()) + { + //Maybe we might want to have some sort of mru list and keep a mapping + //per document, rather than the current reuse of "the last thing + //selected, regardless of the document" + aViewSettings.GetUserItem(TG_SETTING_LASTMARK) >>= sLastSelectedMark; + uno::Sequence aTmp; + aViewSettings.GetUserItem(TG_SETTING_LASTPATH) >>= aTmp; + aLastSelectedPath = comphelper::sequenceToContainer< std::deque >(aTmp); + } + //fallback to previous entry selected the last time we executed this dialog. + //First see if the exact mark exists and re-use that + if (!sLastSelectedMark.isEmpty()) + bSelectedEntry = SelectEntry(sLastSelectedMark); + //Otherwise just select the closest path available + //now to what was available at dialog close time + if (!bSelectedEntry && !aLastSelectedPath.empty()) + { + std::deque aTmpSelectedPath(aLastSelectedPath); + std::unique_ptr xEntry(mxLbTree->make_iterator()); + if (!mxLbTree->get_iter_first(*xEntry)) + xEntry.reset(); + SelectPath(xEntry.get(), *mxLbTree, aTmpSelectedPath); + } +} + +// Interface to refresh tree +void SvxHlinkDlgMarkWnd::RefreshTree (const OUString& aStrURL) +{ + OUString aUStrURL; + + weld::WaitObject aWait(m_xDialog.get()); + + ClearTree(); + + sal_Int32 nPos = aStrURL.indexOf('#'); + + if (nPos != 0) + aUStrURL = aStrURL; + + if (!RefreshFromDoc(aUStrURL)) + ErrorChanged(); + + bool bSelectedEntry = false; + + if ( nPos != -1 ) + { + OUString aStrMark = aStrURL.copy(nPos+1); + bSelectedEntry = SelectEntry(aStrMark); + } + + if (!bSelectedEntry) + RestoreLastSelection(); +} + +// get links from document +bool SvxHlinkDlgMarkWnd::RefreshFromDoc(const OUString& aURL) +{ + mnError = LERR_NOERROR; + + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( ::comphelper::getProcessComponentContext() ); + uno::Reference< lang::XComponent > xComp; + + if( !aURL.isEmpty() ) + { + // load from url + if( xDesktop.is() ) + { + try + { + uno::Sequence< beans::PropertyValue > aArg { comphelper::makePropertyValue("Hidden", true) }; + xComp = xDesktop->loadComponentFromURL( aURL, "_blank", 0, aArg ); + } + catch( const io::IOException& ) + { + + } + catch( const lang::IllegalArgumentException& ) + { + + } + } + } + else + { + // the component with user focus ( current document ) + xComp = xDesktop->getCurrentComponent(); + } + + if( xComp.is() ) + { + uno::Reference< document::XLinkTargetSupplier > xLTS( xComp, uno::UNO_QUERY ); + + if( xLTS.is() ) + { + if( FillTree( xLTS->getLinks() ) == 0 ) + mnError = LERR_NOENTRIES; + } + else + mnError = LERR_DOCNOTOPEN; + + if ( !aURL.isEmpty() ) + xComp->dispose(); + } + else + { + if( !aURL.isEmpty() ) + mnError=LERR_DOCNOTOPEN; + } + return (mnError==0); +} + +// Fill Tree-Control +int SvxHlinkDlgMarkWnd::FillTree( const uno::Reference< container::XNameAccess >& xLinks, const weld::TreeIter* pParentEntry ) +{ + // used to create the Headings outline parent children tree view relation + std::stack, const sal_Int32>> aHeadingsParentEntryStack; + + int nEntries=0; + const uno::Sequence< OUString > aNames( xLinks->getElementNames() ); + const sal_Int32 nLinks = aNames.getLength(); + const OUString* pNames = aNames.getConstArray(); + + static constexpr OUStringLiteral aProp_LinkDisplayName( u"LinkDisplayName" ); + static constexpr OUStringLiteral aProp_LinkTarget( u"com.sun.star.document.LinkTarget" ); + static constexpr OUStringLiteral aProp_LinkDisplayBitmap( u"LinkDisplayBitmap" ); + for( sal_Int32 i = 0; i < nLinks; i++ ) + { + uno::Any aAny; + OUString aLink( *pNames++ ); + + bool bError = false; + try + { + aAny = xLinks->getByName( aLink ); + } + catch(const uno::Exception&) + { + // if the name of the target was invalid (like empty headings) + // no object can be provided + bError = true; + } + if(bError) + continue; + + uno::Reference< beans::XPropertySet > xTarget; + + if( aAny >>= xTarget ) + { + try + { + // get name to display + aAny = xTarget->getPropertyValue( aProp_LinkDisplayName ); + OUString aDisplayName; + aAny >>= aDisplayName; + OUString aStrDisplayname ( aDisplayName ); + + // is it a target ? + uno::Reference< lang::XServiceInfo > xSI( xTarget, uno::UNO_QUERY ); + bool bIsTarget = xSI->supportsService( aProp_LinkTarget ); + + // create userdata + TargetData *pData = new TargetData ( aLink, bIsTarget ); + OUString sId(weld::toId(pData)); + + std::unique_ptr xEntry(mxLbTree->make_iterator()); + if (pParentEntry) + { + OUString sContentType = mxLbTree->get_text(*pParentEntry); + if (sContentType == "Headings") + { + if (aHeadingsParentEntryStack.empty()) + aHeadingsParentEntryStack.push( + std::pair(mxLbTree->make_iterator(pParentEntry), -1)); + + // get the headings name to display + aAny = xTarget->getPropertyValue("ActualOutlineName"); + OUString sActualOutlineName; + aAny >>= sActualOutlineName; + + // get the headings outline level + aAny = xTarget->getPropertyValue("OutlineLevel"); + sal_Int32 nOutlineLevel = *o3tl::doAccess(aAny); + + // pop until the top of stack entry has an outline level less than + // the to be inserted heading outline level + while (nOutlineLevel <= aHeadingsParentEntryStack.top().second) + aHeadingsParentEntryStack.pop(); + + mxLbTree->insert(aHeadingsParentEntryStack.top().first.get(), -1, + &sActualOutlineName, &sId, nullptr, nullptr, false, + xEntry.get()); + + // push if the inserted entry is a child + if (nOutlineLevel > aHeadingsParentEntryStack.top().second) + aHeadingsParentEntryStack.push( + std::pair(mxLbTree->make_iterator(xEntry.get()), nOutlineLevel)); + } + else + { + mxLbTree->insert(pParentEntry, -1, &aStrDisplayname, &sId, nullptr, + nullptr, false, xEntry.get()); + } + } + else + { + mxLbTree->insert(pParentEntry, -1, &aStrDisplayname, &sId, nullptr, nullptr, + false, xEntry.get()); + } + + try + { + // get bitmap for the tree-entry + uno::Reference< awt::XBitmap > + aXBitmap( xTarget->getPropertyValue( aProp_LinkDisplayBitmap ), uno::UNO_QUERY ); + if (aXBitmap.is()) + { + Graphic aBmp(Graphic(VCLUnoHelper::GetBitmap(aXBitmap))); + // insert Displayname into treelist with bitmaps + mxLbTree->set_image(*xEntry, aBmp.GetXGraphic(), -1); + } + } + catch(const css::uno::Exception&) + { + } + + nEntries++; + + uno::Reference< document::XLinkTargetSupplier > xLTS( xTarget, uno::UNO_QUERY ); + if( xLTS.is() ) + nEntries += FillTree( xLTS->getLinks(), xEntry.get() ); + } + catch(const css::uno::Exception&) + { + } + } + } + + return nEntries; +} + +// Clear Tree +void SvxHlinkDlgMarkWnd::ClearTree() +{ + std::unique_ptr xEntry = mxLbTree->make_iterator(); + bool bEntry = mxLbTree->get_iter_first(*xEntry); + + while (bEntry) + { + TargetData* pUserData = weld::fromId(mxLbTree->get_id(*xEntry)); + delete pUserData; + + bEntry = mxLbTree->iter_next(*xEntry); + } + + mxLbTree->clear(); +} + +// Find Entry for String +std::unique_ptr SvxHlinkDlgMarkWnd::FindEntry (std::u16string_view aStrName) +{ + bool bFound=false; + std::unique_ptr xEntry = mxLbTree->make_iterator(); + bool bEntry = mxLbTree->get_iter_first(*xEntry); + + while (bEntry && !bFound) + { + TargetData* pUserData = weld::fromId(mxLbTree->get_id(*xEntry)); + if (aStrName == pUserData->aUStrLinkname) + bFound = true; + else + bEntry = mxLbTree->iter_next(*xEntry); + } + + if (!bFound) + xEntry.reset(); + + return xEntry; +} + +// Select Entry +bool SvxHlinkDlgMarkWnd::SelectEntry(std::u16string_view aStrMark) +{ + std::unique_ptr xEntry = FindEntry(aStrMark); + if (!xEntry) + return false; + mxLbTree->set_cursor(*xEntry); + return true; +} + +// Click on Apply-Button / Double-click on item in tree +IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, DoubleClickApplyHdl_Impl, weld::TreeView&, bool) +{ + ClickApplyHdl_Impl(*mxBtApply); + return true; +} + +IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, ClickApplyHdl_Impl, weld::Button&, void) +{ + std::unique_ptr xEntry(mxLbTree->make_iterator()); + bool bEntry = mxLbTree->get_cursor(xEntry.get()); + if (bEntry) + { + TargetData* pData = weld::fromId(mxLbTree->get_id(*xEntry)); + if (pData->bIsTarget) + { + mpParent->SetMarkStr(pData->aUStrLinkname); + } + } +} + +// Click on Close-Button +IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, ClickCloseHdl_Impl, weld::Button&, void) +{ + std::unique_ptr xEntry(mxLbTree->make_iterator()); + bool bEntry = mxLbTree->get_cursor(xEntry.get()); + if (bEntry) + { + TargetData* pUserData = weld::fromId(mxLbTree->get_id(*xEntry)); + OUString sLastSelectedMark = pUserData->aUStrLinkname; + + std::deque aLastSelectedPath; + //If the bottommost entry is expanded but nothing + //underneath it is selected leave a dummy entry + if (mxLbTree->get_row_expanded(*xEntry)) + aLastSelectedPath.push_front(OUString()); + while (bEntry) + { + aLastSelectedPath.push_front(mxLbTree->get_text(*xEntry)); + bEntry = mxLbTree->iter_parent(*xEntry); + } + + uno::Sequence< beans::NamedValue > aSettings + { + { TG_SETTING_LASTMARK, css::uno::Any(sLastSelectedMark) }, + { TG_SETTING_LASTPATH, css::uno::Any(comphelper::containerToSequence(aLastSelectedPath)) } + }; + + // write + SvtViewOptions aViewSettings( EViewType::Dialog, TG_SETTING_MANAGER ); + aViewSettings.SetUserData( aSettings ); + } + + m_xDialog->response(RET_CANCEL); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/hltpbase.cxx b/cui/source/dialogs/hltpbase.cxx new file mode 100644 index 0000000000..f0aa7c368c --- /dev/null +++ b/cui/source/dialogs/hltpbase.cxx @@ -0,0 +1,571 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::ucbhelper; + +namespace { + +OUString CreateUiNameFromURL( const OUString& aStrURL ) +{ + OUString aStrUiURL; + INetURLObject aURLObj( aStrURL ); + + switch(aURLObj.GetProtocol()) + { + case INetProtocol::File: + osl::FileBase::getSystemPathFromFileURL(aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), aStrUiURL); + break; + case INetProtocol::Ftp : + { + //remove password from name + INetURLObject aTmpURL(aURLObj); + aTmpURL.SetPass(u""); + aStrUiURL = aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ); + } + break; + default : + { + aStrUiURL = aURLObj.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous); + } + } + if(aStrUiURL.isEmpty()) + return aStrURL; + return aStrUiURL; +} + +} + +// ComboBox-Control for URL's with History and Autocompletion +SvxHyperURLBox::SvxHyperURLBox(std::unique_ptr xControl) + : SvtURLBox(std::move(xControl)) + , DropTargetHelper(getWidget()->get_drop_target()) +{ + SetSmartProtocol(INetProtocol::Http); +} + +sal_Int8 SvxHyperURLBox::AcceptDrop( const AcceptDropEvent& /* rEvt */ ) +{ + return IsDropFormatSupported( SotClipboardFormatId::STRING ) ? DND_ACTION_COPY : DND_ACTION_NONE; +} + +sal_Int8 SvxHyperURLBox::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + TransferableDataHelper aDataHelper( rEvt.maDropEvent.Transferable ); + OUString aString; + sal_Int8 nRet = DND_ACTION_NONE; + + if( aDataHelper.GetString( SotClipboardFormatId::STRING, aString ) ) + { + set_entry_text(aString); + nRet = DND_ACTION_COPY; + } + + return nRet; +} + +//# Hyperlink-Dialog: Tabpages-Baseclass # + +SvxHyperlinkTabPageBase::SvxHyperlinkTabPageBase(weld::Container* pParent, + SvxHpLinkDlg* pDlg, + const OUString& rUIXMLDescription, + const OUString& rID, + const SfxItemSet* pItemSet) + : IconChoicePage(pParent, rUIXMLDescription, rID, pItemSet) + , mxCbbFrame(xBuilder->weld_combo_box("frame")) + , mxLbForm(xBuilder->weld_combo_box("form")) + , mxEdIndication(xBuilder->weld_entry("indication")) + , mxEdText(xBuilder->weld_entry("name")) + , mxBtScript(xBuilder->weld_button("script")) + , mxFormLabel(xBuilder->weld_label("form_label")) + , mxFrameLabel(xBuilder->weld_label("frame_label")) + , mbIsCloseDisabled( false ) + , mpDialog( pDlg ) + , mbStdControlsInit( false ) + , maTimer("cui SvxHyperlinkTabPageBase maTimer") +{ + // create bookmark-window +} + +SvxHyperlinkTabPageBase::~SvxHyperlinkTabPageBase() +{ + maTimer.Stop(); + + HideMarkWnd(); +} + +bool SvxHyperlinkTabPageBase::QueryClose() +{ + return !mbIsCloseDisabled; +} + +void SvxHyperlinkTabPageBase::InitStdControls () +{ + if ( !mbStdControlsInit ) + { + SfxDispatcher* pDispatch = GetDispatcher(); + SfxViewFrame* pViewFrame = pDispatch ? pDispatch->GetFrame() : nullptr; + SfxFrame* pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr; + if ( pFrame ) + { + TargetList aList; + SfxFrame::GetDefaultTargetList(aList); + if( !aList.empty() ) + { + size_t nCount = aList.size(); + size_t i; + for ( i = 0; i < nCount; i++ ) + { + mxCbbFrame->append_text( aList.at( i ) ); + } + } + } + + mxBtScript->set_from_icon_name(RID_SVXBMP_SCRIPT); + + mxBtScript->connect_clicked ( LINK ( this, SvxHyperlinkTabPageBase, ClickScriptHdl_Impl ) ); + } + + mbStdControlsInit = true; +} + +// Move Extra-Window +void SvxHyperlinkTabPageBase::MoveToExtraWnd( Point aNewPos ) +{ + mxMarkWnd->MoveTo(aNewPos); +} + +// Show Extra-Window +void SvxHyperlinkTabPageBase::ShowMarkWnd() +{ + if (mxMarkWnd) + { + mxMarkWnd->getDialog()->present(); + return; + } + + weld::Dialog* pDialog = mpDialog->getDialog(); + + mxMarkWnd = std::make_shared(pDialog, this); + + // Size of dialog-window in screen pixels + Point aDlgPos(pDialog->get_position()); + Size aDlgSize(pDialog->get_size()); + + // Absolute size of the screen + ::tools::Rectangle aScreen(pDialog->get_monitor_workarea()); + + // Size of Extrawindow + Size aExtraWndSize(mxMarkWnd->getDialog()->get_preferred_size()); + + // mxMarkWnd is a child of mpDialog, so coordinates for positioning must be relative to mpDialog + if( aDlgPos.X()+(1.05*aDlgSize.Width())+aExtraWndSize.Width() > aScreen.Right() ) + { + if( aDlgPos.X() - ( 0.05*aDlgSize.Width() ) - aExtraWndSize.Width() < 0 ) + { + // Pos Extrawindow anywhere + MoveToExtraWnd( Point(10,10) ); // very unlikely + } + else + { + // Pos Extrawindow on the left side of Dialog + MoveToExtraWnd( Point(0,0) - Point( tools::Long(0.05*aDlgSize.Width()), 0 ) - Point( aExtraWndSize.Width(), 0 ) ); + } + } + else + { + // Pos Extrawindow on the right side of Dialog + MoveToExtraWnd ( Point( tools::Long(1.05*aDlgSize.getWidth()), 0 ) ); + } + + // Set size of Extra-Window + mxMarkWnd->getDialog()->set_size_request(aExtraWndSize.Width(), aDlgSize.Height()); + + weld::DialogController::runAsync(mxMarkWnd, [this](sal_Int32 /*nResult*/) { mxMarkWnd.reset(); } ); +} + +void SvxHyperlinkTabPageBase::HideMarkWnd() +{ + if (mxMarkWnd) + { + mxMarkWnd->response(RET_CANCEL); + mxMarkWnd.reset(); + } +} + +// Fill Dialogfields +void SvxHyperlinkTabPageBase::FillStandardDlgFields ( const SvxHyperlinkItem* pHyperlinkItem ) +{ + if (!comphelper::LibreOfficeKit::isActive()) + { + // Frame + sal_Int32 nPos = mxCbbFrame->find_text(pHyperlinkItem->GetTargetFrame()); + if (nPos != -1) + mxCbbFrame->set_active(nPos); + + // Form + OUString aStrFormText = CuiResId( RID_CUISTR_HYPERDLG_FROM_TEXT ); + + OUString aStrFormButton = CuiResId( RID_CUISTR_HYPERDLG_FORM_BUTTON ); + + if( pHyperlinkItem->GetInsertMode() & HLINK_HTMLMODE ) + { + mxLbForm->clear(); + mxLbForm->append_text( aStrFormText ); + mxLbForm->set_active( 0 ); + } + else + { + mxLbForm->clear(); + mxLbForm->append_text( aStrFormText ); + mxLbForm->append_text( aStrFormButton ); + mxLbForm->set_active( pHyperlinkItem->GetInsertMode() == HLINK_BUTTON ? 1 : 0 ); + } + } + else + { + mxCbbFrame->hide(); + mxLbForm->hide(); + mxFormLabel->hide(); + mxFrameLabel->hide(); + } + + // URL + mxEdIndication->set_text( pHyperlinkItem->GetName() ); + + // Name + mxEdText->set_text( pHyperlinkItem->GetIntName() ); + + // Script-button + if (!comphelper::LibreOfficeKit::isActive()) + { + if ( pHyperlinkItem->GetMacroEvents() == HyperDialogEvent::NONE ) + mxBtScript->set_sensitive(false); + else + mxBtScript->set_sensitive(true); + } + else + { + mxBtScript->hide(); + } +} + +// Any action to do after apply-button is pressed +void SvxHyperlinkTabPageBase::DoApply () +{ + // default-implementation : do nothing +} + +// This method would be called from bookmark-window to set new mark-string +void SvxHyperlinkTabPageBase::SetMarkStr ( const OUString& /*aStrMark*/ ) +{ + // default-implementation : do nothing +} + +// Set initial focus +void SvxHyperlinkTabPageBase::SetInitFocus() +{ + xContainer->grab_focus(); +} + +// retrieve dispatcher +SfxDispatcher* SvxHyperlinkTabPageBase::GetDispatcher() const +{ + return mpDialog->GetDispatcher(); +} + +void SvxHyperlinkTabPageBase::DisableClose(bool _bDisable) +{ + mbIsCloseDisabled = _bDisable; + if (mbIsCloseDisabled) + maBusy.incBusy(mpDialog->getDialog()); + else + maBusy.decBusy(); +} + +// Click on imagebutton : Script +IMPL_LINK_NOARG(SvxHyperlinkTabPageBase, ClickScriptHdl_Impl, weld::Button&, void) +{ + SvxHyperlinkItem *pHyperlinkItem = const_cast( + GetItemSet().GetItem (SID_HYPERLINK_GETLINK)); + + if (!pHyperlinkItem || pHyperlinkItem->GetMacroEvents() == HyperDialogEvent::NONE) + return; + + // get macros from itemset + const SvxMacroTableDtor* pMacroTbl = pHyperlinkItem->GetMacroTable(); + SvxMacroItem aItem ( SID_ATTR_MACROITEM ); + if( pMacroTbl ) + aItem.SetMacroTable( *pMacroTbl ); + + // create empty itemset for macro-dlg + SfxItemSetFixed aItemSet( SfxGetpApp()->GetPool() ); + aItemSet.Put ( aItem ); + + DisableClose( true ); + + SfxMacroAssignDlg aDlg(mpDialog->getDialog(), mxDocumentFrame, aItemSet); + + // add events + SfxMacroTabPage *pMacroPage = aDlg.GetTabPage(); + + if ( pHyperlinkItem->GetMacroEvents() & HyperDialogEvent::MouseOverObject ) + pMacroPage->AddEvent( CuiResId(RID_CUISTR_HYPDLG_MACROACT1), + SvMacroItemId::OnMouseOver ); + if ( pHyperlinkItem->GetMacroEvents() & HyperDialogEvent::MouseClickObject ) + pMacroPage->AddEvent( CuiResId(RID_CUISTR_HYPDLG_MACROACT2), + SvMacroItemId::OnClick); + if ( pHyperlinkItem->GetMacroEvents() & HyperDialogEvent::MouseOutObject ) + pMacroPage->AddEvent( CuiResId(RID_CUISTR_HYPDLG_MACROACT3), + SvMacroItemId::OnMouseOut); + // execute dlg + short nRet = aDlg.run(); + DisableClose( false ); + if ( RET_OK == nRet ) + { + const SfxItemSet* pOutSet = aDlg.GetOutputItemSet(); + const SfxPoolItem* pItem; + if( SfxItemState::SET == pOutSet->GetItemState( SID_ATTR_MACROITEM, false, &pItem )) + { + pHyperlinkItem->SetMacroTable( static_cast(pItem)->GetMacroTable() ); + } + } +} + +// Get Macro-Infos +HyperDialogEvent SvxHyperlinkTabPageBase::GetMacroEvents() const +{ + const SvxHyperlinkItem *pHyperlinkItem = + GetItemSet().GetItem (SID_HYPERLINK_GETLINK); + + return pHyperlinkItem ? pHyperlinkItem->GetMacroEvents() : HyperDialogEvent(); +} + +SvxMacroTableDtor* SvxHyperlinkTabPageBase::GetMacroTable() +{ + const SvxHyperlinkItem *pHyperlinkItem = + GetItemSet().GetItem (SID_HYPERLINK_GETLINK); + + return const_cast(pHyperlinkItem->GetMacroTable()); +} + +// try to detect the current protocol that is used in rStrURL +OUString SvxHyperlinkTabPageBase::GetSchemeFromURL( const OUString& rStrURL ) +{ + OUString aStrScheme; + + INetURLObject aURL( rStrURL ); + INetProtocol aProtocol = aURL.GetProtocol(); + + // our new INetUrlObject now has the ability + // to detect if a Url is valid or not :-( + if ( aProtocol == INetProtocol::NotValid ) + { + if ( rStrURL.startsWithIgnoreAsciiCase( INET_HTTP_SCHEME ) ) + { + aStrScheme = INET_HTTP_SCHEME; + } + else if ( rStrURL.startsWithIgnoreAsciiCase( INET_HTTPS_SCHEME ) ) + { + aStrScheme = INET_HTTPS_SCHEME; + } + else if ( rStrURL.startsWithIgnoreAsciiCase( INET_FTP_SCHEME ) ) + { + aStrScheme = INET_FTP_SCHEME; + } + else if ( rStrURL.startsWithIgnoreAsciiCase( INET_MAILTO_SCHEME ) ) + { + aStrScheme = INET_MAILTO_SCHEME; + } + } + else + aStrScheme = INetURLObject::GetScheme( aProtocol ); + return aStrScheme; +} + +void SvxHyperlinkTabPageBase::GetDataFromCommonFields( OUString& aStrName, + OUString& aStrIntName, OUString& aStrFrame, + SvxLinkInsertMode& eMode ) +{ + aStrIntName = mxEdText->get_text(); + aStrName = mxEdIndication->get_text(); + aStrFrame = mxCbbFrame->get_active_text(); + + sal_Int32 nPos = mxLbForm->get_active(); + if (nPos == -1) + // This happens when FillStandardDlgFields() hides mpLbForm. + nPos = 0; + eMode = static_cast(nPos + 1); + + // Ask dialog whether the current doc is a HTML-doc + if (mpDialog->IsHTMLDoc()) + eMode = static_cast( sal_uInt16(eMode) | HLINK_HTMLMODE ); +} + +// reset dialog-fields +void SvxHyperlinkTabPageBase::Reset( const SfxItemSet& rItemSet) +{ + + // Set dialog-fields from create-itemset + maStrInitURL.clear(); + + const SvxHyperlinkItem *pHyperlinkItem = + rItemSet.GetItem (SID_HYPERLINK_GETLINK); + + if ( pHyperlinkItem ) + { + // tdf#146576 - propose clipboard content when inserting a hyperlink + OUString aStrURL(pHyperlinkItem->GetURL()); + // Store initial URL + maStrInitURL = aStrURL; + if (aStrURL.isEmpty()) + { + if (auto xClipboard = GetSystemClipboard()) + { + if (auto xTransferable = xClipboard->getContents()) + { + css::datatransfer::DataFlavor aFlavor; + SotExchange::GetFormatDataFlavor(SotClipboardFormatId::STRING, aFlavor); + if (xTransferable->isDataFlavorSupported(aFlavor)) + { + OUString aClipBoardConentent; + try + { + if (xTransferable->getTransferData(aFlavor) >>= aClipBoardConentent) + { + INetURLObject aURL; + aURL.SetSmartURL(aClipBoardConentent); + if (!aURL.HasError()) + aStrURL + = aURL.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous); + } + } + // tdf#158345: Opening Hyperlink dialog leads to crash + // MimeType = "text/plain;charset=utf-16" + catch(const css::datatransfer::UnsupportedFlavorException&) + { + } + } + } + } + } + + // set dialog-fields + FillStandardDlgFields (pHyperlinkItem); + + // set all other fields + FillDlgFields(aStrURL); + } +} + +// Fill output-ItemSet +bool SvxHyperlinkTabPageBase::FillItemSet( SfxItemSet* rOut) +{ + OUString aStrURL, aStrName, aStrIntName, aStrFrame; + SvxLinkInsertMode eMode; + + GetCurrentItemData ( aStrURL, aStrName, aStrIntName, aStrFrame, eMode); + if ( aStrName.isEmpty() ) //automatically create a visible name if the link is created without name + aStrName = CreateUiNameFromURL(aStrURL); + + HyperDialogEvent nEvents = GetMacroEvents(); + SvxMacroTableDtor* pTable = GetMacroTable(); + + SvxHyperlinkItem aItem( SID_HYPERLINK_SETLINK, aStrName, aStrURL, aStrFrame, + aStrIntName, eMode, nEvents, pTable ); + rOut->Put (aItem); + + return true; +} + +// Activate / Deactivate Tabpage +void SvxHyperlinkTabPageBase::ActivatePage( const SfxItemSet& rItemSet ) +{ + + // Set dialog-fields from input-itemset + const SvxHyperlinkItem *pHyperlinkItem = + rItemSet.GetItem (SID_HYPERLINK_GETLINK); + + if ( pHyperlinkItem ) + { + // standard-fields + FillStandardDlgFields (pHyperlinkItem); + } + + // show mark-window if it was open before + if ( ShouldOpenMarkWnd () ) + ShowMarkWnd (); +} + +DeactivateRC SvxHyperlinkTabPageBase::DeactivatePage( SfxItemSet* _pSet) +{ + // hide mark-wnd + SetMarkWndShouldOpen( IsMarkWndVisible () ); + HideMarkWnd (); + + // retrieve data of dialog + OUString aStrURL, aStrName, aStrIntName, aStrFrame; + SvxLinkInsertMode eMode; + + GetCurrentItemData ( aStrURL, aStrName, aStrIntName, aStrFrame, eMode); + + HyperDialogEvent nEvents = GetMacroEvents(); + SvxMacroTableDtor* pTable = GetMacroTable(); + + if( _pSet ) + { + SvxHyperlinkItem aItem( SID_HYPERLINK_GETLINK, aStrName, aStrURL, aStrFrame, + aStrIntName, eMode, nEvents, pTable ); + _pSet->Put( aItem ); + } + + return DeactivateRC::LeavePage; +} + +bool SvxHyperlinkTabPageBase::ShouldOpenMarkWnd() +{ + return false; +} + +void SvxHyperlinkTabPageBase::SetMarkWndShouldOpen(bool) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/hyphen.cxx b/cui/source/dialogs/hyphen.cxx new file mode 100644 index 0000000000..10ad1d9bba --- /dev/null +++ b/cui/source/dialogs/hyphen.cxx @@ -0,0 +1,474 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define HYPH_POS_CHAR '=' + +#define CUR_HYPH_POS_CHAR '-' + +using namespace css; + +IMPL_LINK_NOARG(SvxHyphenWordDialog, CursorChangeHdl_Impl, weld::Entry&, void) +{ + int nStart, nEnd; + m_xWordEdit->get_selection_bounds(nStart, nEnd); + if (nStart == m_nOldPos && nEnd == m_nOldPos + 1) + return; + bool bReSelect; + if (nStart <= m_nOldPos) + bReSelect = !SelLeft(); + else + bReSelect = !SelRight(); + if (bReSelect) + select_region(m_nOldPos, m_nOldPos + 1); +} + +void SvxHyphenWordDialog::EnableLRBtn_Impl() +{ + const sal_Int32 nLen = m_aEditWord.getLength(); + + m_xRightBtn->set_sensitive(false); + for ( sal_Int32 i = m_nOldPos + 2; i < nLen; ++i ) + { + if ( m_aEditWord[ i ] == sal_Unicode( HYPH_POS_CHAR ) ) + { + m_xRightBtn->set_sensitive(true); + break; + } + } + + DBG_ASSERT(m_nOldPos < nLen, "nOldPos out of range"); + if (m_nOldPos >= nLen) + m_nOldPos = nLen - 1; + m_xLeftBtn->set_sensitive(false); + for ( sal_Int32 i = m_nOldPos; i-- > 0; ) + { + if ( m_aEditWord[ i ] == sal_Unicode( HYPH_POS_CHAR ) ) + { + m_xLeftBtn->set_sensitive(true); + break; + } + } +} + +OUString SvxHyphenWordDialog::EraseUnusableHyphens_Impl() +{ + // returns a String showing only those hyphen positions which will result + // in a line break if hyphenation is done there + // 1) we will need to discard all hyphenation positions at the end that + // will not result in a line break where the text to the left still fits + // on the line. + // 2) since as from OOo 3.2 '-' are part of a word and thus text like + // 'multi-line-editor' is regarded as single word we also need to discard those + // hyphenation positions to the left of the rightmost '-' that is still left of + // the rightmost valid hyphenation position according to 1) + + // Example: + // If the possible hyphenation position in 'multi-line-editor' are to be marked + // by '=' then the text will look like this: 'mul=ti-line-ed=it=or'. + // If now the first line is only large enough for 'multi-line-edi' we need to discard + // the last possible hyphenation point because of 1). The right most valid + // hyphenation position is "ed=itor". The first '-' left of this position is + // "line-ed", thus because of 2) we now need to discard all possible hyphenation + // positions to the left of that as well. Thus in the end leaving us with just + // 'multi-line-ed=itor' as return value for this function. (Just one valid hyphenation + // position for the user to choose from. However ALL the '-' characters in the word + // will ALWAYS be valid implicit hyphenation positions for the core to choose from! + // And thus even if this word is skipped in the hyphenation dialog it will still be broken + // right after 'multi-line-' (actually it might already be broken up that way before + // the hyphenation dialog is called!). + // Thus rule 2) just eliminates those positions which will not be used by the core at all + // even if the user were to select one of them. + + OUString aTxt; + DBG_ASSERT(m_xPossHyph.is(), "missing possible hyphens"); + if (m_xPossHyph.is()) + { + DBG_ASSERT( m_aActWord == m_xPossHyph->getWord(), "word mismatch" ); + + aTxt = m_xPossHyph->getPossibleHyphens(); + + m_nHyphenationPositionsOffset = 0; + uno::Sequence< sal_Int16 > aHyphenationPositions( + m_xPossHyph->getHyphenationPositions() ); + sal_Int32 nLen = aHyphenationPositions.getLength(); + const sal_Int16 *pHyphenationPos = aHyphenationPositions.getConstArray(); + + // find position nIdx after which all hyphen positions are unusable + sal_Int32 nIdx = -1; + sal_Int32 nPos = 0, nPos1 = 0; + if (nLen) + { + sal_Int32 nStart = 0; + for (sal_Int32 i = 0; i < nLen; ++i) + { + if (pHyphenationPos[i] > m_nMaxHyphenationPos) + break; + else + { + // find corresponding hyphen positions in string + nPos = aTxt.indexOf( sal_Unicode( HYPH_POS_CHAR ), nStart ); + + if (nPos == -1) + break; + else + { + nIdx = nPos; + nStart = nPos + 1; + } + } + } + } + DBG_ASSERT(nIdx != -1, "no usable hyphenation position"); + + // 1) remove all not usable hyphenation positions from the end of the string + nPos = nIdx == -1 ? 0 : nIdx + 1; + nPos1 = nPos; //save for later use in 2) below + const OUString aTmp( sal_Unicode( HYPH_POS_CHAR ) ); + while (nPos != -1) + { + nPos++; + aTxt = aTxt.replaceFirst( aTmp, "", &nPos); + } + + // 2) remove all hyphenation positions from the start that are not considered by the core + const std::u16string_view aSearchRange( aTxt.subView( 0, nPos1 ) ); + size_t nPos2 = aSearchRange.rfind( '-' ); // the '-' position the core will use by default + if (nPos2 != std::u16string_view::npos && nPos2 != 0) + { + OUString aLeft( aSearchRange.substr( 0, nPos2 ) ); + nPos = 0; + while (nPos != -1) + { + nPos++; + aLeft = aLeft.replaceFirst( aTmp, "", &nPos ); + if (nPos != -1) + ++m_nHyphenationPositionsOffset; + if (nPos >= aLeft.getLength()) // tdf#158837 + break; + } + aTxt = aTxt.replaceAt( 0, nPos2, aLeft ); + } + } + return aTxt; +} + +void SvxHyphenWordDialog::InitControls_Impl() +{ + m_xPossHyph = nullptr; + if (m_xHyphenator.is()) + { + lang::Locale aLocale( LanguageTag::convertToLocale(m_nActLanguage) ); + m_xPossHyph = m_xHyphenator->createPossibleHyphens( m_aActWord, aLocale, + uno::Sequence< beans::PropertyValue >() ); + if (m_xPossHyph.is()) + m_aEditWord = EraseUnusableHyphens_Impl(); + } + m_xWordEdit->set_text(m_aEditWord); + + m_nOldPos = m_aEditWord.getLength(); + SelLeft(); + EnableLRBtn_Impl(); +} + +void SvxHyphenWordDialog::ContinueHyph_Impl( sal_Int32 nInsPos ) +{ + if ( nInsPos >= 0 && m_xPossHyph.is() ) + { + if (nInsPos) + { + DBG_ASSERT(nInsPos <= m_aEditWord.getLength() - 2, "wrong hyphen position"); + + sal_Int32 nIdxPos = -1; + for (sal_Int32 i = 0; i <= nInsPos; ++i) + { + if (HYPH_POS_CHAR == m_aEditWord[ i ]) + nIdxPos++; + } + // take the possible hyphenation positions that got removed from the + // start of the word into account: + nIdxPos += m_nHyphenationPositionsOffset; + + uno::Sequence< sal_Int16 > aSeq = m_xPossHyph->getHyphenationPositions(); + sal_Int32 nLen = aSeq.getLength(); + DBG_ASSERT(nLen, "empty sequence"); + DBG_ASSERT(0 <= nIdxPos && nIdxPos < nLen, "index out of range"); + if (nLen && 0 <= nIdxPos && nIdxPos < nLen) + { + nInsPos = aSeq.getConstArray()[ nIdxPos ]; + m_pHyphWrapper->InsertHyphen( nInsPos ); + } + } + else + { + //! calling with 0 as argument will remove hyphens! + m_pHyphWrapper->InsertHyphen( nInsPos ); + } + } + + if ( m_pHyphWrapper->FindSpellError() ) + { + uno::Reference< linguistic2::XHyphenatedWord > xHyphWord( m_pHyphWrapper->GetLast(), uno::UNO_QUERY ); + + // adapt actual word and language to new found hyphenation result + if(xHyphWord.is()) + { + m_aActWord = xHyphWord->getWord(); + m_nActLanguage = LanguageTag( xHyphWord->getLocale() ).getLanguageType(); + m_nMaxHyphenationPos = xHyphWord->getHyphenationPos(); + InitControls_Impl(); + SetWindowTitle( m_nActLanguage ); + } + } + else + { + m_xCloseBtn->set_sensitive(false); + m_xDialog->response(RET_OK); + } +} + +bool SvxHyphenWordDialog::SelLeft() +{ + bool bRet = false; + DBG_ASSERT( m_nOldPos > 0, "invalid hyphenation position" ); + if (m_nOldPos > 0) + { + OUString aTxt( m_aEditWord ); + for( sal_Int32 i = m_nOldPos - 1; i > 0; --i ) + { + DBG_ASSERT(i <= aTxt.getLength(), "index out of range"); + if (aTxt[ i ] == sal_Unicode( HYPH_POS_CHAR )) + { + aTxt = aTxt.replaceAt( i, 1, rtl::OUStringChar( CUR_HYPH_POS_CHAR ) ); + + m_nOldPos = i; + m_xWordEdit->set_text(aTxt); + select_region(i, i + 1); + m_xWordEdit->grab_focus(); + bRet = true; + break; + } + } + EnableLRBtn_Impl(); + } + return bRet; +} + +bool SvxHyphenWordDialog::SelRight() +{ + bool bRet = false; + OUString aTxt( m_aEditWord ); + for ( sal_Int32 i = m_nOldPos + 1; i < aTxt.getLength(); ++i ) + { + if (aTxt[ i ] == sal_Unicode( HYPH_POS_CHAR )) + { + aTxt = aTxt.replaceAt( i, 1, rtl::OUStringChar( CUR_HYPH_POS_CHAR ) ); + + m_nOldPos = i; + m_xWordEdit->set_text(aTxt); + select_region(i, i + 1); + m_xWordEdit->grab_focus(); + bRet = true; + break; + } + } + EnableLRBtn_Impl(); + return bRet; +} + +IMPL_LINK_NOARG(SvxHyphenWordDialog, CutHdl_Impl, weld::Button&, void) +{ + if( !m_bBusy ) + { + m_bBusy = true; + ContinueHyph_Impl( /*m_nHyphPos*/m_nOldPos ); + m_bBusy = false; + } +} + +IMPL_LINK_NOARG(SvxHyphenWordDialog, HyphenateAllHdl_Impl, weld::Button&, void) +{ + if( m_bBusy ) + return; + + try + { + uno::Reference< linguistic2::XLinguProperties > xProp( LinguMgr::GetLinguPropertySet() ); + + xProp->setIsHyphAuto( true ); + + m_bBusy = true; + ContinueHyph_Impl( /*m_nHyphPos*/m_nOldPos ); + m_bBusy = false; + + xProp->setIsHyphAuto( false ); + } + catch (uno::Exception &) + { + SAL_WARN( "cui.dialogs", "Hyphenate All failed" ); + } +} + +IMPL_LINK_NOARG(SvxHyphenWordDialog, DeleteHdl_Impl, weld::Button&, void) +{ + if( !m_bBusy ) + { + m_bBusy = true; + ContinueHyph_Impl( 0 ); + m_bBusy = false; + } +} + +IMPL_LINK_NOARG(SvxHyphenWordDialog, ContinueHdl_Impl, weld::Button&, void) +{ + if( !m_bBusy ) + { + m_bBusy = true; + ContinueHyph_Impl(); + m_bBusy = false; + } +} + +IMPL_LINK_NOARG(SvxHyphenWordDialog, CancelHdl_Impl, weld::Button&, void) +{ + if( !m_bBusy ) + { + m_bBusy = true; + m_xDialog->response(RET_CANCEL); + m_bBusy = false; + } +} + +IMPL_LINK_NOARG(SvxHyphenWordDialog, Left_Impl, weld::Button&, void) +{ + if( !m_bBusy ) + { + m_bBusy = true; + SelLeft(); + m_bBusy = false; + } +} + +IMPL_LINK_NOARG(SvxHyphenWordDialog, Right_Impl, weld::Button&, void) +{ + if( !m_bBusy ) + { + m_bBusy = true; + SelRight(); + m_bBusy = false; + } +} + +void SvxHyphenWordDialog::select_region(int nStart, int nEnd) +{ + int nScrollPos = nStart + m_nWordEditWidth/2; + if (nScrollPos > m_aEditWord.getLength()) + nScrollPos = m_aEditWord.getLength() - m_nWordEditWidth/2; + if (nScrollPos < 0) + nScrollPos = 0; + m_xWordEdit->set_position(nScrollPos); + m_xWordEdit->select_region(nStart, nEnd); +} + +IMPL_LINK_NOARG(SvxHyphenWordDialog, GetFocusHdl_Impl, weld::Widget&, void) +{ + select_region(m_nOldPos, m_nOldPos + 1); +} + +// class SvxHyphenWordDialog --------------------------------------------- + +SvxHyphenWordDialog::SvxHyphenWordDialog( + OUString aWord, LanguageType nLang, + weld::Widget* pParent, + uno::Reference< linguistic2::XHyphenator > const &xHyphen, + SvxSpellWrapper* pWrapper) + : SfxDialogController(pParent, "cui/ui/hyphenate.ui", "HyphenateDialog") + , m_pHyphWrapper(pWrapper) + , m_aActWord(std::move(aWord)) + , m_nActLanguage(nLang) + , m_nMaxHyphenationPos(0) + , m_nOldPos(0) + , m_nHyphenationPositionsOffset(0) + , m_bBusy(false) + , m_xWordEdit(m_xBuilder->weld_entry("worded")) + , m_xLeftBtn(m_xBuilder->weld_button("left")) + , m_xRightBtn(m_xBuilder->weld_button("right")) + , m_xOkBtn(m_xBuilder->weld_button("ok")) + , m_xContBtn(m_xBuilder->weld_button("continue")) + , m_xDelBtn(m_xBuilder->weld_button("delete")) + , m_xHyphAll(m_xBuilder->weld_button("hyphall")) + , m_xCloseBtn(m_xBuilder->weld_button("close")) +{ + m_nWordEditWidth = m_xWordEdit->get_width_chars(); + m_aLabel = m_xDialog->get_title(); + m_xHyphenator = xHyphen; + + uno::Reference< linguistic2::XHyphenatedWord > xHyphWord( m_pHyphWrapper ? + m_pHyphWrapper->GetLast() : nullptr, uno::UNO_QUERY ); + DBG_ASSERT( xHyphWord.is(), "hyphenation result missing" ); + if (xHyphWord.is()) + { + DBG_ASSERT( m_aActWord == xHyphWord->getWord(), "word mismatch" ); + DBG_ASSERT( m_nActLanguage == LanguageTag( xHyphWord->getLocale() ).getLanguageType(), "language mismatch" ); + m_nMaxHyphenationPos = xHyphWord->getHyphenationPos(); + } + + InitControls_Impl(); + m_xWordEdit->grab_focus(); + + m_xLeftBtn->connect_clicked( LINK( this, SvxHyphenWordDialog, Left_Impl ) ); + m_xRightBtn->connect_clicked( LINK( this, SvxHyphenWordDialog, Right_Impl ) ); + m_xOkBtn->connect_clicked( LINK( this, SvxHyphenWordDialog, CutHdl_Impl ) ); + m_xContBtn->connect_clicked( LINK( this, SvxHyphenWordDialog, ContinueHdl_Impl ) ); + m_xDelBtn->connect_clicked( LINK( this, SvxHyphenWordDialog, DeleteHdl_Impl ) ); + m_xHyphAll->connect_clicked( LINK( this, SvxHyphenWordDialog, HyphenateAllHdl_Impl ) ); + m_xCloseBtn->connect_clicked( LINK( this, SvxHyphenWordDialog, CancelHdl_Impl ) ); + m_xWordEdit->connect_focus_in( LINK( this, SvxHyphenWordDialog, GetFocusHdl_Impl ) ); + m_xWordEdit->connect_cursor_position( LINK( this, SvxHyphenWordDialog, CursorChangeHdl_Impl ) ); + + SetWindowTitle( nLang ); + + // disable controls if service is not available + if (!m_xHyphenator.is()) + m_xDialog->set_sensitive(false); +} + +SvxHyphenWordDialog::~SvxHyphenWordDialog() +{ + if (m_xCloseBtn->get_sensitive()) + m_pHyphWrapper->SpellEnd(); +} + +void SvxHyphenWordDialog::SetWindowTitle(LanguageType nLang) +{ + m_xDialog->set_title(m_aLabel + " (" + SvtLanguageTable::GetLanguageString(nLang) + ")"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/iconcdlg.cxx b/cui/source/dialogs/iconcdlg.cxx new file mode 100644 index 0000000000..9bda9d215d --- /dev/null +++ b/cui/source/dialogs/iconcdlg.cxx @@ -0,0 +1,313 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include + +/********************************************************************** +| +| Ctor / Dtor +| +\**********************************************************************/ + +IconChoicePage::IconChoicePage(weld::Container* pParent, + const OUString& rUIXMLDescription, const OUString& rID, + const SfxItemSet* pItemSet) + : xBuilder(Application::CreateBuilder(pParent, rUIXMLDescription)) + , xContainer(xBuilder->weld_container(rID)) + , pSet(pItemSet) + , bHasExchangeSupport(false) +{ +} + +IconChoicePage::~IconChoicePage() +{ +} + +/********************************************************************** +| +| Activate / Deactivate +| +\**********************************************************************/ + +void IconChoicePage::ActivatePage( const SfxItemSet& ) +{ +} + + +DeactivateRC IconChoicePage::DeactivatePage( SfxItemSet* ) +{ + return DeactivateRC::LeavePage; +} + +bool IconChoicePage::QueryClose() +{ + return true; +} + +/********************************************************************** +| +| add new page +| +\**********************************************************************/ +void SvxHpLinkDlg::AddTabPage(const OUString& rId, CreatePage pCreateFunc /* != 0 */) +{ + weld::Container* pPage = m_xIconCtrl->get_page(rId); + maPageList.emplace_back(new IconChoicePageData(rId, pCreateFunc(pPage, this, pSet))); + maPageList.back()->xPage->Reset(*pSet); + PageCreated(*maPageList.back()->xPage); +} + +/********************************************************************** +| +| Show / Hide page or button +| +\**********************************************************************/ +void SvxHpLinkDlg::ShowPage(const OUString& rId) +{ + OUString sOldPageId = GetCurPageId(); + bool bInvalidate = sOldPageId != rId; + if (bInvalidate) + { + IconChoicePageData* pOldData = GetPageData(sOldPageId); + if (pOldData && pOldData->xPage) + { + DeActivatePageImpl(); + } + } + SetCurPageId(rId); + ActivatePageImpl(); +} + +/********************************************************************** +| +| select a page +| +\**********************************************************************/ +IMPL_LINK(SvxHpLinkDlg, ChosePageHdl_Impl, const OUString&, rId, void) +{ + if (rId != msCurrentPageId) + { + ShowPage(rId); + } +} + +/********************************************************************** +| +| Button-handler +| +\**********************************************************************/ +IMPL_LINK_NOARG(SvxHpLinkDlg, ResetHdl, weld::Button&, void) +{ + ResetPageImpl (); + + IconChoicePageData* pData = GetPageData ( msCurrentPageId ); + assert( pData && "ID not known " ); + + pData->xPage->Reset( *pSet ); +} + +/********************************************************************** +| +| call page +| +\**********************************************************************/ +void SvxHpLinkDlg::ActivatePageImpl() +{ + assert( !maPageList.empty() && "no Pages registered " ); + IconChoicePageData* pData = GetPageData ( msCurrentPageId ); + assert( pData && "ID not known " ); + + if ( pData->bRefresh ) + { + pData->xPage->Reset( *pSet ); + pData->bRefresh = false; + } + + if ( pExampleSet ) + pData->xPage->ActivatePage( *pExampleSet ); + m_xDialog->set_help_id(pData->xPage->GetHelpId()); + + // tdf#90496 - remember last used view in hyperlink dialog + msRememberedPageId = msCurrentPageId; + + if (!comphelper::LibreOfficeKit::isActive()) + m_xResetBtn->show(); +} + +void SvxHpLinkDlg::DeActivatePageImpl () +{ + IconChoicePageData *pData = GetPageData ( msCurrentPageId ); + + DeactivateRC nRet = DeactivateRC::LeavePage; + + if ( !pData ) + return; + + IconChoicePage * pPage = pData->xPage.get(); + + if ( !pExampleSet && pPage->HasExchangeSupport() && pSet ) + pExampleSet.reset(new SfxItemSet( *pSet->GetPool(), pSet->GetRanges() )); + + if ( pSet ) + { + SfxItemSet aTmp( *pSet->GetPool(), pSet->GetRanges() ); + + if ( pPage->HasExchangeSupport() ) + nRet = pPage->DeactivatePage( &aTmp ); + + if ( ( DeactivateRC::LeavePage & nRet ) && + aTmp.Count() ) + { + if (pExampleSet) + pExampleSet->Put(aTmp); + pOutSet->Put( aTmp ); + } + } + else + { + if ( pPage->HasExchangeSupport() ) //!!! + { + if ( !pExampleSet ) + { + SfxItemPool* pPool = pPage->GetItemSet().GetPool(); + pExampleSet.reset( + new SfxItemSet( *pPool, GetInputRanges( *pPool ) ) ); + } + nRet = pPage->DeactivatePage( pExampleSet.get() ); + } + else + nRet = pPage->DeactivatePage( nullptr ); + } + + if ( nRet & DeactivateRC::RefreshSet ) + { + // TODO refresh input set + // flag all pages to be newly initialized + for (auto & pObj : maPageList) + { + if ( pObj->xPage.get() != pPage ) + pObj->bRefresh = true; + else + pObj->bRefresh = false; + } + } +} + + +void SvxHpLinkDlg::ResetPageImpl () +{ + IconChoicePageData *pData = GetPageData ( msCurrentPageId ); + + assert( pData && "ID not known " ); + + pData->xPage->Reset( *pSet ); +} + +/********************************************************************** +| +| handling itemsets +| +\**********************************************************************/ + +WhichRangesContainer SvxHpLinkDlg::GetInputRanges( const SfxItemPool& ) +{ + if ( pSet ) + { + SAL_WARN( "cui.dialogs", "Set does already exist!" ); + return pSet->GetRanges(); + } + + if ( !pRanges.empty() ) + return pRanges; + + return WhichRangesContainer(); +} + + +void SvxHpLinkDlg::SetInputSet( const SfxItemSet* pInSet ) +{ + bool bSet = ( pSet != nullptr ); + + pSet = pInSet; + + if ( !bSet && !pExampleSet && !pOutSet ) + { + pExampleSet.reset(new SfxItemSet( *pSet )); + pOutSet.reset(new SfxItemSet( *pSet->GetPool(), pSet->GetRanges() )); + } +} + +bool SvxHpLinkDlg::QueryClose() +{ + bool bRet = true; + for (auto & pData : maPageList) + { + if ( pData->xPage && !pData->xPage->QueryClose() ) + { + bRet = false; + break; + } + } + return bRet; +} + +void SvxHpLinkDlg::Start() +{ + SwitchPage(msCurrentPageId); + ActivatePageImpl(); +} + +/********************************************************************** +| +| tool-methods +| +\**********************************************************************/ + +IconChoicePageData* SvxHpLinkDlg::GetPageData ( std::u16string_view rId ) +{ + IconChoicePageData *pRet = nullptr; + for (const auto & pData : maPageList) + { + if ( pData->sId == rId ) + { + pRet = pData.get(); + break; + } + } + return pRet; +} + +/********************************************************************** +| +| OK-Status +| +\**********************************************************************/ + +void SvxHpLinkDlg::SwitchPage( const OUString& rId ) +{ + m_xIconCtrl->set_current_page(rId); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/insdlg.cxx b/cui/source/dialogs/insdlg.cxx new file mode 100644 index 0000000000..3cb50c2a60 --- /dev/null +++ b/cui/source/dialogs/insdlg.cxx @@ -0,0 +1,633 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui::dialogs; + +bool InsertObjectDialog_Impl::IsCreateNew() const +{ + return false; +} + +uno::Reference< io::XInputStream > InsertObjectDialog_Impl::GetIconIfIconified( OUString* /*pGraphicMediaType*/ ) +{ + return uno::Reference< io::XInputStream >(); +} + +InsertObjectDialog_Impl::InsertObjectDialog_Impl(weld::Window* pParent, + const OUString& rUIXMLDescription, const OUString& rID, + css::uno::Reference < css::embed::XStorage > xStorage) + : GenericDialogController(pParent, rUIXMLDescription, rID) + , m_xStorage(std::move( xStorage )) + , aCnt( m_xStorage ) +{ +} + +IMPL_LINK_NOARG(SvInsertOleDlg, DoubleClickHdl, weld::TreeView&, bool) +{ + m_xDialog->response(RET_OK); + return true; +} + +IMPL_LINK_NOARG(SvInsertOleDlg, BrowseHdl, weld::Button&, void) +{ + sfx2::FileDialogHelper aHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, m_xDialog.get()); + aHelper.SetContext(sfx2::FileDialogHelper::InsertOLE); + const Reference< XFilePicker3 >& xFilePicker = aHelper.GetFilePicker(); + + // add filter + try + { + xFilePicker->appendFilter(CuiResId(RID_CUISTR_FILTER_ALL), "*.*"); + } + catch( const IllegalArgumentException& ) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "caught IllegalArgumentException when registering filter" ); + } + + if( xFilePicker->execute() == ExecutableDialogResults::OK ) + { + Sequence< OUString > aPathSeq( xFilePicker->getSelectedFiles() ); + INetURLObject aObj( aPathSeq[0] ); + m_xEdFilepath->set_text(aObj.PathToFileName()); + } +} + +IMPL_LINK(SvInsertOleDlg, RadioHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + + if (m_xRbNewObject->get_active()) + { + m_xObjectTypeFrame->show(); + m_xFileFrame->hide(); + } + else + { + m_xFileFrame->show(); + m_xObjectTypeFrame->hide(); + } +} + +SvInsertOleDlg::SvInsertOleDlg(weld::Window* pParent, const Reference& xStorage, + const SvObjectServerList* pServers) + : InsertObjectDialog_Impl( pParent, "cui/ui/insertoleobject.ui", "InsertOLEObjectDialog", xStorage) + , m_pServers( pServers ) + , m_xRbNewObject(m_xBuilder->weld_radio_button("createnew")) + , m_xRbObjectFromfile(m_xBuilder->weld_radio_button("createfromfile")) + , m_xObjectTypeFrame(m_xBuilder->weld_frame("objecttypeframe")) + , m_xLbObjecttype(m_xBuilder->weld_tree_view("types")) + , m_xFileFrame(m_xBuilder->weld_frame("fileframe")) + , m_xEdFilepath(m_xBuilder->weld_entry("urled")) + , m_xBtnFilepath(m_xBuilder->weld_button("urlbtn")) + , m_xCbFilelink(m_xBuilder->weld_check_button("linktofile")) + , m_xCbAsIcon(m_xBuilder->weld_check_button("asicon")) +{ + m_xLbObjecttype->set_size_request(m_xLbObjecttype->get_approximate_digit_width() * 32, + m_xLbObjecttype->get_height_rows(6)); + m_xLbObjecttype->connect_row_activated(LINK(this, SvInsertOleDlg, DoubleClickHdl)); + m_xBtnFilepath->connect_clicked(LINK( this, SvInsertOleDlg, BrowseHdl)); + Link aLink( LINK( this, SvInsertOleDlg, RadioHdl ) ); + m_xRbNewObject->connect_toggled( aLink ); + m_xRbObjectFromfile->connect_toggled( aLink ); + m_xRbNewObject->set_active(true); +} + +short SvInsertOleDlg::run() +{ + short nRet = RET_OK; + SvObjectServerList aObjS; + if ( !m_pServers ) + { + // if no list was provided, take the complete one + aObjS.FillInsertObjects(); + m_pServers = &aObjS; + } + + // fill listbox and select default + m_xLbObjecttype->freeze(); + for ( size_t i = 0; i < m_pServers->Count(); i++ ) + m_xLbObjecttype->append_text((*m_pServers)[i].GetHumanName()); + m_xLbObjecttype->thaw(); + m_xLbObjecttype->select(0); + + DBG_ASSERT( m_xStorage.is(), "No storage!"); + if ( m_xStorage.is() && ( nRet = InsertObjectDialog_Impl::run() ) == RET_OK ) + { + OUString aFileName; + OUString aName; + bool bCreateNew = IsCreateNew(); + if ( bCreateNew ) + { + // create and insert new embedded object + OUString aServerName = m_xLbObjecttype->get_selected_text(); + const SvObjectServer* pS = m_pServers->Get( aServerName ); + if ( pS ) + { + if( pS->GetClassName() == SvGlobalName( SO3_OUT_CLASSID ) ) + { + try + { + uno::Reference < embed::XInsertObjectDialog > xDialogCreator( + embed::MSOLEObjectSystemCreator::create( ::comphelper::getProcessComponentContext() ), + uno::UNO_QUERY ); + + if ( xDialogCreator.is() ) + { + aName = aCnt.CreateUniqueObjectName(); + + uno::Reference xProgress; + OUString aProgressText; + if (SfxViewFrame* pFrame = SfxViewFrame::Current()) + { + // Have a current frame, create a matching progressbar, but don't start it yet. + uno::Reference xFrame + = pFrame->GetFrame().GetFrameInterface(); + uno::Reference xProgressFactory( + xFrame, uno::UNO_QUERY); + if (xProgressFactory.is()) + { + xProgress = xProgressFactory->createStatusIndicator(); + if (xProgress) + { + aProgressText = CuiResId(RID_CUISTR_OLE_INSERT); + } + } + } + + const embed::InsertedObjectInfo aNewInf = xDialogCreator->createInstanceByDialog( + m_xStorage, + aName, + {comphelper::makePropertyValue("StatusIndicator", xProgress), + comphelper::makePropertyValue("StatusIndicatorText", aProgressText)} ); + + OSL_ENSURE( aNewInf.Object.is(), "The object must be created or an exception must be thrown!" ); + m_xObj = aNewInf.Object; + for ( const auto& opt : aNewInf.Options ) + if ( opt.Name == "Icon" ) + { + opt.Value >>= m_aIconMetaFile; + } + else if ( opt.Name == "IconFormat" ) + { + datatransfer::DataFlavor aFlavor; + if ( opt.Value >>= aFlavor ) + m_aIconMediaType = aFlavor.MimeType; + } + + } + } + catch( ucb::CommandAbortedException& ) + { + // the user has pressed cancel + } + catch( uno::Exception& ) + { + // TODO: Error handling + } + } + else + { + // create object with desired ClassId + m_xObj = aCnt.CreateEmbeddedObject( pS->GetClassName().GetByteSequence(), aName ); + } + + if ( !m_xObj.is() ) + { + if( !aFileName.isEmpty() ) // from OLE Dialog + { + // object couldn't be created from file + // global Resource from svtools (former so3 resource) + OUString aErr(SvtResId(STR_ERROR_OBJNOCREATE_FROM_FILE)); + aErr = aErr.replaceFirst( "%", aFileName ); + + std::unique_ptr xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, aErr)); + xBox->run(); + } + else + { + // object couldn't be created + // global Resource from svtools (former so3 resource) + OUString aErr(SvtResId(STR_ERROR_OBJNOCREATE)); + aErr = aErr.replaceFirst( "%", aServerName ); + + std::unique_ptr xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, aErr)); + xBox->run(); + } + } + } + } + else + { + aFileName = m_xEdFilepath->get_text(); + INetURLObject aURL; + aURL.SetSmartProtocol( INetProtocol::File ); + aURL.SetSmartURL( aFileName ); + aFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + bool bLink = m_xCbFilelink->get_active(); + + if ( !aFileName.isEmpty() ) + { + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< task::XInteractionHandler2 > xInteraction( + task::InteractionHandler::createWithParent(xContext, nullptr) ); + + // create MediaDescriptor for file to create object from + uno::Sequence < beans::PropertyValue > aMedium{ + comphelper::makePropertyValue("URL", aFileName), + comphelper::makePropertyValue("InteractionHandler", xInteraction) + }; + + // create object from media descriptor + + uno::Reference xProgress; + if (SfxViewFrame* pFrame = SfxViewFrame::Current()) + { + // Have a current frame, create visual indication that insert is in progress. + uno::Reference xFrame = pFrame->GetFrame().GetFrameInterface(); + uno::Reference xProgressFactory(xFrame, uno::UNO_QUERY); + if (xProgressFactory.is()) + { + xProgress = xProgressFactory->createStatusIndicator(); + if (xProgress) + { + OUString aOleInsert(CuiResId(RID_CUISTR_OLE_INSERT)); + xProgress->start(aOleInsert, 100); + } + } + } + + if ( bLink ) + m_xObj = aCnt.InsertEmbeddedLink( aMedium, aName ); + else + m_xObj = aCnt.InsertEmbeddedObject( aMedium, aName ); + + if (xProgress.is()) + { + xProgress->end(); + } + } + + if ( !m_xObj.is() ) + { + // object couldn't be created from file + // global Resource from svtools (former so3 resource) + OUString aErr(SvtResId(STR_ERROR_OBJNOCREATE_FROM_FILE)); + aErr = aErr.replaceFirst( "%", aFileName ); + + std::unique_ptr xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, aErr)); + xBox->run(); + } + else + { + if (m_xCbAsIcon->get_active()) + { + //something nice here I guess would be to write the filename into + //the image with this icon above it + Image aImage = SvFileInformationManager::GetImage(aURL, true); + SvMemoryStream aTemp; + WriteDIBBitmapEx(aImage.GetBitmapEx(), aTemp); + m_aIconMetaFile = Sequence(static_cast(aTemp.GetData()), aTemp.TellEnd()); + m_aIconMediaType = "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\""; + } + } + } + } + + m_pServers = nullptr; + return nRet; +} + +uno::Reference< io::XInputStream > SvInsertOleDlg::GetIconIfIconified( OUString* pGraphicMediaType ) +{ + if ( m_aIconMetaFile.hasElements() ) + { + if ( pGraphicMediaType ) + *pGraphicMediaType = m_aIconMediaType; + + return uno::Reference< io::XInputStream >( new ::comphelper::SequenceInputStream( m_aIconMetaFile ) ); + } + + return uno::Reference< io::XInputStream >(); +} + + +SfxInsertFloatingFrameDialog::SfxInsertFloatingFrameDialog(weld::Window *pParent, + const css::uno::Reference < css::embed::XStorage >& xStorage) + : InsertObjectDialog_Impl(pParent, "cui/ui/insertfloatingframe.ui", "InsertFloatingFrameDialog", + xStorage) +{ + Init(); +} + +SfxInsertFloatingFrameDialog::SfxInsertFloatingFrameDialog(weld::Window *pParent, + const uno::Reference < embed::XEmbeddedObject >& xObj) + : InsertObjectDialog_Impl(pParent, "cui/ui/insertfloatingframe.ui", "InsertFloatingFrameDialog", + uno::Reference()) +{ + m_xObj = xObj; + + Init(); +} + +void SfxInsertFloatingFrameDialog::Init() +{ + m_xEDName = m_xBuilder->weld_entry("edname"); + m_xEDURL = m_xBuilder->weld_entry("edurl"); + m_xBTOpen = m_xBuilder->weld_button("buttonbrowse"); + m_xRBScrollingOn = m_xBuilder->weld_radio_button("scrollbaron"); + m_xRBScrollingOff = m_xBuilder->weld_radio_button("scrollbaroff"); + m_xRBScrollingAuto = m_xBuilder->weld_radio_button("scrollbarauto"); + m_xRBFrameBorderOn = m_xBuilder->weld_radio_button("borderon"); + m_xRBFrameBorderOff = m_xBuilder->weld_radio_button("borderoff"); + m_xFTMarginWidth = m_xBuilder->weld_label("widthlabel"); + m_xNMMarginWidth = m_xBuilder->weld_spin_button("width"); + m_xCBMarginWidthDefault = m_xBuilder->weld_check_button("defaultwidth"); + m_xFTMarginHeight = m_xBuilder->weld_label("heightlabel"); + m_xNMMarginHeight = m_xBuilder->weld_spin_button("height"); + m_xCBMarginHeightDefault = m_xBuilder->weld_check_button("defaultheight"); + + Link aLink(LINK(this, SfxInsertFloatingFrameDialog, CheckHdl)); + m_xCBMarginWidthDefault->connect_toggled(aLink); + m_xCBMarginHeightDefault->connect_toggled(aLink); + + m_xCBMarginWidthDefault->set_active(true); + m_xCBMarginHeightDefault->set_active(true); + m_xRBScrollingAuto->set_active(true); + m_xRBFrameBorderOn->set_active(true); + + m_xBTOpen->connect_clicked(LINK(this, SfxInsertFloatingFrameDialog, OpenHdl)); +} + +short SfxInsertFloatingFrameDialog::run() +{ + short nRet = RET_OK; + bool bOK = false; + uno::Reference < beans::XPropertySet > xSet; + if ( m_xObj.is() ) + { + try + { + if ( m_xObj->getCurrentState() == embed::EmbedStates::LOADED ) + m_xObj->changeState( embed::EmbedStates::RUNNING ); + xSet.set( m_xObj->getComponent(), uno::UNO_QUERY ); + OUString aStr; + uno::Any aAny = xSet->getPropertyValue( "FrameURL" ); + if ( aAny >>= aStr ) + m_xEDURL->set_text( aStr ); + aAny = xSet->getPropertyValue( "FrameName" ); + if ( aAny >>= aStr ) + m_xEDName->set_text(aStr); + + sal_Int32 nSize = SIZE_NOT_SET; + aAny = xSet->getPropertyValue( "FrameMarginWidth" ); + aAny >>= nSize; + + if ( nSize == SIZE_NOT_SET ) + { + m_xCBMarginWidthDefault->set_active(true); + m_xNMMarginWidth->set_text(OUString::number(DEFAULT_MARGIN_WIDTH)); + m_xFTMarginWidth->set_sensitive(false); + m_xNMMarginWidth->set_sensitive(false); + } + else + m_xNMMarginWidth->set_text(OUString::number(nSize)); + + aAny = xSet->getPropertyValue( "FrameMarginHeight" ); + aAny >>= nSize; + + if ( nSize == SIZE_NOT_SET ) + { + m_xCBMarginHeightDefault->set_active(true); + m_xNMMarginHeight->set_text(OUString::number(DEFAULT_MARGIN_HEIGHT)); + m_xFTMarginHeight->set_sensitive(false); + m_xNMMarginHeight->set_sensitive(false); + } + else + m_xNMMarginHeight->set_text(OUString::number(nSize)); + + bool bScrollOn = false; + bool bScrollOff = false; + bool bScrollAuto = false; + + bool bSet = false; + aAny = xSet->getPropertyValue( "FrameIsAutoScroll" ); + aAny >>= bSet; + if ( !bSet ) + { + aAny = xSet->getPropertyValue( "FrameIsScrollingMode" ); + aAny >>= bSet; + bScrollOn = bSet; + bScrollOff = !bSet; + } + else + bScrollAuto = true; + + m_xRBScrollingOn->set_sensitive(bScrollOn); + m_xRBScrollingOff->set_sensitive(bScrollOff); + m_xRBScrollingAuto->set_sensitive(bScrollAuto); + + bSet = false; + aAny = xSet->getPropertyValue( "FrameIsAutoBorder" ); + aAny >>= bSet; + if ( !bSet ) + { + aAny = xSet->getPropertyValue( "FrameIsBorder" ); + aAny >>= bSet; + m_xRBFrameBorderOn->set_active(bSet); + m_xRBFrameBorderOff->set_active(!bSet); + } + + bOK = true; + } + catch ( uno::Exception& ) + { + OSL_FAIL( "No IFrame!" ); + } + } + else + { + DBG_ASSERT( m_xStorage.is(), "No storage!"); + bOK = m_xStorage.is(); + } + + if (!bOK) + return RET_OK; + + nRet = InsertObjectDialog_Impl::run(); + if ( nRet == RET_OK ) + { + OUString aURL; + if (!m_xEDURL->get_text().isEmpty()) + { + // URL can be a valid and absolute URL or a system file name + INetURLObject aObj; + aObj.SetSmartProtocol( INetProtocol::File ); + if ( aObj.SetSmartURL( m_xEDURL->get_text() ) ) + aURL = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + + if ( !m_xObj.is() && !aURL.isEmpty() ) + { + // create the object + OUString aName; + SvGlobalName aClassId( SO3_IFRAME_CLASSID ); + m_xObj = aCnt.CreateEmbeddedObject( aClassId.GetByteSequence(), aName ); + if ( m_xObj->getCurrentState() == embed::EmbedStates::LOADED ) + m_xObj->changeState( embed::EmbedStates::RUNNING ); + xSet.set( m_xObj->getComponent(), uno::UNO_QUERY ); + } + + if ( m_xObj.is() ) + { + try + { + bool bIPActive = m_xObj->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE; + if ( bIPActive ) + m_xObj->changeState( embed::EmbedStates::RUNNING ); + + OUString aName = m_xEDName->get_text(); + ScrollingMode eScroll = ScrollingMode::No; + if (m_xRBScrollingOn->get_active()) + eScroll = ScrollingMode::Yes; + if (m_xRBScrollingOff->get_active()) + eScroll = ScrollingMode::No; + if (m_xRBScrollingAuto->get_active()) + eScroll = ScrollingMode::Auto; + + bool bHasBorder = m_xRBFrameBorderOn->get_active(); + + tools::Long lMarginWidth; + if (!m_xCBMarginWidthDefault->get_active()) + lMarginWidth = static_cast(m_xNMMarginWidth->get_text().toInt32()); + else + lMarginWidth = SIZE_NOT_SET; + + tools::Long lMarginHeight; + if (!m_xCBMarginHeightDefault->get_active()) + lMarginHeight = static_cast(m_xNMMarginHeight->get_text().toInt32()); + else + lMarginHeight = SIZE_NOT_SET; + + xSet->setPropertyValue( "FrameURL", Any( aURL ) ); + xSet->setPropertyValue( "FrameName", Any( aName ) ); + + if ( eScroll == ScrollingMode::Auto ) + xSet->setPropertyValue( "FrameIsAutoScroll", Any( true ) ); + else + xSet->setPropertyValue( "FrameIsScrollingMode", Any( eScroll == ScrollingMode::Yes ) ); + + xSet->setPropertyValue( "FrameIsBorder", Any( bHasBorder ) ); + xSet->setPropertyValue( "FrameMarginWidth", Any( sal_Int32( lMarginWidth ) ) ); + xSet->setPropertyValue( "FrameMarginHeight", Any( sal_Int32( lMarginHeight ) ) ); + + if ( bIPActive ) + m_xObj->changeState( embed::EmbedStates::INPLACE_ACTIVE ); + } + catch ( uno::Exception& ) + { + OSL_FAIL( "No IFrame!" ); + } + } + } + + return nRet; +} + +IMPL_LINK(SfxInsertFloatingFrameDialog, CheckHdl, weld::Toggleable&, rButton, void) +{ + weld::CheckButton& rCB = dynamic_cast(rButton); + if (&rCB == m_xCBMarginWidthDefault.get()) + { + if (rCB.get_active()) + m_xNMMarginWidth->set_text(OUString::number(DEFAULT_MARGIN_WIDTH)); + m_xFTMarginWidth->set_sensitive(!rCB.get_active()); + m_xNMMarginWidth->set_sensitive(!rCB.get_active()); + } + + if (&rCB == m_xCBMarginHeightDefault.get()) + { + if (rCB.get_active()) + m_xNMMarginHeight->set_text(OUString::number(DEFAULT_MARGIN_HEIGHT)); + m_xFTMarginHeight->set_sensitive(!rCB.get_active()); + m_xNMMarginHeight->set_sensitive(!rCB.get_active()); + } +} + +IMPL_LINK_NOARG( SfxInsertFloatingFrameDialog, OpenHdl, weld::Button&, void) +{ + // create the file dialog + sfx2::FileDialogHelper aFileDlg( + ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, OUString(), + SfxFilterFlags::NONE, SfxFilterFlags::NONE, m_xDialog.get()); + + // set the title + aFileDlg.SetTitle(CuiResId(RID_CUISTR_SELECT_FILE_IFRAME)); + + // show the dialog + if ( aFileDlg.Execute() == ERRCODE_NONE ) + m_xEDURL->set_text(INetURLObject(aFileDlg.GetPath()).GetMainURL(INetURLObject::DecodeMechanism::WithCharset)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/insrc.cxx b/cui/source/dialogs/insrc.cxx new file mode 100644 index 0000000000..1ff1a14411 --- /dev/null +++ b/cui/source/dialogs/insrc.cxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +bool SvxInsRowColDlg::isInsertBefore() const +{ + return !m_xAfterBtn->get_active(); +} + +sal_uInt16 SvxInsRowColDlg::getInsertCount() const +{ + return m_xCountEdit->get_value(); +} + +SvxInsRowColDlg::SvxInsRowColDlg(weld::Window* pParent, bool bColumn, const OUString& rHelpId) + : GenericDialogController(pParent, "cui/ui/insertrowcolumn.ui", "InsertRowColumnDialog") + , m_xCountEdit(m_xBuilder->weld_spin_button("insert_number")) + , m_xBeforeBtn(m_xBuilder->weld_radio_button("insert_before")) + , m_xAfterBtn(m_xBuilder->weld_radio_button("insert_after")) +{ + m_xDialog->set_title(bColumn ? CuiResId(RID_CUISTR_COL) : CuiResId(RID_CUISTR_ROW)); + + // tdf#119293 + if (bColumn) { + m_xBeforeBtn->set_label(CuiResId(RID_CUISTR_INSERTCOL_BEFORE)); + m_xAfterBtn->set_label(CuiResId(RID_CUISTR_INSERTCOL_AFTER)); + } else { + m_xBeforeBtn->set_label(CuiResId(RID_CUISTR_INSERTROW_BEFORE)); + m_xAfterBtn->set_label(CuiResId(RID_CUISTR_INSERTROW_AFTER)); + } + + m_xDialog->set_help_id(rHelpId); +} + +short SvxInsRowColDlg::Execute() +{ + return run(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/linkdlg.cxx b/cui/source/dialogs/linkdlg.cxx new file mode 100644 index 0000000000..b31c5d74a7 --- /dev/null +++ b/cui/source/dialogs/linkdlg.cxx @@ -0,0 +1,644 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + +using namespace sfx2; +using namespace ::com::sun::star; + +namespace { + +class SvBaseLinkMemberList { +private: + std::vector mLinks; + +public: + ~SvBaseLinkMemberList() + { + for (auto const& link : mLinks) + { + if( link ) + link->ReleaseRef(); + } + } + + size_t size() const { return mLinks.size(); } + + SvBaseLink *operator[](size_t i) const { return mLinks[i]; } + + void push_back(SvBaseLink* p) + { + mLinks.push_back(p); + p->AddFirstRef(); + } +}; + +} + +SvBaseLinksDlg::SvBaseLinksDlg(weld::Window * pParent, LinkManager* pMgr, bool bHtmlMode) + : GenericDialogController(pParent, "cui/ui/baselinksdialog.ui", "BaseLinksDialog") + , aStrAutolink( CuiResId( STR_AUTOLINK ) ) + , aStrManuallink( CuiResId( STR_MANUALLINK ) ) + , aStrBrokenlink( CuiResId( STR_BROKENLINK ) ) + , aStrCloselinkmsg( CuiResId( STR_CLOSELINKMSG ) ) + , aStrCloselinkmsgMulti( CuiResId( STR_CLOSELINKMSG_MULTI ) ) + , aStrWaitinglink( CuiResId( STR_WAITINGLINK ) ) + , pLinkMgr( nullptr ) + , aUpdateIdle("cui SvBaseLinksDlg UpdateIdle") + , m_xTbLinks(m_xBuilder->weld_tree_view("TB_LINKS")) + , m_xFtFullFileName(m_xBuilder->weld_link_button("FULL_FILE_NAME")) + , m_xFtFullSourceName(m_xBuilder->weld_label("FULL_SOURCE_NAME")) + , m_xFtFullTypeName(m_xBuilder->weld_label("FULL_TYPE_NAME")) + , m_xRbAutomatic(m_xBuilder->weld_radio_button("AUTOMATIC")) + , m_xRbManual(m_xBuilder->weld_radio_button("MANUAL")) + , m_xPbUpdateNow(m_xBuilder->weld_button("UPDATE_NOW")) + , m_xPbChangeSource(m_xBuilder->weld_button("CHANGE_SOURCE")) + , m_xPbBreakLink(m_xBuilder->weld_button("BREAK_LINK")) + , m_xVirDev(VclPtr::Create()) +{ + // expand the point size of the desired font to the equivalent pixel size + weld::SetPointFont(*m_xVirDev, m_xTbLinks->get_font()); + m_xTbLinks->set_size_request(m_xTbLinks->get_approximate_digit_width() * 90, + m_xTbLinks->get_height_rows(12)); + + m_xTbLinks->set_selection_mode(SelectionMode::Multiple); + + std::vector aWidths + { + o3tl::narrowing(m_xTbLinks->get_approximate_digit_width() * 30), + o3tl::narrowing(m_xTbLinks->get_approximate_digit_width() * 20), + o3tl::narrowing(m_xTbLinks->get_approximate_digit_width() * 20) + }; + m_xTbLinks->set_column_fixed_widths(aWidths); + + // UpdateTimer for DDE-/Grf-links, which are waited for + aUpdateIdle.SetInvokeHandler( LINK( this, SvBaseLinksDlg, UpdateWaitingHdl ) ); + aUpdateIdle.SetPriority( TaskPriority::LOWEST ); + + m_xTbLinks->connect_changed( LINK( this, SvBaseLinksDlg, LinksSelectHdl ) ); + m_xTbLinks->connect_row_activated( LINK( this, SvBaseLinksDlg, LinksDoubleClickHdl ) ); + m_xRbAutomatic->connect_toggled( LINK( this, SvBaseLinksDlg, ToggleHdl ) ); + m_xRbManual->connect_toggled( LINK( this, SvBaseLinksDlg, ToggleHdl ) ); + m_xPbUpdateNow->connect_clicked( LINK( this, SvBaseLinksDlg, UpdateNowClickHdl ) ); + m_xPbChangeSource->connect_clicked( LINK( this, SvBaseLinksDlg, ChangeSourceClickHdl ) ); + if(!bHtmlMode) + m_xPbBreakLink->connect_clicked( LINK( this, SvBaseLinksDlg, BreakLinkClickHdl ) ); + else + m_xPbBreakLink->hide(); + + SetManager( pMgr ); +} + +SvBaseLinksDlg::~SvBaseLinksDlg() +{ +} + +/************************************************************************* +|* SvBaseLinksDlg::Handler() +*************************************************************************/ +IMPL_LINK(SvBaseLinksDlg, LinksSelectHdl, weld::TreeView&, rTreeView, void) +{ + LinksSelectHdl(&rTreeView); +} + +void SvBaseLinksDlg::LinksSelectHdl(weld::TreeView* pSvTabListBox) +{ + const int nSelectionCount = pSvTabListBox ? + pSvTabListBox->count_selected_rows() : 0; + if (nSelectionCount > 1) + { + // possibly deselect old entries in case of multi-selection + int nSelEntry = pSvTabListBox->get_selected_index(); + SvBaseLink* pLink = weld::fromId(pSvTabListBox->get_id(nSelEntry)); + SvBaseLinkObjectType nObjectType = pLink->GetObjType(); + if(!isClientFileType(nObjectType)) + { + pSvTabListBox->unselect_all(); + pSvTabListBox->select(nSelEntry); + } + else + { + std::vector aRows = pSvTabListBox->get_selected_rows(); + for (auto nEntry : aRows) + { + pLink = weld::fromId(pSvTabListBox->get_id(nEntry)); + DBG_ASSERT(pLink, "Where is the Link?"); + if (!pLink) + continue; + if( !isClientFileType(pLink->GetObjType()) ) + pSvTabListBox->unselect(nEntry); + } + } + + m_xPbUpdateNow->set_sensitive(true); + m_xRbAutomatic->set_sensitive(false); + m_xRbManual->set_active(true); + m_xRbManual->set_sensitive(false); + } + else + { + int nPos; + SvBaseLink* pLink = GetSelEntry( &nPos ); + if( !pLink ) + return; + + m_xPbUpdateNow->set_sensitive(true); + + OUString sType, sLink; + OUString *pLinkNm = &sLink, *pFilter = nullptr; + + if( isClientFileType(pLink->GetObjType()) ) + { + m_xRbAutomatic->set_sensitive(false); + m_xRbManual->set_active(true); + m_xRbManual->set_sensitive(false); + if( SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() ) + { + pLinkNm = nullptr; + pFilter = &sLink; + } + } + else + { + m_xRbAutomatic->set_sensitive(true); + m_xRbManual->set_sensitive(true); + + if( SfxLinkUpdateMode::ALWAYS == pLink->GetUpdateMode() ) + m_xRbAutomatic->set_active(true); + else + m_xRbManual->set_active(true); + } + + OUString aFileName; + sfx2::LinkManager::GetDisplayNames( pLink, &sType, &aFileName, pLinkNm, pFilter ); + aFileName = INetURLObject::decode(aFileName, INetURLObject::DecodeMechanism::Unambiguous); + m_xFtFullFileName->set_label( aFileName ); + m_xFtFullFileName->set_uri( aFileName ); + m_xFtFullSourceName->set_label( sLink ); + m_xFtFullTypeName->set_label( sType ); + } +} + +IMPL_LINK_NOARG( SvBaseLinksDlg, LinksDoubleClickHdl, weld::TreeView&, bool ) +{ + ChangeSourceClickHdl(*m_xPbChangeSource); + return true; +} + +IMPL_LINK(SvBaseLinksDlg, ToggleHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + + int nPos; + SvBaseLink* pLink = GetSelEntry( &nPos ); + + if (m_xRbAutomatic->get_active()) + { + if( pLink && !isClientFileType( pLink->GetObjType() ) && + SfxLinkUpdateMode::ALWAYS != pLink->GetUpdateMode() ) + SetType( *pLink, nPos, SfxLinkUpdateMode::ALWAYS ); + } + else + { + if( pLink && !isClientFileType( pLink->GetObjType() ) && + SfxLinkUpdateMode::ONCALL != pLink->GetUpdateMode()) + SetType( *pLink, nPos, SfxLinkUpdateMode::ONCALL ); + } +} + +IMPL_LINK_NOARG(SvBaseLinksDlg, UpdateNowClickHdl, weld::Button&, void) +{ + std::vector< SvBaseLink* > aLnkArr; + std::vector< sal_Int16 > aPosArr; + + std::vector aRows = m_xTbLinks->get_selected_rows(); + for (int nFndPos : aRows) + { + aLnkArr.push_back( weld::fromId( m_xTbLinks->get_id(nFndPos) ) ); + aPosArr.push_back( nFndPos ); + } + + if( aLnkArr.empty() ) + return; + + for( size_t n = 0; n < aLnkArr.size(); ++n ) + { + tools::SvRef xLink = aLnkArr[ n ]; + + // first look for the entry in the array + for(const auto & i : pLinkMgr->GetLinks()) + if( xLink == i ) + { + SetType( *xLink, aPosArr[ n ], xLink->GetUpdateMode() ); + break; + } + } + + // if somebody is of the opinion to swap his links (SD) + LinkManager* pNewMgr = pLinkMgr; + pLinkMgr = nullptr; + SetManager( pNewMgr ); + + + OUString sId = weld::toId(aLnkArr[0]); + int nE = m_xTbLinks->find_id(sId); + if (nE == -1) + nE = m_xTbLinks->get_selected_index(); + int nSelEntry = m_xTbLinks->get_selected_index(); + if (nE != nSelEntry) + m_xTbLinks->unselect(nSelEntry); + m_xTbLinks->select(nE); + m_xTbLinks->scroll_to_row(nE); + + pNewMgr->CloseCachedComps(); +} + +IMPL_LINK_NOARG(SvBaseLinksDlg, ChangeSourceClickHdl, weld::Button&, void) +{ + std::vector aRows = m_xTbLinks->get_selected_rows(); + if (aRows.size() > 1) + { + try + { + uno::Reference xFolderPicker = sfx2::createFolderPicker( + comphelper::getProcessComponentContext(), m_xDialog.get()); + + OUString sType, sFile, sLinkName; + OUString sFilter; + SvBaseLink* pLink = weld::fromId(m_xTbLinks->get_id(aRows[0])); + sfx2::LinkManager::GetDisplayNames( pLink, &sType, &sFile ); + INetURLObject aUrl(sFile); + if(aUrl.GetProtocol() == INetProtocol::File) + { + OUString sOldPath(aUrl.PathToFileName()); + sal_Int32 nLen = aUrl.GetLastName().getLength(); + sOldPath = sOldPath.copy(0, sOldPath.getLength() - nLen); + xFolderPicker->setDisplayDirectory(sOldPath); + } + if (xFolderPicker->execute() == ui::dialogs::ExecutableDialogResults::OK) + { + OUString aPath = xFolderPicker->getDirectory(); + + for (auto nRow : aRows) + { + pLink = weld::fromId(m_xTbLinks->get_id(nRow)); + DBG_ASSERT(pLink,"Where is the link?"); + if (!pLink) + continue; + sfx2::LinkManager::GetDisplayNames( pLink, &sType, &sFile, &sLinkName, &sFilter ); + INetURLObject aUrl_(sFile); + INetURLObject aUrl2(aPath, INetProtocol::File); + aUrl2.insertName( aUrl_.getName() ); + OUString sNewLinkName; + MakeLnkName( sNewLinkName, nullptr , + aUrl2.GetMainURL(INetURLObject::DecodeMechanism::ToIUri), sLinkName, &sFilter); + pLink->SetLinkSourceName( sNewLinkName ); + pLink->Update(); + } + if( pLinkMgr->GetPersist() ) + pLinkMgr->GetPersist()->SetModified(); + LinkManager* pNewMgr = pLinkMgr; + pLinkMgr = nullptr; + SetManager( pNewMgr ); + } + } + catch (const uno::Exception &) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "SvBaseLinksDlg"); + } + } + else + { + int nPos; + SvBaseLink* pLink = GetSelEntry( &nPos ); + if ( pLink && !pLink->GetLinkSourceName().isEmpty() ) + pLink->Edit(m_xDialog.get(), LINK(this, SvBaseLinksDlg, EndEditHdl)); + } +} + +IMPL_LINK_NOARG( SvBaseLinksDlg, BreakLinkClickHdl, weld::Button&, void ) +{ + bool bModified = false; + if (m_xTbLinks->count_selected_rows() <= 1) + { + int nPos; + tools::SvRef xLink = GetSelEntry( &nPos ); + if( !xLink.is() ) + return; + + std::unique_ptr xQueryBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Question, VclButtonsType::YesNo, + aStrCloselinkmsg)); + xQueryBox->set_default_response(RET_YES); + + if (RET_YES == xQueryBox->run()) + { + m_xTbLinks->remove(nPos); + + // close object, if it's still existing + bool bNewLnkMgr = SvBaseLinkObjectType::ClientFile == xLink->GetObjType(); + + // tell the link that it will be resolved! + xLink->Closed(); + + // if somebody has forgotten to deregister himself + if( xLink.is() ) + pLinkMgr->Remove( xLink.get() ); + + if( bNewLnkMgr ) + { + LinkManager* pNewMgr = pLinkMgr; + pLinkMgr = nullptr; + SetManager( pNewMgr ); + m_xTbLinks->set_cursor(nPos ? --nPos : 0); + } + bModified = true; + } + } + else + { + std::unique_ptr xQueryBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Question, VclButtonsType::YesNo, + aStrCloselinkmsgMulti)); + xQueryBox->set_default_response(RET_YES); + + if (RET_YES == xQueryBox->run()) + { + std::vector aRows = m_xTbLinks->get_selected_rows(); + SvBaseLinkMemberList aLinkList; + for (auto nRow : aRows) + { + SvBaseLink* pLink = weld::fromId(m_xTbLinks->get_id(nRow)); + if (pLink) + aLinkList.push_back(pLink); + } + std::sort(aRows.begin(), aRows.end()); + for (auto it = aRows.rbegin(); it != aRows.rend(); ++it) + m_xTbLinks->remove(*it); + for (size_t i = 0; i < aLinkList.size(); ++i) + { + tools::SvRef xLink = aLinkList[i]; + // tell the link that it will be resolved! + xLink->Closed(); + + // if somebody has forgotten to deregister himself + pLinkMgr->Remove( xLink.get() ); + bModified = true; + } + // then remove all selected entries + } + } + if(!bModified) + return; + + if (!m_xTbLinks->n_children()) + { + m_xRbAutomatic->set_sensitive(false); + m_xRbManual->set_sensitive(false); + m_xPbUpdateNow->set_sensitive(false); + m_xPbChangeSource->set_sensitive(false); + m_xPbBreakLink->set_sensitive(false); + + m_xFtFullSourceName->set_label( "" ); + m_xFtFullTypeName->set_label( "" ); + } + if( pLinkMgr && pLinkMgr->GetPersist() ) + pLinkMgr->GetPersist()->SetModified(); +} + +IMPL_LINK_NOARG( SvBaseLinksDlg, UpdateWaitingHdl, Timer*, void ) +{ + m_xTbLinks->freeze(); + for (int nPos = m_xTbLinks->n_children(); nPos; --nPos) + { + tools::SvRef xLink( weld::fromId(m_xTbLinks->get_id(nPos)) ); + if( xLink.is() ) + { + OUString sCur( ImplGetStateStr( *xLink ) ), + sOld( m_xTbLinks->get_text(nPos, 3) ); + if( sCur != sOld ) + m_xTbLinks->set_text(nPos, sCur, 3); + } + } + m_xTbLinks->thaw(); +} + +IMPL_LINK( SvBaseLinksDlg, EndEditHdl, sfx2::SvBaseLink&, _rLink, void ) +{ + int nPos; + GetSelEntry( &nPos ); + + if( !_rLink.WasLastEditOK() ) + return; + + // StarImpress/Draw swap the LinkObjects themselves! + // So search for the link in the manager; if it does not exist + // anymore, fill the list completely new. Otherwise only the + // edited link needs to be refreshed. + bool bLinkFnd = false; + for( size_t n = pLinkMgr->GetLinks().size(); n; ) + if( &_rLink == &(*pLinkMgr->GetLinks()[ --n ]) ) + { + bLinkFnd = true; + break; + } + + if( bLinkFnd ) + { + m_xTbLinks->remove(nPos); + int nToUnselect = m_xTbLinks->get_selected_index(); + InsertEntry(_rLink, nPos, true); + if (nToUnselect != -1) + m_xTbLinks->unselect(nToUnselect); + } + else + { + LinkManager* pNewMgr = pLinkMgr; + pLinkMgr = nullptr; + SetManager( pNewMgr ); + } + if (pLinkMgr && pLinkMgr->GetPersist()) + pLinkMgr->GetPersist()->SetModified(); +} + +OUString SvBaseLinksDlg::ImplGetStateStr( const SvBaseLink& rLnk ) +{ + OUString sRet; + if( !rLnk.GetObj() ) + sRet = aStrBrokenlink; + else if( rLnk.GetObj()->IsPending() ) + { + sRet = aStrWaitinglink; + aUpdateIdle.Start(); + } + else if( SfxLinkUpdateMode::ALWAYS == rLnk.GetUpdateMode() ) + sRet = aStrAutolink; + else + sRet = aStrManuallink; + + return sRet; +} + +void SvBaseLinksDlg::SetManager( LinkManager* pNewMgr ) +{ + if( pLinkMgr == pNewMgr ) + return; + + if (pNewMgr) + { + // update has to be stopped before clear + m_xTbLinks->freeze(); + } + + m_xTbLinks->clear(); + pLinkMgr = pNewMgr; + + if( !pLinkMgr ) + return; + + SvBaseLinks& rLnks = const_cast(pLinkMgr->GetLinks()); + for( size_t n = 0; n < rLnks.size(); ++n ) + { + tools::SvRef& rLinkRef = rLnks[ n ]; + if( !rLinkRef.is() ) + { + rLnks.erase( rLnks.begin() + n ); + --n; + continue; + } + if( rLinkRef->IsVisible() ) + InsertEntry( *rLinkRef ); + } + + m_xTbLinks->thaw(); + + if( !rLnks.empty() ) + { + m_xTbLinks->set_cursor(0); + m_xTbLinks->select(0); + LinksSelectHdl( nullptr ); + } +} + +void SvBaseLinksDlg::InsertEntry(const SvBaseLink& rLink, int nPos, bool bSelect) +{ + OUString sFileNm, sLinkNm, sTypeNm, sFilter; + + sfx2::LinkManager::GetDisplayNames( &rLink, &sTypeNm, &sFileNm, &sLinkNm, &sFilter ); + + auto nWidthPixel = m_xTbLinks->get_column_width(0); + OUString aTxt = m_xVirDev->GetEllipsisString(sFileNm, nWidthPixel, DrawTextFlags::PathEllipsis); + INetURLObject aPath( sFileNm, INetProtocol::File ); + OUString aFileName = aPath.getName( + INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::Unambiguous); + + if( aFileName.getLength() > aTxt.getLength() ) + aTxt = aFileName; + else if (!aFileName.isEmpty() && aTxt.indexOf(aFileName, aTxt.getLength() - aFileName.getLength()) == -1) + // filename not in string + aTxt = aFileName; + + if (nPos == -1) + nPos = m_xTbLinks->n_children(); + m_xTbLinks->insert(nPos); + m_xTbLinks->set_text(nPos, aTxt, 0); + m_xTbLinks->set_id(nPos, weld::toId(&rLink)); + if( SvBaseLinkObjectType::ClientGraphic == rLink.GetObjType() ) + m_xTbLinks->set_text(nPos, sFilter, 1); + else + m_xTbLinks->set_text(nPos, sLinkNm, 1); + m_xTbLinks->set_text(nPos, sTypeNm, 2); + m_xTbLinks->set_text(nPos, ImplGetStateStr(rLink), 3); + if (bSelect) + m_xTbLinks->select(nPos); +} + +SvBaseLink* SvBaseLinksDlg::GetSelEntry(int* pPos) +{ + int nPos = m_xTbLinks->get_selected_index(); + if (nPos != -1) + { + if (pPos) + *pPos = nPos; + return weld::fromId(m_xTbLinks->get_id(nPos)); + } + return nullptr; +} + +void SvBaseLinksDlg::SetType(SvBaseLink& rLink, + int nSelPos, + SfxLinkUpdateMode nType) +{ + rLink.SetUpdateMode( nType ); + rLink.Update(); + m_xTbLinks->set_text(nSelPos, ImplGetStateStr(rLink), 3); + if (pLinkMgr->GetPersist()) + pLinkMgr->GetPersist()->SetModified(); +} + +void SvBaseLinksDlg::SetActLink( SvBaseLink const * pLink ) +{ + if( !pLinkMgr ) + return; + + const SvBaseLinks& rLnks = pLinkMgr->GetLinks(); + int nSelect = 0; + for(const auto & rLinkRef : rLnks) + { + // #109573# only visible links have been inserted into the TreeListBox, + // invisible ones have to be skipped here + if( rLinkRef->IsVisible() ) + { + if( pLink == rLinkRef.get() ) + { + m_xTbLinks->select(nSelect); + LinksSelectHdl( nullptr ); + return ; + } + ++nSelect; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/multipat.cxx b/cui/source/dialogs/multipat.cxx new file mode 100644 index 0000000000..085d21f995 --- /dev/null +++ b/cui/source/dialogs/multipat.cxx @@ -0,0 +1,316 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::uno; + +IMPL_LINK_NOARG(SvxMultiPathDialog, SelectHdl_Impl, weld::TreeView&, void) +{ + auto nCount = m_xRadioLB->n_children(); + bool bIsSelected = m_xRadioLB->get_selected_index() != -1; + bool bEnable = nCount > 1; + m_xDelBtn->set_sensitive(bEnable && bIsSelected); +} + +IMPL_LINK_NOARG(SvxPathSelectDialog, SelectHdl_Impl, weld::TreeView&, void) +{ + auto nCount = m_xPathLB->n_children(); + bool bIsSelected = m_xPathLB->get_selected_index() != -1; + bool bEnable = nCount > 1; + m_xDelBtn->set_sensitive(bEnable && bIsSelected); +} + +void SvxMultiPathDialog::HandleEntryChecked(int nRow) +{ + m_xRadioLB->select(nRow); + bool bChecked = m_xRadioLB->get_toggle(nRow) == TRISTATE_TRUE; + if (bChecked) + { + // we have radio button behavior -> so uncheck the other entries + int nCount = m_xRadioLB->n_children(); + for (int i = 0; i < nCount; ++i) + { + if (i != nRow) + m_xRadioLB->set_toggle(i, TRISTATE_FALSE); + } + } +} + +IMPL_LINK(SvxMultiPathDialog, CheckHdl_Impl, const weld::TreeView::iter_col&, rRowCol, void) +{ + HandleEntryChecked(m_xRadioLB->get_iter_index_in_parent(rRowCol.first)); +} + +void SvxMultiPathDialog::AppendEntry(const OUString& rText, const OUString& rId) +{ + m_xRadioLB->append(); + const int nRow = m_xRadioLB->n_children() - 1; + m_xRadioLB->set_toggle(nRow, TRISTATE_FALSE); + m_xRadioLB->set_text(nRow, rText, 0); + m_xRadioLB->set_id(nRow, rId); +} + +IMPL_LINK_NOARG(SvxMultiPathDialog, AddHdl_Impl, weld::Button&, void) +{ + Reference < XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference < XFolderPicker2 > xFolderPicker = sfx2::createFolderPicker(xContext, m_xDialog.get()); + + if ( xFolderPicker->execute() != ExecutableDialogResults::OK ) + return; + + INetURLObject aPath( xFolderPicker->getDirectory() ); + aPath.removeFinalSlash(); + OUString aURL = aPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + OUString sInsPath; + osl::FileBase::getSystemPathFromFileURL(aURL, sInsPath); + + if (m_xRadioLB->find_text(sInsPath) != -1) + { + OUString sMsg( CuiResId( RID_MULTIPATH_DBL_ERR ) ); + sMsg = sMsg.replaceFirst( "%1", sInsPath ); + std::unique_ptr xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, sMsg)); + xInfoBox->run(); + } + else + { + AppendEntry(sInsPath, aURL); + } + + SelectHdl_Impl(*m_xRadioLB); +} + +IMPL_LINK_NOARG(SvxPathSelectDialog, AddHdl_Impl, weld::Button&, void) +{ + Reference < XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference < XFolderPicker2 > xFolderPicker = sfx2::createFolderPicker(xContext, m_xDialog.get()); + + if ( xFolderPicker->execute() != ExecutableDialogResults::OK ) + return; + + INetURLObject aPath( xFolderPicker->getDirectory() ); + aPath.removeFinalSlash(); + OUString aURL = aPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + OUString sInsPath; + osl::FileBase::getSystemPathFromFileURL(aURL, sInsPath); + + if (m_xPathLB->find_text(sInsPath) != -1) + { + OUString sMsg( CuiResId( RID_MULTIPATH_DBL_ERR ) ); + sMsg = sMsg.replaceFirst( "%1", sInsPath ); + std::unique_ptr xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, sMsg)); + xInfoBox->run(); + } + else + { + m_xPathLB->append(aURL, sInsPath); + } + + SelectHdl_Impl(*m_xPathLB); +} + +IMPL_LINK_NOARG(SvxMultiPathDialog, DelHdl_Impl, weld::Button&, void) +{ + int nPos = m_xRadioLB->get_selected_index(); + bool bChecked = m_xRadioLB->get_toggle(nPos) == TRISTATE_TRUE; + m_xRadioLB->remove(nPos); + int nCnt = m_xRadioLB->n_children(); + if (nCnt) + { + --nCnt; + + if ( nPos > nCnt ) + nPos = nCnt; + if (bChecked) + { + m_xRadioLB->set_toggle(nPos, TRISTATE_TRUE); + HandleEntryChecked(nPos); + } + m_xRadioLB->select(nPos); + } + + SelectHdl_Impl(*m_xRadioLB); +} + +IMPL_LINK_NOARG(SvxPathSelectDialog, DelHdl_Impl, weld::Button&, void) +{ + int nPos = m_xPathLB->get_selected_index(); + m_xPathLB->remove(nPos); + int nCnt = m_xPathLB->n_children(); + + if (nCnt) + { + --nCnt; + + if ( nPos > nCnt ) + nPos = nCnt; + m_xPathLB->select(nPos); + } + + SelectHdl_Impl(*m_xPathLB); +} + +SvxMultiPathDialog::SvxMultiPathDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/multipathdialog.ui", "MultiPathDialog") + , m_xRadioLB(m_xBuilder->weld_tree_view("paths")) + , m_xAddBtn(m_xBuilder->weld_button("add")) + , m_xDelBtn(m_xBuilder->weld_button("delete")) +{ + m_xRadioLB->set_size_request(m_xRadioLB->get_approximate_digit_width() * 60, + m_xRadioLB->get_text_height() * 10); + m_xRadioLB->enable_toggle_buttons(weld::ColumnToggleType::Radio); + m_xRadioLB->connect_toggled(LINK(this, SvxMultiPathDialog, CheckHdl_Impl)); + m_xRadioLB->connect_changed(LINK(this, SvxMultiPathDialog, SelectHdl_Impl)); + + m_xAddBtn->connect_clicked(LINK(this, SvxMultiPathDialog, AddHdl_Impl)); + m_xDelBtn->connect_clicked(LINK(this, SvxMultiPathDialog, DelHdl_Impl)); + + SelectHdl_Impl(*m_xRadioLB); +} + +SvxPathSelectDialog::SvxPathSelectDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/selectpathdialog.ui", "SelectPathDialog") + , m_xPathLB(m_xBuilder->weld_tree_view("paths")) + , m_xAddBtn(m_xBuilder->weld_button("add")) + , m_xDelBtn(m_xBuilder->weld_button("delete")) +{ + m_xPathLB->set_size_request(m_xPathLB->get_approximate_digit_width() * 60, + m_xPathLB->get_text_height() * 10); + + m_xPathLB->connect_changed(LINK(this, SvxPathSelectDialog, SelectHdl_Impl)); + m_xAddBtn->connect_clicked(LINK(this, SvxPathSelectDialog, AddHdl_Impl)); + m_xDelBtn->connect_clicked(LINK(this, SvxPathSelectDialog, DelHdl_Impl)); + + SelectHdl_Impl(*m_xPathLB); +} + +SvxMultiPathDialog::~SvxMultiPathDialog() +{ +} + +OUString SvxMultiPathDialog::GetPath() const +{ + OUStringBuffer sNewPath; + sal_Unicode cDelim = SVT_SEARCHPATH_DELIMITER; + + OUString sWritable; + for (int i = 0, nCount = m_xRadioLB->n_children(); i < nCount; ++i) + { + if (m_xRadioLB->get_toggle(i) == TRISTATE_TRUE) + sWritable = m_xRadioLB->get_id(i); + else + { + if (!sNewPath.isEmpty()) + sNewPath.append(cDelim); + sNewPath.append(m_xRadioLB->get_id(i)); + } + } + if (!sNewPath.isEmpty()) + sNewPath.append(cDelim); + sNewPath.append(sWritable); + + return sNewPath.makeStringAndClear(); +} + +OUString SvxPathSelectDialog::GetPath() const +{ + OUStringBuffer sNewPath; + + for (int i = 0; i < m_xPathLB->n_children(); ++i) + { + if ( !sNewPath.isEmpty() ) + sNewPath.append(SVT_SEARCHPATH_DELIMITER); + sNewPath.append( m_xPathLB->get_id(i)); + } + + return sNewPath.makeStringAndClear(); +} + +void SvxMultiPathDialog::SetPath( std::u16string_view rPath ) +{ + if ( !rPath.empty() ) + { + const sal_Unicode cDelim = SVT_SEARCHPATH_DELIMITER; + int nCount = 0; + sal_Int32 nIndex = 0; + do + { + const OUString sPath( o3tl::getToken(rPath, 0, cDelim, nIndex ) ); + OUString sSystemPath; + bool bIsSystemPath = + osl::FileBase::getSystemPathFromFileURL(sPath, sSystemPath) == osl::FileBase::E_None; + + const OUString sEntry((bIsSystemPath ? sSystemPath : sPath)); + AppendEntry(sEntry, sPath); + ++nCount; + } + while (nIndex >= 0); + + if (nCount) + { + m_xRadioLB->set_toggle(nCount - 1, TRISTATE_TRUE); + HandleEntryChecked(nCount - 1); + } + } + + SelectHdl_Impl(*m_xRadioLB); +} + +void SvxPathSelectDialog::SetPath(std::u16string_view rPath) +{ + if ( !rPath.empty() ) + { + sal_Int32 nIndex = 0; + do + { + const OUString sPath( o3tl::getToken(rPath, 0, SVT_SEARCHPATH_DELIMITER, nIndex ) ); + OUString sSystemPath; + bool bIsSystemPath = + osl::FileBase::getSystemPathFromFileURL(sPath, sSystemPath) == osl::FileBase::E_None; + + m_xPathLB->append(sPath, bIsSystemPath ? sSystemPath : sPath); + } + while (nIndex >= 0); + } + + SelectHdl_Impl(*m_xPathLB); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/newtabledlg.cxx b/cui/source/dialogs/newtabledlg.cxx new file mode 100644 index 0000000000..9eb50987ea --- /dev/null +++ b/cui/source/dialogs/newtabledlg.cxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +SvxNewTableDialog::SvxNewTableDialog(weld::Window* pWindow) + : GenericDialogController(pWindow, "cui/ui/newtabledialog.ui", "NewTableDialog") + , mxNumColumns(m_xBuilder->weld_spin_button("columns")) + , mxNumRows(m_xBuilder->weld_spin_button("rows")) +{ +} + +sal_Int32 SvxNewTableDialog::getRows() const +{ + return sal::static_int_cast(mxNumRows->get_value()); +} + +sal_Int32 SvxNewTableDialog::getColumns() const +{ + return sal::static_int_cast(mxNumColumns->get_value()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/passwdomdlg.cxx b/cui/source/dialogs/passwdomdlg.cxx new file mode 100644 index 0000000000..a7e95a29f3 --- /dev/null +++ b/cui/source/dialogs/passwdomdlg.cxx @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +IMPL_LINK_NOARG(PasswordToOpenModifyDialog, OkBtnClickHdl, weld::Button&, void) +{ + bool bInvalidState = !m_xOpenReadonlyCB->get_active() && !m_bAllowEmpty && + m_xPasswdToOpenED->get_text().isEmpty() && + m_xPasswdToModifyED->get_text().isEmpty(); + if (bInvalidState) + { + m_xErrorBox.reset(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + m_bIsPasswordToModify? m_aInvalidStateForOkButton : m_aInvalidStateForOkButton_v2)); + m_xErrorBox->runAsync(m_xErrorBox, [](sal_Int32 /*nResult*/) {}); + } + else // check for mismatched passwords and password policy + { + if (m_oPasswordPolicy) + { + if (!SvPasswordHelper::PasswordMeetsPolicy(m_xPasswdToOpenED->get_text(), + m_oPasswordPolicy)) + { + m_xPasswdToOpenED->grab_focus(); + return; + } + + if (m_xOpenReadonlyCB->get_active() + && !SvPasswordHelper::PasswordMeetsPolicy(m_xPasswdToModifyED->get_text(), + m_oPasswordPolicy)) + { + m_xPasswdToModifyED->grab_focus(); + return; + } + } + + const bool bToOpenMatch = m_xPasswdToOpenED->get_text() == m_xReenterPasswdToOpenED->get_text(); + const bool bToModifyMatch = m_xPasswdToModifyED->get_text() == m_xReenterPasswdToModifyED->get_text(); + const int nMismatch = (bToOpenMatch? 0 : 1) + (bToModifyMatch? 0 : 1); + if (nMismatch > 0) + { + m_xErrorBox.reset(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + nMismatch == 1 ? m_aOneMismatch : m_aTwoMismatch)); + m_xErrorBox->runAsync(m_xErrorBox, [this, bToOpenMatch, nMismatch](sal_Int32 /*nResult*/) + { + weld::Entry* pEdit = !bToOpenMatch ? m_xPasswdToOpenED.get() : m_xPasswdToModifyED.get(); + weld::Entry* pRepeatEdit = !bToOpenMatch? m_xReenterPasswdToOpenED.get() : m_xReenterPasswdToModifyED.get(); + if (nMismatch == 1) + { + pEdit->set_text( "" ); + pRepeatEdit->set_text( "" ); + } + else if (nMismatch == 2) + { + m_xPasswdToOpenED->set_text( "" ); + m_xReenterPasswdToOpenED->set_text( "" ); + m_xPasswdToModifyED->set_text( "" ); + m_xReenterPasswdToModifyED->set_text( "" ); + } + pEdit->grab_focus(); + }); + } + else + { + m_xDialog->response(RET_OK); + } + } +} + +IMPL_LINK(PasswordToOpenModifyDialog, ChangeHdl, weld::Entry&, rEntry, void) +{ + auto aPasswordText = rEntry.get_text(); + + weld::Label* pIndicator = nullptr; + weld::LevelBar* pLevelBar = nullptr; + if (&rEntry == m_xPasswdToOpenED.get()) + { + pIndicator = m_xPasswdToOpenInd.get(); + pLevelBar = m_xPasswdToOpenBar.get(); + } + else if (&rEntry == m_xReenterPasswdToOpenED.get()) + { + pIndicator = m_xReenterPasswdToOpenInd.get(); + } + else if (&rEntry == m_xPasswdToModifyED.get()) + { + pIndicator = m_xPasswdToModifyInd.get(); + pLevelBar = m_xPasswdToModifyBar.get(); + } + else if (&rEntry == m_xReenterPasswdToModifyED.get()) + { + pIndicator = m_xReenterPasswdToModifyInd.get(); + } + assert(pIndicator); + + bool bPasswordMeetsPolicy + = SvPasswordHelper::PasswordMeetsPolicy(aPasswordText, m_oPasswordPolicy); + if (pLevelBar) + { + rEntry.set_message_type(bPasswordMeetsPolicy ? weld::EntryMessageType::Normal + : weld::EntryMessageType::Error); + pIndicator->set_visible(!bPasswordMeetsPolicy); + } + + // if password doesn't meet policy cap the percentage at 70% + if (pLevelBar) + pLevelBar->set_percentage( + std::min(SvPasswordHelper::GetPasswordStrengthPercentage(aPasswordText), + bPasswordMeetsPolicy ? std::numeric_limits::max() : 70.0)); + + if (m_nMaxPasswdLen) + { + int nLength = aPasswordText.getLength(); + pIndicator->set_visible(nLength >= m_nMaxPasswdLen); + } +} + +PasswordToOpenModifyDialog::PasswordToOpenModifyDialog(weld::Window * pParent, sal_uInt16 nMaxPasswdLen, bool bIsPasswordToModify) + : SfxDialogController(pParent, "cui/ui/password.ui", "PasswordDialog") + , m_xPasswdToOpenED(m_xBuilder->weld_entry("newpassEntry")) + , m_xPasswdToOpenInd(m_xBuilder->weld_label("newpassIndicator")) + , m_xPasswdToOpenBar(m_xBuilder->weld_level_bar("passlevelbar")) + , m_xReenterPasswdToOpenED(m_xBuilder->weld_entry("confirmpassEntry")) + , m_xReenterPasswdToOpenInd(m_xBuilder->weld_label("confirmpassIndicator")) + , m_xOptionsExpander(m_xBuilder->weld_expander("expander")) + , m_xOk(m_xBuilder->weld_button("ok")) + , m_xOpenReadonlyCB(m_xBuilder->weld_check_button("readonly")) + , m_xPasswdToModifyFT(m_xBuilder->weld_label("label7")) + , m_xPasswdToModifyED(m_xBuilder->weld_entry("newpassroEntry")) + , m_xPasswdToModifyInd(m_xBuilder->weld_label("newpassroIndicator")) + , m_xPasswdToModifyBar(m_xBuilder->weld_level_bar("ropasslevelbar")) + , m_xReenterPasswdToModifyFT(m_xBuilder->weld_label("label8")) + , m_xReenterPasswdToModifyED(m_xBuilder->weld_entry("confirmropassEntry")) + , m_xReenterPasswdToModifyInd(m_xBuilder->weld_label("confirmropassIndicator")) + , m_aOneMismatch( CuiResId( RID_CUISTR_ONE_PASSWORD_MISMATCH ) ) + , m_aTwoMismatch( CuiResId( RID_CUISTR_TWO_PASSWORDS_MISMATCH ) ) + , m_aInvalidStateForOkButton( CuiResId( RID_CUISTR_INVALID_STATE_FOR_OK_BUTTON ) ) + , m_aInvalidStateForOkButton_v2( CuiResId( RID_CUISTR_INVALID_STATE_FOR_OK_BUTTON_V2 ) ) + , m_oPasswordPolicy(officecfg::Office::Common::Security::Scripting::PasswordPolicy::get()) + , m_nMaxPasswdLen(nMaxPasswdLen) + , m_bIsPasswordToModify( bIsPasswordToModify ) + , m_bAllowEmpty( false ) +{ + m_xOk->connect_clicked(LINK(this, PasswordToOpenModifyDialog, OkBtnClickHdl)); + m_xPasswdToOpenED->connect_changed(LINK(this, PasswordToOpenModifyDialog, ChangeHdl)); + m_xPasswdToModifyED->connect_changed(LINK(this, PasswordToOpenModifyDialog, ChangeHdl)); + if(m_oPasswordPolicy || nMaxPasswdLen) + { + m_xReenterPasswdToOpenED->connect_changed(LINK(this, PasswordToOpenModifyDialog, ChangeHdl)); + m_xReenterPasswdToModifyED->connect_changed(LINK(this, PasswordToOpenModifyDialog, ChangeHdl)); + + OUString aIndicatorText{}; + OUString aMaxPassLengthIndicator{ CuiResId(RID_CUISTR_PASSWORD_LEN_INDICATOR) + .replaceFirst("%1", + OUString::number(nMaxPasswdLen)) }; + if (m_oPasswordPolicy && nMaxPasswdLen) + { + aIndicatorText + = officecfg::Office::Common::Security::Scripting::PasswordPolicyErrorMessage::get() + + "\n" + aMaxPassLengthIndicator; + } + else if (m_oPasswordPolicy) + { + aIndicatorText + = officecfg::Office::Common::Security::Scripting::PasswordPolicyErrorMessage::get(); + } + else if (nMaxPasswdLen) + { + aIndicatorText = aMaxPassLengthIndicator; + } + + m_xPasswdToOpenInd->set_label(aIndicatorText); + m_xReenterPasswdToOpenInd->set_label(aMaxPassLengthIndicator); + m_xPasswdToModifyInd->set_label(aIndicatorText); + m_xReenterPasswdToModifyInd->set_label(aMaxPassLengthIndicator); + + if (nMaxPasswdLen) + { + m_xPasswdToOpenED->set_max_length(nMaxPasswdLen); + m_xReenterPasswdToOpenED->set_max_length(nMaxPasswdLen); + m_xPasswdToModifyED->set_max_length(nMaxPasswdLen); + m_xReenterPasswdToModifyED->set_max_length(nMaxPasswdLen); + } + } + + m_xPasswdToOpenED->grab_focus(); + + m_xOptionsExpander->set_sensitive(bIsPasswordToModify); + if (!bIsPasswordToModify) + m_xOptionsExpander->hide(); + else if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + if (pSh->IsLoadReadonly()) + { + m_xOpenReadonlyCB->set_active(true); + m_xOptionsExpander->set_expanded(true); + } + } + + m_xOpenReadonlyCB->connect_toggled(LINK(this, PasswordToOpenModifyDialog, ReadonlyOnOffHdl)); + ReadonlyOnOffHdl(*m_xOpenReadonlyCB); +} + +PasswordToOpenModifyDialog::~PasswordToOpenModifyDialog() +{ + if (m_xErrorBox) + { + m_xErrorBox->response(RET_CANCEL); + } +} + +void PasswordToOpenModifyDialog::AllowEmpty() +{ + m_bAllowEmpty = true; +} + +OUString PasswordToOpenModifyDialog::GetPasswordToOpen() const +{ + const bool bPasswdOk = + !m_xPasswdToOpenED->get_text().isEmpty() && + m_xPasswdToOpenED->get_text() == m_xReenterPasswdToOpenED->get_text(); + return bPasswdOk ? m_xPasswdToOpenED->get_text() : OUString(); +} + + +OUString PasswordToOpenModifyDialog::GetPasswordToModify() const +{ + const bool bPasswdOk = + !m_xPasswdToModifyED->get_text().isEmpty() && + m_xPasswdToModifyED->get_text() == m_xReenterPasswdToModifyED->get_text(); + return bPasswdOk ? m_xPasswdToModifyED->get_text() : OUString(); +} + + +bool PasswordToOpenModifyDialog::IsRecommendToOpenReadonly() const +{ + return m_xOpenReadonlyCB->get_active(); +} + +IMPL_LINK_NOARG(PasswordToOpenModifyDialog, ReadonlyOnOffHdl, weld::Toggleable&, void) +{ + bool bEnable = m_xOpenReadonlyCB->get_active(); + m_xPasswdToModifyED->set_sensitive(bEnable); + m_xPasswdToModifyFT->set_sensitive(bEnable); + m_xReenterPasswdToModifyED->set_sensitive(bEnable); + m_xReenterPasswdToModifyFT->set_sensitive(bEnable); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/pastedlg.cxx b/cui/source/dialogs/pastedlg.cxx new file mode 100644 index 0000000000..423637e9d9 --- /dev/null +++ b/cui/source/dialogs/pastedlg.cxx @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SvPasteObjectDialog::SvPasteObjectDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/pastespecial.ui", "PasteSpecialDialog") + , m_xFtObjectSource(m_xBuilder->weld_label("source")) + , m_xLbInsertList(m_xBuilder->weld_tree_view("list")) + , m_xOKButton(m_xBuilder->weld_button("ok")) +{ + m_xLbInsertList->set_size_request(m_xLbInsertList->get_approximate_digit_width() * 40, + m_xLbInsertList->get_height_rows(6)); + m_xOKButton->set_sensitive(false); + + ObjectLB().connect_changed(LINK(this, SvPasteObjectDialog, SelectHdl)); + ObjectLB().connect_row_activated(LINK( this, SvPasteObjectDialog, DoubleClickHdl)); +} + +void SvPasteObjectDialog::SelectObject() +{ + if (m_xLbInsertList->n_children()) + { + m_xLbInsertList->select(0); + SelectHdl(*m_xLbInsertList); + } +} + +IMPL_LINK_NOARG(SvPasteObjectDialog, SelectHdl, weld::TreeView&, void) +{ + if (!m_xOKButton->get_sensitive()) + m_xOKButton->set_sensitive(true); +} + +IMPL_LINK_NOARG(SvPasteObjectDialog, DoubleClickHdl, weld::TreeView&, bool) +{ + m_xDialog->response(RET_OK); + return true; +} + +/************************************************************************* +|* SvPasteObjectDialog::Insert() +*************************************************************************/ +void SvPasteObjectDialog::Insert( SotClipboardFormatId nFormat, const OUString& rFormatName ) +{ + aSupplementMap.insert( std::make_pair( nFormat, rFormatName ) ); +} + +void SvPasteObjectDialog::InsertUno(const OUString& sCmd, const OUString& sLabel) +{ + aExtraCommand.first = sCmd; + aExtraCommand.second = sLabel; +} + + +void SvPasteObjectDialog::PreGetFormat( const TransferableDataHelper &rHelper ) +{ + //TODO/LATER: why is the Descriptor never used?! + TransferableObjectDescriptor aDesc; + if (rHelper.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR)) + { + (void)rHelper.GetTransferableObjectDescriptor( + SotClipboardFormatId::OBJECTDESCRIPTOR, aDesc); + } + const DataFlavorExVector* pFormats = &rHelper.GetDataFlavorExVector(); + + // create and fill dialog box + OUString aSourceName, aTypeName; + SvGlobalName aEmptyNm; + + //ObjectLB().SetUpdateMode( false ); + ObjectLB().freeze(); + + DataFlavorExVector::iterator aIter( const_cast(*pFormats).begin() ), + aEnd( const_cast(*pFormats).end() ); + while( aIter != aEnd ) + { + SotClipboardFormatId nFormat = (*aIter++).mnSotId; + + std::map< SotClipboardFormatId, OUString >::iterator itName = + aSupplementMap.find( nFormat ); + + // if there is an "Embed Source" or and "Embedded Object" on the + // Clipboard we read the Description and the Source of this object + // from an accompanied "Object Descriptor" format on the clipboard + // Remember: these formats mostly appear together on the clipboard + OUString aName; + const OUString* pName = nullptr; + if ( itName == aSupplementMap.end() ) + { + SvPasteObjectHelper::GetEmbeddedName(rHelper,aName,aSourceName,nFormat); + if ( !aName.isEmpty() ) + pName = &aName; + } + else + { + pName = &(itName->second); + } + + if( pName ) + { + aName = *pName; + + if( SotClipboardFormatId::EMBED_SOURCE == nFormat ) + { + if( aDesc.maClassName != aEmptyNm ) + { + aSourceName = aDesc.maDisplayName; + + if( aDesc.maClassName == aObjClassName ) + aName = aObjName; + else + aName = aTypeName = aDesc.maTypeName; + } + } + else if( SotClipboardFormatId::LINK_SOURCE == nFormat ) + { + continue; + } + else if( aName.isEmpty() ) + aName = SvPasteObjectHelper::GetSotFormatUIName( nFormat ); + + // Show RICHTEXT only in case RTF is not present. + if (nFormat == SotClipboardFormatId::RICHTEXT && + std::any_of(pFormats->begin(), pFormats->end(), + [](const DataFlavorEx& rFlavor) { + return rFlavor.mnSotId == SotClipboardFormatId::RTF; + })) + { + continue; + } + + if (ObjectLB().find_text(aName) == -1) + { + ObjectLB().append(OUString::number(static_cast(nFormat)), aName); + } + } + } + + if( aTypeName.isEmpty() && aSourceName.isEmpty() ) + { + if( aDesc.maClassName != aEmptyNm ) + { + aSourceName = aDesc.maDisplayName; + aTypeName = aDesc.maTypeName; + } + + if( aTypeName.isEmpty() && aSourceName.isEmpty() ) + { + // global resource from svtools (former so3 resource) + aSourceName = SvtResId(STR_UNKNOWN_SOURCE); + } + } + + ObjectLB().thaw(); + SelectObject(); + + if( !aSourceName.isEmpty() ) + { + if( !aTypeName.isEmpty() ) + aTypeName += "\n"; + + aTypeName += aSourceName; + aTypeName = convertLineEnd(aTypeName, GetSystemLineEnd()); + } + + m_xFtObjectSource->set_label(aTypeName); +} + +SotClipboardFormatId SvPasteObjectDialog::GetFormatOnly() +{ + return static_cast(ObjectLB().get_selected_id().toUInt32()); +} + +SotClipboardFormatId SvPasteObjectDialog::GetFormat( const TransferableDataHelper& rHelper) +{ + //TODO/LATER: why is the Descriptor never used?! + TransferableObjectDescriptor aDesc; + if (rHelper.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR)) + { + (void)rHelper.GetTransferableObjectDescriptor( + SotClipboardFormatId::OBJECTDESCRIPTOR, aDesc); + } + const DataFlavorExVector* pFormats = &rHelper.GetDataFlavorExVector(); + + // create and fill dialog box + OUString aSourceName, aTypeName; + SotClipboardFormatId nSelFormat = SotClipboardFormatId::NONE; + SvGlobalName aEmptyNm; + + ObjectLB().freeze(); + + for (auto const& format : *pFormats) + { + SotClipboardFormatId nFormat = format.mnSotId; + + std::map< SotClipboardFormatId, OUString >::iterator itName = + aSupplementMap.find( nFormat ); + + // if there is an "Embed Source" or and "Embedded Object" on the + // Clipboard we read the Description and the Source of this object + // from an accompanied "Object Descriptor" format on the clipboard + // Remember: these formats mostly appear together on the clipboard + OUString aName; + const OUString* pName = nullptr; + if ( itName == aSupplementMap.end() ) + { + SvPasteObjectHelper::GetEmbeddedName(rHelper,aName,aSourceName,nFormat); + if ( !aName.isEmpty() ) + pName = &aName; + } + else + { + pName = &(itName->second); + } + + if( pName ) + { + aName = *pName; + + if( SotClipboardFormatId::EMBED_SOURCE == nFormat ) + { + if( aDesc.maClassName != aEmptyNm ) + { + aSourceName = aDesc.maDisplayName; + + if( aDesc.maClassName == aObjClassName ) + aName = aObjName; + else + aName = aTypeName = aDesc.maTypeName; + } + } + else if( SotClipboardFormatId::LINK_SOURCE == nFormat ) + { + continue; + } + else if( aName.isEmpty() ) + aName = SvPasteObjectHelper::GetSotFormatUIName( nFormat ); + + // Show RICHTEXT only in case RTF is not present. + if (nFormat == SotClipboardFormatId::RICHTEXT && + std::any_of(pFormats->begin(), pFormats->end(), + [](const DataFlavorEx& rFlavor) { + return rFlavor.mnSotId == SotClipboardFormatId::RTF; + })) + { + continue; + } + + if (ObjectLB().find_text(aName) == -1) + { + ObjectLB().append(OUString::number(static_cast(nFormat)), aName); + } + } + } + + if( aTypeName.isEmpty() && aSourceName.isEmpty() ) + { + if( aDesc.maClassName != aEmptyNm ) + { + aSourceName = aDesc.maDisplayName; + aTypeName = aDesc.maTypeName; + } + + if( aTypeName.isEmpty() && aSourceName.isEmpty() ) + { + // global resource from svtools (former so3 resource) + aSourceName = SvtResId(STR_UNKNOWN_SOURCE); + } + } + + if (!aExtraCommand.first.isEmpty()) + { + ObjectLB().append(aExtraCommand.first, aExtraCommand.second); + } + + ObjectLB().thaw(); + SelectObject(); + + if( !aSourceName.isEmpty() ) + { + if( !aTypeName.isEmpty() ) + aTypeName += "\n"; + + aTypeName += aSourceName; + aTypeName = convertLineEnd(aTypeName, GetSystemLineEnd()); + } + + m_xFtObjectSource->set_label(aTypeName); + + if (run() == RET_OK) + { + if (ObjectLB().get_selected_id().startsWithIgnoreAsciiCase(".uno")) + { + comphelper::dispatchCommand(aExtraCommand.first, {}); + nSelFormat = SotClipboardFormatId::NONE; + } + else + { + nSelFormat = static_cast(ObjectLB().get_selected_id().toUInt32()); + } + } + + return nSelFormat; +} + +void SvPasteObjectDialog::SetObjName( const SvGlobalName & rClass, const OUString & rObjName ) +{ + aObjClassName = rClass; + aObjName = rObjName; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/postdlg.cxx b/cui/source/dialogs/postdlg.cxx new file mode 100644 index 0000000000..1bcb6b7a01 --- /dev/null +++ b/cui/source/dialogs/postdlg.cxx @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// class SvxPostItDialog ------------------------------------------------- + +SvxPostItDialog::SvxPostItDialog(weld::Widget* pParent, const SfxItemSet& rCoreSet, + bool bPrevNext) + : SfxDialogController(pParent, "cui/ui/comment.ui", "CommentDialog") + , m_rSet(rCoreSet) + , m_xLastEditFT(m_xBuilder->weld_label("lastedit")) + , m_xAltTitle(m_xBuilder->weld_label("alttitle")) + , m_xEditED(m_xBuilder->weld_text_view("edit")) + , m_xInsertAuthor(m_xBuilder->weld_widget("insertauthor")) + , m_xAuthorBtn(m_xBuilder->weld_button("author")) + , m_xOKBtn(m_xBuilder->weld_button("ok")) + , m_xPrevBtn(m_xBuilder->weld_button("previous")) + , m_xNextBtn(m_xBuilder->weld_button("next")) +{ + m_xPrevBtn->connect_clicked( LINK( this, SvxPostItDialog, PrevHdl ) ); + m_xNextBtn->connect_clicked( LINK( this, SvxPostItDialog, NextHdl ) ); + m_xAuthorBtn->connect_clicked( LINK( this, SvxPostItDialog, Stamp ) ); + m_xOKBtn->connect_clicked( LINK( this, SvxPostItDialog, OKHdl ) ); + + bool bNew = true; + + m_xPrevBtn->set_visible(bPrevNext); + m_xNextBtn->set_visible(bPrevNext); + + OUString aAuthorStr, aDateStr; + + if (m_rSet.GetItemState( SID_ATTR_POSTIT_AUTHOR ) >= SfxItemState::DEFAULT) + { + bNew = false; + const SvxPostItAuthorItem& rAuthor = m_rSet.Get(SID_ATTR_POSTIT_AUTHOR); + aAuthorStr = rAuthor.GetValue(); + } + else + aAuthorStr = SvtUserOptions().GetID(); + + if (m_rSet.GetItemState( SID_ATTR_POSTIT_DATE ) >= SfxItemState::DEFAULT) + { + const SvxPostItDateItem& rDate = m_rSet.Get( SID_ATTR_POSTIT_DATE ); + aDateStr = rDate.GetValue(); + } + else + { + const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() ); + aDateStr = rLocaleWrapper.getDate( Date( Date::SYSTEM ) ); + } + + OUString aTextStr; + if (m_rSet.GetItemState( SID_ATTR_POSTIT_TEXT ) >= SfxItemState::DEFAULT) + { + const SvxPostItTextItem& rText = m_rSet.Get( SID_ATTR_POSTIT_TEXT ); + aTextStr = rText.GetValue(); + } + + ShowLastAuthor(aAuthorStr, aDateStr); + + //lock to an initial size before replacing contents + m_xEditED->set_size_request(m_xEditED->get_approximate_digit_width() * 32, + m_xEditED->get_height_rows(10)); + m_xEditED->set_text(convertLineEnd(aTextStr, GetSystemLineEnd())); + + if (!bNew) + m_xDialog->set_title(m_xAltTitle->get_label()); +} + + +SvxPostItDialog::~SvxPostItDialog() +{ +} + +void SvxPostItDialog::ShowLastAuthor(std::u16string_view rAuthor, std::u16string_view rDate) +{ + OUString sTxt = OUString::Concat(rAuthor) + ", " + rDate; + m_xLastEditFT->set_label( sTxt ); +} + +WhichRangesContainer SvxPostItDialog::GetRanges() +{ + return WhichRangesContainer(svl::Items); +} + +void SvxPostItDialog::EnableTravel(bool bNext, bool bPrev) +{ + m_xPrevBtn->set_sensitive(bPrev); + m_xNextBtn->set_sensitive(bNext); +} + +IMPL_LINK_NOARG(SvxPostItDialog, PrevHdl, weld::Button&, void) +{ + m_aPrevHdlLink.Call( *this ); +} + +IMPL_LINK_NOARG(SvxPostItDialog, NextHdl, weld::Button&, void) +{ + m_aNextHdlLink.Call( *this ); +} + +IMPL_LINK_NOARG(SvxPostItDialog, Stamp, weld::Button&, void) +{ + Date aDate( Date::SYSTEM ); + tools::Time aTime( tools::Time::SYSTEM ); + OUString aTmp( SvtUserOptions().GetID() ); + const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() ); + OUString aStr( m_xEditED->get_text() + "\n---- " ); + + if ( !aTmp.isEmpty() ) + { + aStr += aTmp + ", "; + } + aStr += rLocaleWrapper.getDate(aDate) + ", " + rLocaleWrapper.getTime(aTime, false) + " ----\n"; + aStr = convertLineEnd(aStr, GetSystemLineEnd()); + + m_xEditED->set_text(aStr); + sal_Int32 nLen = aStr.getLength(); + m_xEditED->grab_focus(); + m_xEditED->select_region(nLen, nLen); +} + +IMPL_LINK_NOARG(SvxPostItDialog, OKHdl, weld::Button&, void) +{ + const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() ); + m_xOutSet.reset(new SfxItemSet(m_rSet)); + m_xOutSet->Put( SvxPostItAuthorItem(SvtUserOptions().GetID(), SID_ATTR_POSTIT_AUTHOR ) ); + m_xOutSet->Put( SvxPostItDateItem(rLocaleWrapper.getDate( Date( Date::SYSTEM ) ), SID_ATTR_POSTIT_DATE ) ); + m_xOutSet->Put( SvxPostItTextItem(m_xEditED->get_text(), SID_ATTR_POSTIT_TEXT ) ); + m_xDialog->response(RET_OK); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/screenshotannotationdlg.cxx b/cui/source/dialogs/screenshotannotationdlg.cxx new file mode 100644 index 0000000000..fca03c02d4 --- /dev/null +++ b/cui/source/dialogs/screenshotannotationdlg.cxx @@ -0,0 +1,578 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace +{ + OUString lcl_genRandom( std::u16string_view rId ) + { + //FIXME: plus timestamp + unsigned int nRand = comphelper::rng::uniform_uint_distribution(0, 0xFFFF); + return OUString( rId + OUString::number( nRand ) ); + } + + + OUString lcl_AltDescr() + { + OUString aTempl("" + " " //FIXME real dialog title or something + ""); + aTempl = aTempl.replaceFirst( "%1", lcl_genRandom(u"alt_id") ); + + return aTempl; + } + + OUString lcl_Image( std::u16string_view rScreenshotId, const Size& rSize ) + { + OUString aTempl("" + "%5" + ""); + aTempl = aTempl.replaceFirst( "%1", lcl_genRandom(u"img_id") ); + aTempl = aTempl.replaceFirst( "%2", rScreenshotId ); + aTempl = aTempl.replaceFirst( "%3", OUString::number( rSize.Width() ) ); + aTempl = aTempl.replaceFirst( "%4", OUString::number( rSize.Height() ) ); + aTempl = aTempl.replaceFirst( "%5", lcl_AltDescr() ); + + return aTempl; + } + + OUString lcl_ParagraphWithImage( std::u16string_view rScreenshotId, const Size& rSize ) + { + OUString aTempl( "%2" + "" SAL_NEWLINE_STRING ); + aTempl = aTempl.replaceFirst( "%1", lcl_genRandom(u"par_id") ); + aTempl = aTempl.replaceFirst( "%2", lcl_Image(rScreenshotId, rSize) ); + + return aTempl; + } + + OUString lcl_Bookmark( std::u16string_view rWidgetId ) + { + OUString aTempl = "" SAL_NEWLINE_STRING + "" SAL_NEWLINE_STRING; + aTempl = aTempl.replaceFirst( "%1", rWidgetId ); + aTempl = aTempl.replaceFirst( "%2", rWidgetId ); + aTempl = aTempl.replaceFirst( "%3", lcl_genRandom(u"bm_id") ); + + return aTempl; + } +} + +namespace +{ + class Picture : public weld::CustomWidgetController + { + private: + ScreenshotAnnotationDlg_Impl *m_pDialog; + bool m_bMouseOver; + private: + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + virtual bool MouseMove(const MouseEvent& rMouseEvent) override; + virtual bool MouseButtonUp(const MouseEvent& rMouseEvent) override; + public: + Picture(ScreenshotAnnotationDlg_Impl* pDialog) + : m_pDialog(pDialog) + , m_bMouseOver(false) + { + } + + bool IsMouseOver() const + { + return m_bMouseOver; + } + }; +} + +class ScreenshotAnnotationDlg_Impl +{ +public: + ScreenshotAnnotationDlg_Impl( + weld::Window* pParent, + weld::Builder& rParent, + weld::Dialog& rParentDialog); + ~ScreenshotAnnotationDlg_Impl(); + +private: + // Handler for click on save + DECL_LINK(saveButtonHandler, weld::Button&, void); + + // helper methods + weld::ScreenShotEntry* CheckHit(const basegfx::B2IPoint& rPosition); + void PaintScreenShotEntry( + const weld::ScreenShotEntry& rEntry, + const Color& rColor, + double fLineWidth, + double fTransparency); + void RepaintToBuffer( + bool bUseDimmed = false, + bool bPaintHilight = false); + void RepaintPictureElement(); + Point GetOffsetInPicture() const; + + // local variables + weld::Window* mpParentWindow; + weld::Dialog& mrParentDialog; + BitmapEx maParentDialogBitmap; + BitmapEx maDimmedDialogBitmap; + Size maParentDialogSize; + + // VirtualDevice for buffered interaction paints + VclPtr mxVirtualBufferDevice; + + // all detected children + weld::ScreenShotCollection maAllChildren; + + // highlighted/selected children + weld::ScreenShotEntry* mpHilighted; + std::set< weld::ScreenShotEntry* > + maSelected; + + // list of detected controls + Picture maPicture; + std::unique_ptr mxPicture; + std::unique_ptr mxText; + std::unique_ptr mxSave; + + // save as text + OUString maSaveAsText; + OUString maMainMarkupText; + + // folder URL + static OUString maLastFolderURL; +public: + void Paint(vcl::RenderContext& rRenderContext); + bool MouseMove(const MouseEvent& rMouseEvent); + bool MouseButtonUp(); +}; + +OUString ScreenshotAnnotationDlg_Impl::maLastFolderURL = OUString(); + +ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl( + weld::Window* pParent, + weld::Builder& rParentBuilder, + weld::Dialog& rParentDialog) +: mpParentWindow(pParent), + mrParentDialog(rParentDialog), + mxVirtualBufferDevice(nullptr), + mpHilighted(nullptr), + maPicture(this), + maSaveAsText(CuiResId(RID_CUISTR_SAVE_SCREENSHOT_AS)) +{ + VclPtr xParentDialogSurface(rParentDialog.screenshot()); + maParentDialogSize = xParentDialogSurface->GetOutputSizePixel(); + maParentDialogBitmap = xParentDialogSurface->GetBitmapEx(Point(), maParentDialogSize); + maDimmedDialogBitmap = maParentDialogBitmap; + + // image ain't empty + assert(!maParentDialogBitmap.IsEmpty()); + assert(0 != maParentDialogBitmap.GetSizePixel().Width()); + assert(0 != maParentDialogBitmap.GetSizePixel().Height()); + + // get needed widgets + mxPicture.reset(new weld::CustomWeld(rParentBuilder, "picture", maPicture)); + assert(mxPicture); + mxText = rParentBuilder.weld_text_view("text"); + assert(mxText); + mxSave = rParentBuilder.weld_button("save"); + assert(mxSave); + + // set screenshot image at DrawingArea, resize, set event listener + if (mxPicture) + { + maAllChildren = mrParentDialog.collect_screenshot_data(); + + // to make clear that maParentDialogBitmap is a background image, adjust + // luminance a bit for maDimmedDialogBitmap - other methods may be applied + maDimmedDialogBitmap.Adjust(-15, 0, 0, 0, 0); + + // init paint buffering VirtualDevice + mxVirtualBufferDevice = VclPtr::Create(*Application::GetDefaultDevice(), DeviceFormat::WITHOUT_ALPHA); + mxVirtualBufferDevice->SetOutputSizePixel(maParentDialogSize); + mxVirtualBufferDevice->SetFillColor(COL_TRANSPARENT); + + // initially set image for picture control + mxVirtualBufferDevice->DrawBitmapEx(Point(0, 0), maDimmedDialogBitmap); + + // set size for picture control, this will re-layout so that + // the picture control shows the whole dialog + maPicture.SetOutputSizePixel(maParentDialogSize); + mxPicture->set_size_request(maParentDialogSize.Width(), maParentDialogSize.Height()); + + mxPicture->queue_draw(); + } + + // set some test text at VclMultiLineEdit and make read-only - only + // copying content to clipboard is allowed + if (mxText) + { + mxText->set_size_request(400, mxText->get_height_rows(10)); + OUString aHelpId = mrParentDialog.get_help_id(); + Size aSizeCm = Application::GetDefaultDevice()->PixelToLogic(maParentDialogSize, MapMode(MapUnit::MapCM)); + maMainMarkupText = lcl_ParagraphWithImage( aHelpId, aSizeCm ); + mxText->set_text( maMainMarkupText ); + mxText->set_editable(false); + } + + // set click handler for save button + if (mxSave) + { + mxSave->connect_clicked(LINK(this, ScreenshotAnnotationDlg_Impl, saveButtonHandler)); + } +} + +ScreenshotAnnotationDlg_Impl::~ScreenshotAnnotationDlg_Impl() +{ + mxVirtualBufferDevice.disposeAndClear(); +} + +IMPL_LINK_NOARG(ScreenshotAnnotationDlg_Impl, saveButtonHandler, weld::Button&, void) +{ + // 'save screenshot...' pressed, offer to save maParentDialogBitmap + // as PNG image, use *.id file name as screenshot file name offering + // get a suggestion for the filename from buildable name + OUString aDerivedFileName = mrParentDialog.get_buildable_name(); + + auto xFileDlg = std::make_unique(ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION, + FileDialogFlags::NONE, mpParentWindow); + xFileDlg->SetContext(sfx2::FileDialogHelper::ScreenshotAnnotation); + + const uno::Reference< ui::dialogs::XFilePicker3 > xFilePicker = xFileDlg->GetFilePicker(); + + xFilePicker->setTitle(maSaveAsText); + + if (!maLastFolderURL.isEmpty()) + { + xFilePicker->setDisplayDirectory(maLastFolderURL); + } + + xFilePicker->appendFilter("*.png", "*.png"); + xFilePicker->setCurrentFilter("*.png"); + xFilePicker->setDefaultName(aDerivedFileName); + xFilePicker->setMultiSelectionMode(false); + + if (xFilePicker->execute() != ui::dialogs::ExecutableDialogResults::OK) + return; + + maLastFolderURL = xFilePicker->getDisplayDirectory(); + const uno::Sequence< OUString > files(xFilePicker->getSelectedFiles()); + + if (!files.hasElements()) + return; + + OUString aConfirmedName = files[0]; + + if (aConfirmedName.isEmpty()) + return; + + INetURLObject aConfirmedURL(aConfirmedName); + OUString aCurrentExtension(aConfirmedURL.getExtension()); + + if (!aCurrentExtension.isEmpty() && aCurrentExtension != "png") + { + aConfirmedURL.removeExtension(); + aCurrentExtension.clear(); + } + + if (aCurrentExtension.isEmpty()) + { + aConfirmedURL.setExtension(u"png"); + } + + // open stream + SvFileStream aNew(aConfirmedURL.PathToFileName(), StreamMode::WRITE | StreamMode::TRUNC); + + if (!aNew.IsOpen()) + return; + + // prepare bitmap to save - do use the original screenshot here, + // not the dimmed one + RepaintToBuffer(); + + // extract Bitmap + const BitmapEx aTargetBitmap( + mxVirtualBufferDevice->GetBitmapEx( + Point(0, 0), + mxVirtualBufferDevice->GetOutputSizePixel())); + + // write as PNG + vcl::PngImageWriter aPNGWriter(aNew); + aPNGWriter.write(aTargetBitmap); +} + +weld::ScreenShotEntry* ScreenshotAnnotationDlg_Impl::CheckHit(const basegfx::B2IPoint& rPosition) +{ + weld::ScreenShotEntry* pRetval = nullptr; + + for (auto&& rCandidate : maAllChildren) + { + if (rCandidate.getB2IRange().isInside(rPosition)) + { + if (pRetval) + { + if (pRetval->getB2IRange().isInside(rCandidate.getB2IRange().getMinimum()) + && pRetval->getB2IRange().isInside(rCandidate.getB2IRange().getMaximum())) + { + pRetval = &rCandidate; + } + } + else + { + pRetval = &rCandidate; + } + } + } + + return pRetval; +} + +void ScreenshotAnnotationDlg_Impl::PaintScreenShotEntry( + const weld::ScreenShotEntry& rEntry, + const Color& rColor, + double fLineWidth, + double fTransparency) +{ + if (!(mxPicture && mxVirtualBufferDevice)) + return; + + basegfx::B2DRange aB2DRange(rEntry.getB2IRange()); + + // grow in pixels to be a little bit 'outside'. This also + // ensures that getWidth()/getHeight() ain't 0.0 (see division below) + static const double fGrowTopLeft(1.5); + static const double fGrowBottomRight(0.5); + aB2DRange.expand(aB2DRange.getMinimum() - basegfx::B2DPoint(fGrowTopLeft, fGrowTopLeft)); + aB2DRange.expand(aB2DRange.getMaximum() + basegfx::B2DPoint(fGrowBottomRight, fGrowBottomRight)); + + // edge rounding in pixel. Need to convert, value for + // createPolygonFromRect is relative [0.0 .. 1.0] + static const double fEdgeRoundPixel(8.0); + const basegfx::B2DPolygon aPolygon( + basegfx::utils::createPolygonFromRect( + aB2DRange, + fEdgeRoundPixel / aB2DRange.getWidth(), + fEdgeRoundPixel / aB2DRange.getHeight())); + + mxVirtualBufferDevice->SetLineColor(rColor); + + // try to use transparency + if (!mxVirtualBufferDevice->DrawPolyLineDirect( + basegfx::B2DHomMatrix(), + aPolygon, + fLineWidth, + fTransparency, + nullptr, // MM01 + basegfx::B2DLineJoin::Round)) + { + // no transparency, draw without + mxVirtualBufferDevice->DrawPolyLine( + aPolygon, + fLineWidth); + } +} + +Point ScreenshotAnnotationDlg_Impl::GetOffsetInPicture() const +{ + const Size aPixelSizeTarget(maPicture.GetOutputSizePixel()); + + return Point( + aPixelSizeTarget.Width() > maParentDialogSize.Width() ? (aPixelSizeTarget.Width() - maParentDialogSize.Width()) >> 1 : 0, + aPixelSizeTarget.Height() > maParentDialogSize.Height() ? (aPixelSizeTarget.Height() - maParentDialogSize.Height()) >> 1 : 0); +} + +void ScreenshotAnnotationDlg_Impl::RepaintToBuffer( + bool bUseDimmed, + bool bPaintHilight) +{ + if (!mxVirtualBufferDevice) + return; + + // reset with original screenshot bitmap + mxVirtualBufferDevice->DrawBitmapEx( + Point(0, 0), + bUseDimmed ? maDimmedDialogBitmap : maParentDialogBitmap); + + // get various options + const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor()); + const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01); + const bool bIsAntiAliasing(SvtOptionsDrawinglayer::IsAntiAliasing()); + const AntialiasingFlags nOldAA(mxVirtualBufferDevice->GetAntialiasing()); + + if (bIsAntiAliasing) + { + mxVirtualBufferDevice->SetAntialiasing(AntialiasingFlags::Enable); + } + + // paint selected entries + for (auto&& rCandidate : maSelected) + { + static const double fLineWidthEntries(5.0); + PaintScreenShotEntry(*rCandidate, COL_LIGHTRED, fLineWidthEntries, fTransparence * 0.2); + } + + // paint highlighted entry + if (mpHilighted && bPaintHilight) + { + static const double fLineWidthHilight(7.0); + PaintScreenShotEntry(*mpHilighted, aHilightColor, fLineWidthHilight, fTransparence); + } + + if (bIsAntiAliasing) + { + mxVirtualBufferDevice->SetAntialiasing(nOldAA); + } +} + +void ScreenshotAnnotationDlg_Impl::RepaintPictureElement() +{ + if (mxPicture && mxVirtualBufferDevice) + { + // reset image in buffer, use dimmed version and allow highlight + RepaintToBuffer(true, true); + mxPicture->queue_draw(); + } +} + +void ScreenshotAnnotationDlg_Impl::Paint(vcl::RenderContext& rRenderContext) +{ + Point aPos(GetOffsetInPicture()); + Size aSize(mxVirtualBufferDevice->GetOutputSizePixel()); + rRenderContext.DrawOutDev(aPos, aSize, Point(), aSize, *mxVirtualBufferDevice); +} + +void Picture::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + m_pDialog->Paint(rRenderContext); +} + +bool ScreenshotAnnotationDlg_Impl::MouseMove(const MouseEvent& rMouseEvent) +{ + bool bRepaint(false); + + if (maPicture.IsMouseOver()) + { + const weld::ScreenShotEntry* pOldHit = mpHilighted; + const Point aOffset(GetOffsetInPicture()); + const basegfx::B2IPoint aMousePos( + rMouseEvent.GetPosPixel().X() - aOffset.X(), + rMouseEvent.GetPosPixel().Y() - aOffset.Y()); + const weld::ScreenShotEntry* pHit = CheckHit(aMousePos); + + if (pHit && pOldHit != pHit) + { + mpHilighted = const_cast(pHit); + bRepaint = true; + } + } + else if (mpHilighted) + { + mpHilighted = nullptr; + bRepaint = true; + } + + if (bRepaint) + { + RepaintPictureElement(); + } + + return true; +} + +bool Picture::MouseMove(const MouseEvent& rMouseEvent) +{ + if (rMouseEvent.IsEnterWindow()) + m_bMouseOver = true; + if (rMouseEvent.IsLeaveWindow()) + m_bMouseOver = false; + return m_pDialog->MouseMove(rMouseEvent); +} + +bool ScreenshotAnnotationDlg_Impl::MouseButtonUp() +{ + // event in picture frame + bool bRepaint(false); + + if (maPicture.IsMouseOver() && mpHilighted) + { + if (maSelected.erase(mpHilighted) == 0) + { + maSelected.insert(mpHilighted); + } + + OUStringBuffer aBookmarks(maMainMarkupText); + for (auto&& rCandidate : maSelected) + aBookmarks.append(lcl_Bookmark(rCandidate->GetHelpId())); + + mxText->set_text( aBookmarks.makeStringAndClear() ); + bRepaint = true; + } + + if (bRepaint) + { + RepaintPictureElement(); + } + + return true; +} + +bool Picture::MouseButtonUp(const MouseEvent&) +{ + return m_pDialog->MouseButtonUp(); +} + +ScreenshotAnnotationDlg::ScreenshotAnnotationDlg(weld::Dialog& rParentDialog) + : GenericDialogController(&rParentDialog, "cui/ui/screenshotannotationdialog.ui", "ScreenshotAnnotationDialog") +{ + m_pImpl.reset(new ScreenshotAnnotationDlg_Impl(m_xDialog.get(), *m_xBuilder, rParentDialog)); +} + +ScreenshotAnnotationDlg::~ScreenshotAnnotationDlg() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/scriptdlg.cxx b/cui/source/dialogs/scriptdlg.cxx new file mode 100644 index 0000000000..3f1cb9fd33 --- /dev/null +++ b/cui/source/dialogs/scriptdlg.cxx @@ -0,0 +1,1330 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::script; +using namespace css::frame; +using namespace css::document; + +void SvxScriptOrgDialog::delUserData(const weld::TreeIter& rIter) +{ + SFEntry* pUserData = weld::fromId(m_xScriptsBox->get_id(rIter)); + if (pUserData) + { + delete pUserData; + // TBD seem to get a Select event on node that is remove ( below ) + // so need to be able to detect that this node is not to be + // processed in order to do this, setting userData to NULL ( must + // be a better way to do this ) + m_xScriptsBox->set_id(rIter, OUString()); + } +} + +void SvxScriptOrgDialog::deleteTree(const weld::TreeIter& rIter) +{ + delUserData(rIter); + std::unique_ptr xIter = m_xScriptsBox->make_iterator(&rIter); + if (!m_xScriptsBox->iter_children(*xIter)) + return; + + std::unique_ptr xAltIter = m_xScriptsBox->make_iterator(); + bool bNextEntry; + do + { + m_xScriptsBox->copy_iterator(*xIter, *xAltIter); + bNextEntry = m_xScriptsBox->iter_next_sibling(*xAltIter); + deleteTree(*xIter); + m_xScriptsBox->remove(*xIter); + m_xScriptsBox->copy_iterator(*xAltIter, *xIter); + } + while (bNextEntry); +} + +void SvxScriptOrgDialog::deleteAllTree() +{ + std::unique_ptr xIter = m_xScriptsBox->make_iterator(); + if (!m_xScriptsBox->get_iter_first(*xIter)) + return; + + std::unique_ptr xAltIter = m_xScriptsBox->make_iterator(); + // TBD - below is a candidate for a destroyAllTrees method + bool bNextEntry; + do + { + m_xScriptsBox->copy_iterator(*xIter, *xAltIter); + bNextEntry = m_xScriptsBox->iter_next_sibling(*xAltIter); + deleteTree(*xIter); + m_xScriptsBox->remove(*xIter); + m_xScriptsBox->copy_iterator(*xAltIter, *xIter); + } + while (bNextEntry); +} + +void SvxScriptOrgDialog::Init( std::u16string_view language ) +{ + m_xScriptsBox->freeze(); + + deleteAllTree(); + + Reference< browse::XBrowseNode > rootNode; + Reference< XComponentContext > xCtx( + comphelper::getProcessComponentContext() ); + + Sequence< Reference< browse::XBrowseNode > > children; + + try + { + Reference< browse::XBrowseNodeFactory > xFac = browse::theBrowseNodeFactory::get(xCtx); + + rootNode.set( xFac->createView( + browse::BrowseNodeFactoryViewTypes::MACROORGANIZER ) ); + + if ( rootNode.is() && rootNode->hasChildNodes() ) + { + children = rootNode->getChildNodes(); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "Exception getting root browse node from factory"); + // TODO exception handling + } + + Reference xDocumentModel; + for ( const Reference< browse::XBrowseNode >& childNode : std::as_const(children) ) + { + bool app = false; + OUString uiName = childNode->getName(); + OUString factoryURL; + if (uiName == "user") + { + app = true; + uiName = m_sMyMacros; + } + else if (uiName == "share") + { + app = true; + uiName = m_sProdMacros; + } + else + { + xDocumentModel.set(getDocumentModel(xCtx, uiName ), UNO_QUERY); + + if ( xDocumentModel.is() ) + { + Reference< frame::XModuleManager2 > xModuleManager( frame::ModuleManager::create(xCtx) ); + + // get the long name of the document: + Sequence moduleDescr; + try{ + OUString appModule = xModuleManager->identify( xDocumentModel ); + xModuleManager->getByName(appModule) >>= moduleDescr; + } catch(const uno::Exception&) + {} + + for ( const beans::PropertyValue& prop : std::as_const(moduleDescr)) + { + if ( prop.Name == "ooSetupFactoryEmptyDocumentURL" ) + { + prop.Value >>= factoryURL; + break; + } + } + } + } + + Reference< browse::XBrowseNode > langEntries = + getLangNodeFromRootNode( childNode, language ); + + insertEntry( uiName, app ? RID_CUIBMP_HARDDISK : RID_CUIBMP_DOC, + nullptr, true, std::make_unique< SFEntry >( langEntries, xDocumentModel ), factoryURL, false ); + } + + m_xScriptsBox->thaw(); +} + +Reference< XInterface > +SvxScriptOrgDialog::getDocumentModel( Reference< XComponentContext > const & xCtx, std::u16string_view docName ) +{ + Reference< XInterface > xModel; + Reference< frame::XDesktop2 > desktop = frame::Desktop::create(xCtx); + + Reference< container::XEnumerationAccess > componentsAccess = + desktop->getComponents(); + Reference< container::XEnumeration > components = + componentsAccess->createEnumeration(); + while (components->hasMoreElements()) + { + Reference< frame::XModel > model( + components->nextElement(), UNO_QUERY ); + if ( model.is() ) + { + OUString sTdocUrl = ::comphelper::DocumentInfo::getDocumentTitle( model ); + if( sTdocUrl == docName ) + { + xModel = model; + break; + } + } + } + return xModel; +} + +Reference< browse::XBrowseNode > +SvxScriptOrgDialog::getLangNodeFromRootNode( Reference< browse::XBrowseNode > const & rootNode, std::u16string_view language ) +{ + Reference< browse::XBrowseNode > langNode; + + try + { + auto tryFind = [&] { + const Sequence> children = rootNode->getChildNodes(); + const auto it = std::find_if(children.begin(), children.end(), + [&](const Reference& child) { + return child->getName() == language; + }); + return (it != children.end()) ? *it : nullptr; + }; + { + // First try without Java interaction, to avoid warnings for non-JRE-dependent providers + css::uno::ContextLayer layer(comphelper::NoEnableJavaInteractionContext()); + langNode = tryFind(); + } + if (!langNode) + { + // Now try with Java interaction enabled + langNode = tryFind(); + } + } + catch ( Exception& ) + { + // if getChildNodes() throws an exception we just return + // the empty Reference + } + return langNode; +} + +void SvxScriptOrgDialog::RequestSubEntries(const weld::TreeIter& rRootEntry, Reference< css::script::browse::XBrowseNode > const & node, + Reference< XModel >& model) +{ + if (!node.is()) + { + return; + } + + Sequence< Reference< browse::XBrowseNode > > children; + try + { + children = node->getChildNodes(); + } + catch ( Exception& ) + { + // if we catch an exception in getChildNodes then no entries are added + } + + for ( const Reference< browse::XBrowseNode >& childNode : std::as_const(children) ) + { + OUString name( childNode->getName() ); + if ( childNode->getType() != browse::BrowseNodeTypes::SCRIPT) + { + insertEntry(name, RID_CUIBMP_LIB, &rRootEntry, true, std::make_unique(childNode, model), false); + } + else + { + insertEntry(name, RID_CUIBMP_MACRO, &rRootEntry, false, std::make_unique(childNode, model), false); + } + } +} + +void SvxScriptOrgDialog::insertEntry(const OUString& rText, const OUString& rBitmap, + const weld::TreeIter* pParent, bool bChildrenOnDemand, std::unique_ptr && aUserData, + std::u16string_view factoryURL, bool bSelect) +{ + if (rBitmap == RID_CUIBMP_DOC && !factoryURL.empty()) + { + OUString aImage = SvFileInformationManager::GetFileImageId(INetURLObject(factoryURL)); + insertEntry(rText, aImage, pParent, bChildrenOnDemand, std::move(aUserData), bSelect); + return; + } + insertEntry(rText, rBitmap, pParent, bChildrenOnDemand, std::move(aUserData), bSelect); +} + +void SvxScriptOrgDialog::insertEntry( + const OUString& rText, const OUString& rBitmap, const weld::TreeIter* pParent, + bool bChildrenOnDemand, std::unique_ptr && aUserData, bool bSelect) +{ + OUString sId(weld::toId(aUserData.release())); // XXX possible leak + m_xScriptsBox->insert(pParent, -1, &rText, &sId, nullptr, nullptr, + bChildrenOnDemand, m_xScratchIter.get()); + m_xScriptsBox->set_image(*m_xScratchIter, rBitmap); + if (bSelect) + { + m_xScriptsBox->set_cursor(*m_xScratchIter); + m_xScriptsBox->select(*m_xScratchIter); + } +} + +IMPL_LINK(SvxScriptOrgDialog, ExpandingHdl, const weld::TreeIter&, rIter, bool) +{ + SFEntry* userData = weld::fromId(m_xScriptsBox->get_id(rIter)); + + Reference< browse::XBrowseNode > node; + Reference< XModel > model; + if ( userData && !userData->isLoaded() ) + { + node = userData->GetNode(); + model = userData->GetModel(); + RequestSubEntries(rIter, node, model); + userData->setLoaded(); + } + + return true; +} + +// CuiInputDialog ------------------------------------------------------------ +CuiInputDialog::CuiInputDialog(weld::Window * pParent, InputDialogMode nMode) + : GenericDialogController(pParent, "cui/ui/newlibdialog.ui", "NewLibDialog") + , m_xEdit(m_xBuilder->weld_entry("entry")) +{ + m_xEdit->grab_focus(); + + std::unique_ptr xNewLibFT(m_xBuilder->weld_label("newlibft")); + + if ( nMode == InputDialogMode::NEWMACRO ) + { + xNewLibFT->hide(); + std::unique_ptr xNewMacroFT(m_xBuilder->weld_label("newmacroft")); + xNewMacroFT->show(); + std::unique_ptr xAltTitle(m_xBuilder->weld_label("altmacrotitle")); + m_xDialog->set_title(xAltTitle->get_label()); + } + else if ( nMode == InputDialogMode::RENAME ) + { + xNewLibFT->hide(); + std::unique_ptr xRenameFT(m_xBuilder->weld_label("renameft")); + xRenameFT->show(); + std::unique_ptr xAltTitle(m_xBuilder->weld_label("altrenametitle")); + m_xDialog->set_title(xAltTitle->get_label()); + } +} + +// ScriptOrgDialog ------------------------------------------------------------ + +SvxScriptOrgDialog::SvxScriptOrgDialog(weld::Window* pParent, OUString language) + : SfxDialogController(pParent, "cui/ui/scriptorganizer.ui", "ScriptOrganizerDialog") + , m_pParent(pParent) + , m_sLanguage(std::move(language)) + , m_delErrStr(CuiResId(RID_CUISTR_DELFAILED)) + , m_delErrTitleStr(CuiResId(RID_CUISTR_DELFAILED_TITLE)) + , m_delQueryStr(CuiResId(RID_CUISTR_DELQUERY)) + , m_delQueryTitleStr(CuiResId(RID_CUISTR_DELQUERY_TITLE)) + , m_createErrStr(CuiResId(RID_CUISTR_CREATEFAILED)) + , m_createDupStr(CuiResId(RID_CUISTR_CREATEFAILEDDUP)) + , m_createErrTitleStr(CuiResId(RID_CUISTR_CREATEFAILED_TITLE)) + , m_renameErrStr(CuiResId(RID_CUISTR_RENAMEFAILED)) + , m_renameErrTitleStr(CuiResId(RID_CUISTR_RENAMEFAILED_TITLE)) + , m_sMyMacros(CuiResId(RID_CUISTR_MYMACROS)) + , m_sProdMacros(CuiResId(RID_CUISTR_PRODMACROS)) + , m_xScriptsBox(m_xBuilder->weld_tree_view("scripts")) + , m_xScratchIter(m_xScriptsBox->make_iterator()) + , m_xRunButton(m_xBuilder->weld_button("ok")) + , m_xCloseButton(m_xBuilder->weld_button("close")) + , m_xCreateButton(m_xBuilder->weld_button("create")) + , m_xEditButton(m_xBuilder->weld_button("edit")) + , m_xRenameButton(m_xBuilder->weld_button("rename")) + , m_xDelButton(m_xBuilder->weld_button("delete")) +{ + // must be a neater way to deal with the strings than as above + // append the language to the dialog title + OUString winTitle(m_xDialog->get_title()); + winTitle = winTitle.replaceFirst( "%MACROLANG", m_sLanguage ); + m_xDialog->set_title(winTitle); + + m_xScriptsBox->set_size_request(m_xScriptsBox->get_approximate_digit_width() * 45, + m_xScriptsBox->get_height_rows(12)); + + m_xScriptsBox->connect_changed( LINK( this, SvxScriptOrgDialog, ScriptSelectHdl ) ); + m_xScriptsBox->connect_expanding(LINK( this, SvxScriptOrgDialog, ExpandingHdl ) ); + m_xRunButton->connect_clicked( LINK( this, SvxScriptOrgDialog, ButtonHdl ) ); + m_xCloseButton->connect_clicked( LINK( this, SvxScriptOrgDialog, ButtonHdl ) ); + m_xRenameButton->connect_clicked( LINK( this, SvxScriptOrgDialog, ButtonHdl ) ); + m_xEditButton->connect_clicked( LINK( this, SvxScriptOrgDialog, ButtonHdl ) ); + m_xDelButton->connect_clicked( LINK( this, SvxScriptOrgDialog, ButtonHdl ) ); + m_xCreateButton->connect_clicked( LINK( this, SvxScriptOrgDialog, ButtonHdl ) ); + + m_xRunButton->set_sensitive(false); + m_xRenameButton->set_sensitive(false); + m_xEditButton->set_sensitive(false); + m_xDelButton->set_sensitive(false); + m_xCreateButton->set_sensitive(false); + + Init(m_sLanguage); + RestorePreviousSelection(); +} + +SvxScriptOrgDialog::~SvxScriptOrgDialog() +{ + deleteAllTree(); +} + +short SvxScriptOrgDialog::run() +{ + SfxObjectShell *pDoc = SfxObjectShell::GetFirst(); + + // force load of MSPs for all documents + while ( pDoc ) + { + Reference< provider::XScriptProviderSupplier > xSPS( pDoc->GetModel(), UNO_QUERY ); + if ( xSPS.is() ) + { + xSPS->getScriptProvider(); + } + + pDoc = SfxObjectShell::GetNext(*pDoc); + } + + return SfxDialogController::run(); +} + +void SvxScriptOrgDialog::CheckButtons( Reference< browse::XBrowseNode > const & node ) +{ + if ( node.is() ) + { + if ( node->getType() == browse::BrowseNodeTypes::SCRIPT) + { + m_xRunButton->set_sensitive(true); + } + else + { + m_xRunButton->set_sensitive(false); + } + Reference< beans::XPropertySet > xProps( node, UNO_QUERY ); + + if ( !xProps.is() ) + { + m_xEditButton->set_sensitive(false); + m_xDelButton->set_sensitive(false); + m_xCreateButton->set_sensitive(false); + m_xRunButton->set_sensitive(false); + return; + } + + OUString sName("Editable"); + + if ( getBoolProperty( xProps, sName ) ) + { + m_xEditButton->set_sensitive(true); + } + else + { + m_xEditButton->set_sensitive(false); + } + + sName = "Deletable"; + + if ( getBoolProperty( xProps, sName ) ) + { + m_xDelButton->set_sensitive(true); + } + else + { + m_xDelButton->set_sensitive(false); + } + + sName = "Creatable"; + + if ( getBoolProperty( xProps, sName ) ) + { + m_xCreateButton->set_sensitive(true); + } + else + { + m_xCreateButton->set_sensitive(false); + } + + sName = "Renamable"; + + if ( getBoolProperty( xProps, sName ) ) + { + m_xRenameButton->set_sensitive(true); + } + else + { + m_xRenameButton->set_sensitive(false); + } + } + else + { + // no node info available, disable all configurable actions + m_xDelButton->set_sensitive(false); + m_xCreateButton->set_sensitive(false); + m_xEditButton->set_sensitive(false); + m_xRunButton->set_sensitive(false); + m_xRenameButton->set_sensitive(false); + } +} + +IMPL_LINK_NOARG(SvxScriptOrgDialog, ScriptSelectHdl, weld::TreeView&, void) +{ + std::unique_ptr xIter = m_xScriptsBox->make_iterator(); + if (!m_xScriptsBox->get_selected(xIter.get())) + return; + + SFEntry* userData = weld::fromId(m_xScriptsBox->get_id(*xIter)); + + Reference< browse::XBrowseNode > node; + if (userData) + { + node = userData->GetNode(); + CheckButtons(node); + } +} + +IMPL_LINK(SvxScriptOrgDialog, ButtonHdl, weld::Button&, rButton, void) +{ + if ( &rButton == m_xCloseButton.get() ) + { + StoreCurrentSelection(); + m_xDialog->response(RET_CANCEL); + } + if (!(&rButton == m_xEditButton.get() || + &rButton == m_xCreateButton.get() || + &rButton == m_xDelButton.get() || + &rButton == m_xRunButton.get() || + &rButton == m_xRenameButton.get())) + + return; + + std::unique_ptr xIter = m_xScriptsBox->make_iterator(); + if (!m_xScriptsBox->get_selected(xIter.get())) + return; + SFEntry* userData = weld::fromId(m_xScriptsBox->get_id(*xIter)); + if (!userData) + return; + + Reference< browse::XBrowseNode > node; + Reference< XModel > xModel; + + node = userData->GetNode(); + xModel = userData->GetModel(); + + if ( !node.is() ) + { + return; + } + + if (&rButton == m_xRunButton.get()) + { + OUString tmpString; + Reference< beans::XPropertySet > xProp( node, UNO_QUERY ); + Reference< provider::XScriptProvider > mspNode; + if( !xProp.is() ) + { + return; + } + + if ( xModel.is() ) + { + Reference< XEmbeddedScripts > xEmbeddedScripts( xModel, UNO_QUERY); + if( !xEmbeddedScripts.is() ) + { + return; + } + + if (!xEmbeddedScripts->getAllowMacroExecution()) + { + // Please FIXME: Show a message box if AllowMacroExecution is false + return; + } + } + + std::unique_ptr xParentIter = m_xScriptsBox->make_iterator(xIter.get()); + bool bParent = m_xScriptsBox->iter_parent(*xParentIter); + while (bParent && !mspNode.is() ) + { + SFEntry* mspUserData = weld::fromId(m_xScriptsBox->get_id(*xParentIter)); + mspNode.set( mspUserData->GetNode() , UNO_QUERY ); + bParent = m_xScriptsBox->iter_parent(*xParentIter); + } + xProp->getPropertyValue("URI") >>= tmpString; + const OUString scriptURL( tmpString ); + + if ( mspNode.is() ) + { + try + { + Reference< provider::XScript > xScript( + mspNode->getScript( scriptURL ), UNO_SET_THROW ); + + const Sequence< Any > args(0); + Sequence< sal_Int16 > outIndex; + Sequence< Any > outArgs( 0 ); + xScript->invoke( args, outIndex, outArgs ); + } + catch ( reflection::InvocationTargetException& ite ) + { + SvxScriptErrorDialog::ShowAsyncErrorDialog(m_pParent, css::uno::Any(ite)); + } + catch ( provider::ScriptFrameworkErrorException& ite ) + { + SvxScriptErrorDialog::ShowAsyncErrorDialog(m_pParent, css::uno::Any(ite)); + } + catch ( RuntimeException& re ) + { + SvxScriptErrorDialog::ShowAsyncErrorDialog(m_pParent, css::uno::Any(re)); + } + catch ( Exception& e ) + { + SvxScriptErrorDialog::ShowAsyncErrorDialog(m_pParent, css::uno::Any(e)); + } + } + StoreCurrentSelection(); + m_xDialog->response(RET_CANCEL); + } + else if ( &rButton == m_xEditButton.get() ) + { + Reference< script::XInvocation > xInv( node, UNO_QUERY ); + if ( xInv.is() ) + { + StoreCurrentSelection(); + m_xDialog->response(RET_CANCEL); + Sequence< Any > args(0); + Sequence< Any > outArgs( 0 ); + Sequence< sal_Int16 > outIndex; + try + { + // ISSUE need code to run script here + xInv->invoke( "Editable", args, outIndex, outArgs ); + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "Caught exception trying to invoke" ); + } + } + } + else if ( &rButton == m_xCreateButton.get() ) + { + createEntry(*xIter); + } + else if ( &rButton == m_xDelButton.get() ) + { + deleteEntry(*xIter); + } + else if ( &rButton == m_xRenameButton.get() ) + { + renameEntry(*xIter); + } +} + +Reference< browse::XBrowseNode > SvxScriptOrgDialog::getBrowseNode(const weld::TreeIter& rEntry) +{ + Reference< browse::XBrowseNode > node; + SFEntry* userData = weld::fromId(m_xScriptsBox->get_id(rEntry)); + if (userData) + { + node = userData->GetNode(); + } + return node; +} + +Reference< XModel > SvxScriptOrgDialog::getModel(const weld::TreeIter& rEntry) +{ + Reference< XModel > model; + SFEntry* userData = weld::fromId(m_xScriptsBox->get_id(rEntry)); + if ( userData ) + { + model = userData->GetModel(); + } + return model; +} + +void SvxScriptOrgDialog::createEntry(const weld::TreeIter& rEntry) +{ + + Reference< browse::XBrowseNode > aChildNode; + Reference< browse::XBrowseNode > node = getBrowseNode( rEntry ); + Reference< script::XInvocation > xInv( node, UNO_QUERY ); + + if ( xInv.is() ) + { + OUString aNewName; + OUString aNewStdName; + InputDialogMode nMode = InputDialogMode::NEWLIB; + if (m_xScriptsBox->get_iter_depth(rEntry) == 0) + { + aNewStdName = "Library" ; + } + else + { + aNewStdName = "Macro" ; + nMode = InputDialogMode::NEWMACRO; + } + //do we need L10N for this? ie something like: + //String aNewStdName( ResId( STR_STDMODULENAME ) ); + bool bValid = false; + sal_Int32 i = 1; + + Sequence< Reference< browse::XBrowseNode > > childNodes; + // no children => ok to create Parcel1 or Script1 without checking + try + { + if( !node->hasChildNodes() ) + { + aNewName = aNewStdName + OUString::number(i); + bValid = true; + } + else + { + childNodes = node->getChildNodes(); + } + } + catch ( Exception& ) + { + // ignore, will continue on with empty sequence + } + + OUString extn; + while ( !bValid ) + { + aNewName = aNewStdName + OUString::number(i); + bool bFound = false; + if(childNodes.hasElements() ) + { + OUString nodeName = childNodes[0]->getName(); + sal_Int32 extnPos = nodeName.lastIndexOf( '.' ); + if(extnPos>0) + extn = nodeName.copy(extnPos); + } + for( const Reference< browse::XBrowseNode >& n : std::as_const(childNodes) ) + { + if (Concat2View(aNewName+extn) == n->getName()) + { + bFound = true; + break; + } + } + if( bFound ) + { + i++; + } + else + { + bValid = true; + } + } + + CuiInputDialog aNewDlg(m_xDialog.get(), nMode); + aNewDlg.SetObjectName(aNewName); + + do + { + if (aNewDlg.run() && !aNewDlg.GetObjectName().isEmpty()) + { + OUString aUserSuppliedName = aNewDlg.GetObjectName(); + bValid = true; + for( const Reference< browse::XBrowseNode >& n : std::as_const(childNodes) ) + { + if (Concat2View(aUserSuppliedName+extn) == n->getName()) + { + bValid = false; + OUString aError = m_createErrStr + m_createDupStr; + + std::unique_ptr xErrorBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, aError)); + xErrorBox->set_title(m_createErrTitleStr); + xErrorBox->run(); + aNewDlg.SetObjectName(aNewName); + break; + } + } + if( bValid ) + aNewName = aUserSuppliedName; + } + else + { + // user hit cancel or hit OK with nothing in the editbox + + return; + } + } + while ( !bValid ); + + // open up parent node (which ensures it's loaded) + m_xScriptsBox->expand_row(rEntry); + + Sequence< Any > args{ Any(aNewName) }; + Sequence< Any > outArgs; + Sequence< sal_Int16 > outIndex; + try + { + Any aResult = xInv->invoke( "Creatable", args, outIndex, outArgs ); + Reference< browse::XBrowseNode > newNode( aResult, UNO_QUERY ); + aChildNode = newNode; + + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "Caught exception trying to Create" ); + } + } + if ( aChildNode.is() ) + { + OUString aChildName = aChildNode->getName(); + + Reference xDocumentModel = getModel( rEntry ); + + // ISSUE do we need to remove all entries for parent + // to achieve sort? Just need to determine position + // -- Basic doesn't do this on create. + // Suppose we could avoid this too. -> created nodes are + // not in alphabetical order + if ( aChildNode->getType() == browse::BrowseNodeTypes::SCRIPT ) + { + insertEntry(aChildName, RID_CUIBMP_MACRO, &rEntry, false, + std::make_unique(aChildNode,xDocumentModel), true); + } + else + { + insertEntry(aChildName, RID_CUIBMP_LIB, &rEntry, false, + std::make_unique(aChildNode,xDocumentModel), true); + + // If the Parent is not loaded then set to + // loaded, this will prevent RequestingChildren ( called + // from vcl via RequestingChildren ) from + // creating new ( duplicate ) children + SFEntry* userData = weld::fromId(m_xScriptsBox->get_id(rEntry)); + if ( userData && !userData->isLoaded() ) + { + userData->setLoaded(); + } + } + } + else + { + //ISSUE L10N & message from exception? + OUString aError( m_createErrStr ); + std::unique_ptr xErrorBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, aError)); + xErrorBox->set_title(m_createErrTitleStr); + xErrorBox->run(); + } +} + +void SvxScriptOrgDialog::renameEntry(const weld::TreeIter& rEntry) +{ + + Reference< browse::XBrowseNode > aChildNode; + Reference< browse::XBrowseNode > node = getBrowseNode(rEntry); + Reference< script::XInvocation > xInv( node, UNO_QUERY ); + + if ( xInv.is() ) + { + OUString aNewName = node->getName(); + sal_Int32 extnPos = aNewName.lastIndexOf( '.' ); + if(extnPos>0) + { + aNewName = aNewName.copy(0,extnPos); + } + CuiInputDialog aNewDlg(m_xDialog.get(), InputDialogMode::RENAME); + aNewDlg.SetObjectName(aNewName); + + if (!aNewDlg.run() || aNewDlg.GetObjectName().isEmpty()) + return; // user hit cancel or hit OK with nothing in the editbox + + aNewName = aNewDlg.GetObjectName(); + + Sequence< Any > args{ Any(aNewName) }; + Sequence< Any > outArgs; + Sequence< sal_Int16 > outIndex; + try + { + Any aResult = xInv->invoke( "Renamable", args, outIndex, outArgs ); + Reference< browse::XBrowseNode > newNode( aResult, UNO_QUERY ); + aChildNode = newNode; + + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "Caught exception trying to Rename" ); + } + } + if ( aChildNode.is() ) + { + m_xScriptsBox->set_text(rEntry, aChildNode->getName()); + m_xScriptsBox->set_cursor(rEntry); + m_xScriptsBox->select(rEntry); + + } + else + { + //ISSUE L10N & message from exception? + OUString aError( m_renameErrStr ); + std::unique_ptr xErrorBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, aError)); + xErrorBox->set_title(m_renameErrTitleStr); + xErrorBox->run(); + } +} + +void SvxScriptOrgDialog::deleteEntry(const weld::TreeIter& rEntry) +{ + bool result = false; + Reference< browse::XBrowseNode > node = getBrowseNode(rEntry); + // ISSUE L10N string & can we center list? + OUString aQuery = m_delQueryStr + getListOfChildren( node, 0 ); + std::unique_ptr xQueryBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Question, VclButtonsType::YesNo, aQuery)); + xQueryBox->set_title(m_delQueryTitleStr); + if (xQueryBox->run() == RET_NO) + { + return; + } + + Reference< script::XInvocation > xInv( node, UNO_QUERY ); + if ( xInv.is() ) + { + Sequence< Any > args( 0 ); + Sequence< Any > outArgs( 0 ); + Sequence< sal_Int16 > outIndex; + try + { + Any aResult = xInv->invoke( "Deletable", args, outIndex, outArgs ); + aResult >>= result; // or do we just assume true if no exception ? + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "Caught exception trying to delete" ); + } + } + + if ( result ) + { + deleteTree(rEntry); + m_xScriptsBox->remove(rEntry); + } + else + { + //ISSUE L10N & message from exception? + std::unique_ptr xErrorBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, m_delErrStr)); + xErrorBox->set_title(m_delErrTitleStr); + xErrorBox->run(); + } + +} + +bool SvxScriptOrgDialog::getBoolProperty( Reference< beans::XPropertySet > const & xProps, + OUString const & propName ) +{ + bool result = false; + try + { + xProps->getPropertyValue( propName ) >>= result; + } + catch ( Exception& ) + { + return result; + } + return result; +} + +OUString SvxScriptOrgDialog::getListOfChildren( const Reference< browse::XBrowseNode >& node, int depth ) +{ + OUStringBuffer result = "\n"; + for( int i=0;i<=depth;i++ ) + { + result.append("\t"); + } + result.append(node->getName()); + + try + { + if ( node->hasChildNodes() ) + { + const Sequence< Reference< browse::XBrowseNode > > children + = node->getChildNodes(); + for( const Reference< browse::XBrowseNode >& n : children ) + { + result.append( getListOfChildren( n , depth+1 ) ); + } + } + } + catch ( Exception& ) + { + // ignore, will return an empty string + } + + return result.makeStringAndClear(); +} + +Selection_hash SvxScriptOrgDialog::m_lastSelection; + +void SvxScriptOrgDialog::StoreCurrentSelection() +{ + std::unique_ptr xIter = m_xScriptsBox->make_iterator(); + if (!m_xScriptsBox->get_selected(xIter.get())) + return; + OUString aDescription; + bool bEntry; + do + { + aDescription = m_xScriptsBox->get_text(*xIter) + aDescription; + bEntry = m_xScriptsBox->iter_parent(*xIter); + if (bEntry) + aDescription = ";" + aDescription; + } + while (bEntry); + m_lastSelection[m_sLanguage] = aDescription; +} + +void SvxScriptOrgDialog::RestorePreviousSelection() +{ + OUString aStoredEntry = m_lastSelection[ m_sLanguage ]; + if( aStoredEntry.isEmpty() ) + return; + std::unique_ptr xEntry; + std::unique_ptr xTmpEntry(m_xScriptsBox->make_iterator()); + sal_Int32 nIndex = 0; + while (nIndex != -1) + { + std::u16string_view aTmp( o3tl::getToken(aStoredEntry, 0, ';', nIndex ) ); + + bool bTmpEntry; + if (!xEntry) + { + xEntry = m_xScriptsBox->make_iterator(); + bTmpEntry = m_xScriptsBox->get_iter_first(*xEntry); + m_xScriptsBox->copy_iterator(*xEntry, *xTmpEntry); + } + else + { + m_xScriptsBox->copy_iterator(*xEntry, *xTmpEntry); + bTmpEntry = m_xScriptsBox->iter_children(*xTmpEntry); + } + + while (bTmpEntry) + { + if (m_xScriptsBox->get_text(*xTmpEntry) == aTmp) + { + m_xScriptsBox->copy_iterator(*xTmpEntry, *xEntry); + break; + } + bTmpEntry = m_xScriptsBox->iter_next_sibling(*xTmpEntry); + } + + if (!bTmpEntry) + break; + + m_xScriptsBox->expand_row(*xEntry); + } + + if (xEntry) + { + m_xScriptsBox->set_cursor(*xEntry); + ScriptSelectHdl(*m_xScriptsBox); + } +} + +namespace { + +OUString ReplaceString( + const OUString& source, + std::u16string_view token, + std::u16string_view value ) +{ + sal_Int32 pos = source.indexOf( token ); + + if ( pos != -1 && !value.empty() ) + { + return source.replaceAt( pos, token.size(), value ); + } + else + { + return source; + } +} + +OUString FormatErrorString( + const OUString& unformatted, + std::u16string_view language, + std::u16string_view script, + std::u16string_view line, + std::u16string_view type, + std::u16string_view message ) +{ + OUString result = unformatted; + + result = ReplaceString(result, u"%LANGUAGENAME", language ); + result = ReplaceString(result, u"%SCRIPTNAME", script ); + result = ReplaceString(result, u"%LINENUMBER", line ); + + if ( !type.empty() ) + { + result += "\n\n" + CuiResId(RID_CUISTR_ERROR_TYPE_LABEL) + " " + type; + } + + if ( !message.empty() ) + { + result += "\n\n" + CuiResId(RID_CUISTR_ERROR_MESSAGE_LABEL) + " " + message; + } + + return result; +} + +OUString GetErrorMessage( + const provider::ScriptErrorRaisedException& eScriptError ) +{ + OUString unformatted = CuiResId( RID_CUISTR_ERROR_AT_LINE ); + + OUString unknown("UNKNOWN"); + OUString language = unknown; + OUString script = unknown; + OUString line = unknown; + OUString message = eScriptError.Message; + + if ( !eScriptError.language.isEmpty() ) + { + language = eScriptError.language; + } + + if ( !eScriptError.scriptName.isEmpty() ) + { + script = eScriptError.scriptName; + } + + if ( !eScriptError.Message.isEmpty() ) + { + message = eScriptError.Message; + } + if ( eScriptError.lineNum != -1 ) + { + line = OUString::number( eScriptError.lineNum ); + unformatted = CuiResId( RID_CUISTR_ERROR_AT_LINE ); + } + else + { + unformatted = CuiResId( RID_CUISTR_ERROR_RUNNING ); + } + + return FormatErrorString( + unformatted, language, script, line, u"", message ); +} + +OUString GetErrorMessage( + const provider::ScriptExceptionRaisedException& eScriptException ) +{ + OUString unformatted = CuiResId( RID_CUISTR_EXCEPTION_AT_LINE ); + + OUString unknown("UNKNOWN"); + OUString language = unknown; + OUString script = unknown; + OUString line = unknown; + OUString type = unknown; + OUString message = eScriptException.Message; + + if ( !eScriptException.language.isEmpty() ) + { + language = eScriptException.language; + } + if ( !eScriptException.scriptName.isEmpty() ) + { + script = eScriptException.scriptName; + } + + if ( !eScriptException.Message.isEmpty() ) + { + message = eScriptException.Message; + } + + if ( eScriptException.lineNum != -1 ) + { + line = OUString::number( eScriptException.lineNum ); + unformatted = CuiResId( RID_CUISTR_EXCEPTION_AT_LINE ); + } + else + { + unformatted = CuiResId( RID_CUISTR_EXCEPTION_RUNNING ); + } + + if ( !eScriptException.exceptionType.isEmpty() ) + { + type = eScriptException.exceptionType; + } + + return FormatErrorString( + unformatted, language, script, line, type, message ); + +} +OUString GetErrorMessage( + const provider::ScriptFrameworkErrorException& sError ) +{ + OUString unformatted = CuiResId( RID_CUISTR_FRAMEWORK_ERROR_RUNNING ); + + OUString language("UNKNOWN"); + + OUString script("UNKNOWN"); + + OUString message; + + if ( !sError.scriptName.isEmpty() ) + { + script = sError.scriptName; + } + if ( !sError.language.isEmpty() ) + { + language = sError.language; + } + if ( sError.errorType == provider::ScriptFrameworkErrorType::NOTSUPPORTED ) + { + message = CuiResId(RID_CUISTR_ERROR_LANG_NOT_SUPPORTED); + message = ReplaceString(message, u"%LANGUAGENAME", language ); + + } + else + { + message = sError.Message; + } + return FormatErrorString( + unformatted, language, script, u"", std::u16string_view(), message ); +} + +OUString GetErrorMessage( const css::uno::Any& aException ) +{ + if ( aException.getValueType() == + cppu::UnoType::get()) + { + reflection::InvocationTargetException ite; + aException >>= ite; + if ( ite.TargetException.getValueType() == cppu::UnoType::get()) + { + // Error raised by script + provider::ScriptErrorRaisedException scriptError; + ite.TargetException >>= scriptError; + return GetErrorMessage( scriptError ); + } + else if ( ite.TargetException.getValueType() == cppu::UnoType::get()) + { + // Exception raised by script + provider::ScriptExceptionRaisedException scriptException; + ite.TargetException >>= scriptException; + return GetErrorMessage( scriptException ); + } + else + { + // Unknown error, shouldn't happen + // OSL_ASSERT(...) + } + + } + else if ( aException.getValueType() == cppu::UnoType::get()) + { + // A Script Framework error has occurred + provider::ScriptFrameworkErrorException sfe; + aException >>= sfe; + return GetErrorMessage( sfe ); + + } + // unknown exception + auto msg = aException.getValueTypeName(); + Exception e; + if ( (aException >>= e) && !e.Message.isEmpty() ) + { + msg += ": " + e.Message; + } + return msg; +} + +} + +// Show Error dialog asynchronously +void SvxScriptErrorDialog::ShowAsyncErrorDialog( weld::Window* pParent, css::uno::Any const & aException ) +{ + SolarMutexGuard aGuard; + + // Pass a copy of the message to the ShowDialog method as the + // SvxScriptErrorDialog may be deleted before ShowDialog is called + DialogData* pData = new DialogData; + pData->sMessage = GetErrorMessage(aException); + pData->pParent = pParent; + Application::PostUserEvent( + LINK( nullptr, SvxScriptErrorDialog, ShowDialog ), + pData ); +} + +IMPL_STATIC_LINK( SvxScriptErrorDialog, ShowDialog, void*, p, void ) +{ + std::unique_ptr xData(static_cast(p)); + OUString message = xData->sMessage; + + if ( message.isEmpty() ) + message = CuiResId( RID_CUISTR_ERROR_TITLE ); + + std::shared_ptr xBox; + xBox.reset(Application::CreateMessageDialog( + xData->pParent, + VclMessageType::Warning, + VclButtonsType::Ok, + message)); + + xBox->set_title(CuiResId(RID_CUISTR_ERROR_TITLE)); + + xBox->runAsync(xBox, [](sal_Int32 /*nResult*/) {}); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/sdrcelldlg.cxx b/cui/source/dialogs/sdrcelldlg.cxx new file mode 100644 index 0000000000..6b10e5c688 --- /dev/null +++ b/cui/source/dialogs/sdrcelldlg.cxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SvxFormatCellsDialog::SvxFormatCellsDialog(weld::Window* pParent, const SfxItemSet& rAttr, const SdrModel& rModel, bool bStyle) + : SfxTabDialogController(pParent, "cui/ui/formatcellsdialog.ui", "FormatCellsDialog", &rAttr, bStyle) + , mrOutAttrs(rAttr) + , mpColorTab(rModel.GetColorList()) + , mnColorTabState ( ChangeType::NONE ) + , mpGradientList(rModel.GetGradientList()) + , mpHatchingList(rModel.GetHatchList()) + , mpBitmapList(rModel.GetBitmapList()) + , mpPatternList(rModel.GetPatternList()) +{ + AddTabPage("name", RID_SVXPAGE_CHAR_NAME); + AddTabPage("effects", RID_SVXPAGE_CHAR_EFFECTS); + AddTabPage("border", RID_SVXPAGE_BORDER ); + AddTabPage("area", RID_SVXPAGE_AREA); + + if (bStyle) + { + AddTabPage("position", RID_SVXPAGE_CHAR_POSITION); + AddTabPage("highlight", RID_SVXPAGE_BKG); + AddTabPage("indentspacing", RID_SVXPAGE_STD_PARAGRAPH); + AddTabPage("alignment", SvxParaAlignTabPage::Create, SvxParaAlignTabPage::GetSdrRanges); + RemoveTabPage("shadow"); + } + else + { + RemoveTabPage("position"); + RemoveTabPage("highlight"); + RemoveTabPage("indentspacing"); + RemoveTabPage("alignment"); + AddTabPage("shadow", SvxShadowTabPage::Create, nullptr); + RemoveStandardButton(); + } + + if (bStyle && SvtCJKOptions::IsAsianTypographyEnabled()) + AddTabPage("asian", RID_SVXPAGE_PARA_ASIAN); + else + RemoveTabPage("asian"); +} + +void SvxFormatCellsDialog::PageCreated(const OUString& rId, SfxTabPage &rPage) +{ + if (rId == "area") + { + SvxAreaTabPage& rAreaPage = static_cast(rPage); + rAreaPage.SetColorList( mpColorTab ); + rAreaPage.SetGradientList( mpGradientList ); + rAreaPage.SetHatchingList( mpHatchingList ); + rAreaPage.SetBitmapList( mpBitmapList ); + rAreaPage.SetPatternList( mpPatternList ); + rAreaPage.ActivatePage( mrOutAttrs ); + } + else if (rId == "border") + { + SvxBorderTabPage& rBorderPage = static_cast(rPage); + rBorderPage.SetTableMode(); + } + else if (rId == "shadow") + { + static_cast(rPage).SetColorList( mpColorTab ); + static_cast(rPage).SetColorChgd( &mnColorTabState ); + } + else if (rId == "alignment") + { + static_cast(rPage).EnableSdrVertAlign(); + } + else if (rId == "highlight") + { + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE,static_cast(SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR))); + rPage.PageCreated(aSet); + } + else + SfxTabDialogController::PageCreated(rId, rPage); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/showcols.cxx b/cui/source/dialogs/showcols.cxx new file mode 100644 index 0000000000..ae468b0200 --- /dev/null +++ b/cui/source/dialogs/showcols.cxx @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include + +constexpr OUString CUIFM_PROP_HIDDEN = u"Hidden"_ustr; +constexpr OUStringLiteral CUIFM_PROP_LABEL = u"Label"; + +FmShowColsDialog::FmShowColsDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/showcoldialog.ui", "ShowColDialog") + , m_xList(m_xBuilder->weld_tree_view("treeview")) + , m_xOK(m_xBuilder->weld_button("ok")) +{ + m_xList->set_size_request(m_xList->get_approximate_digit_width() * 40, + m_xList->get_height_rows(8)); + m_xList->set_selection_mode(SelectionMode::Multiple); + m_xOK->connect_clicked(LINK(this, FmShowColsDialog, OnClickedOk)); +} + +FmShowColsDialog::~FmShowColsDialog() {} + +IMPL_LINK_NOARG(FmShowColsDialog, OnClickedOk, weld::Button&, void) +{ + DBG_ASSERT( + m_xColumns.is(), + "FmShowColsDialog::OnClickedOk : you should call SetColumns before executing the dialog !"); + if (m_xColumns.is()) + { + css::uno::Reference xCol; + auto nSelectedRows = m_xList->get_selected_rows(); + for (auto i : nSelectedRows) + { + m_xColumns->getByIndex(m_xList->get_id(i).toInt32()) >>= xCol; + if (xCol.is()) + { + try + { + xCol->setPropertyValue(CUIFM_PROP_HIDDEN, css::uno::Any(false)); + } + catch (...) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", + "FmShowColsDialog::OnClickedOk Exception occurred!"); + } + } + } + } + + m_xDialog->response(RET_OK); +} + +void FmShowColsDialog::SetColumns(const css::uno::Reference& xCols) +{ + DBG_ASSERT(xCols.is(), "FmShowColsDialog::SetColumns : invalid columns !"); + if (!xCols.is()) + return; + m_xColumns = xCols.get(); + + m_xList->clear(); + + css::uno::Reference xCurCol; + OUString sCurName; + for (sal_Int32 i = 0; i < xCols->getCount(); ++i) + { + sCurName.clear(); + xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY); + bool bIsHidden = false; + try + { + css::uno::Any aHidden = xCurCol->getPropertyValue(CUIFM_PROP_HIDDEN); + bIsHidden = ::comphelper::getBOOL(aHidden); + + OUString sName; + xCurCol->getPropertyValue(CUIFM_PROP_LABEL) >>= sName; + sCurName = sName; + } + catch (...) + { + TOOLS_WARN_EXCEPTION("cui.dialogs", "FmShowColsDialog::SetColumns Exception occurred!"); + } + + // if the col is hidden, put it into the list + if (bIsHidden) + m_xList->append(OUString::number(i), sCurName); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/signature-line-draw.svg b/cui/source/dialogs/signature-line-draw.svg new file mode 100644 index 0000000000..b8552c41b8 --- /dev/null +++ b/cui/source/dialogs/signature-line-draw.svg @@ -0,0 +1,36 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + [SIGNED_BY] +[SIGNER_NAME] +[DATE] + + + diff --git a/cui/source/dialogs/signature-line.svg b/cui/source/dialogs/signature-line.svg new file mode 100644 index 0000000000..7ad3fad307 --- /dev/null +++ b/cui/source/dialogs/signature-line.svg @@ -0,0 +1,30 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + 150139132512: XPATHSTROKE_SEQ_BEGIN132133109512: XPATHSTROKE_SEQ_END140150139133132111140150512: XTEXT_PAINTSHAPE_BEGIN138136135134113type: Text; content: [SIGNATURE]; [SIGNATURE]512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOL512: XTEXT_EOP512: XTEXT_PAINTSHAPE_END150512: XTEXT_PAINTSHAPE_BEGIN138136135134113type: Text; content: [SIGNER_NAME]; [SIGNER_NAME]512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOL512: XTEXT_EOP512: XTEXT_PAINTSHAPE_END150512: XTEXT_PAINTSHAPE_BEGIN138136135134113type: Text; content: [SIGNER_TITLE]; [SIGNER_TITLE]512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOL512: XTEXT_EOP512: XTEXT_PAINTSHAPE_END150512: XTEXT_PAINTSHAPE_BEGIN138136135134113type: Text; content: [SIGNED_BY]; [SIGNED_BY]512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOL512: XTEXT_EOP512: XTEXT_PAINTSHAPE_END150512: XTEXT_PAINTSHAPE_BEGIN138136135134113type: Text; content: [DATE]; [DATE]512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOL512: XTEXT_EOP512: XTEXT_PAINTSHAPE_END150512: XTEXT_PAINTSHAPE_BEGIN138136135134113type: Text; content: [INVALID_SIGNATURE]; [INVALID_SIGNATURE]512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOC512: XTEXT_EOW512: XTEXT_EOL512: XTEXT_EOP512: XTEXT_PAINTSHAPE_END[SIGNATURE_IMAGE] + diff --git a/cui/source/dialogs/splitcelldlg.cxx b/cui/source/dialogs/splitcelldlg.cxx new file mode 100644 index 0000000000..14146b44c3 --- /dev/null +++ b/cui/source/dialogs/splitcelldlg.cxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +SvxSplitTableDlg::SvxSplitTableDlg(weld::Window *pParent, bool bIsTableVertical, tools::Long nMaxVertical, tools::Long nMaxHorizontal) + : GenericDialogController(pParent, "cui/ui/splitcellsdialog.ui", "SplitCellsDialog") + , m_xCountEdit(m_xBuilder->weld_spin_button("countnf")) + , m_xHorzBox(!bIsTableVertical ? m_xBuilder->weld_radio_button("hori") : m_xBuilder->weld_radio_button("vert")) + , m_xVertBox(!bIsTableVertical ? m_xBuilder->weld_radio_button("vert") : m_xBuilder->weld_radio_button("hori")) + , m_xPropCB(m_xBuilder->weld_check_button("prop")) + , mnMaxVertical(nMaxVertical) + , mnMaxHorizontal(nMaxHorizontal) +{ + m_xHorzBox->connect_toggled(LINK(this, SvxSplitTableDlg, ToggleHdl)); + m_xVertBox->connect_toggled(LINK(this, SvxSplitTableDlg, ToggleHdl)); + + if (mnMaxVertical < 2) + { + if (!bIsTableVertical) + m_xVertBox->set_sensitive(false); + else + m_xHorzBox->set_sensitive(false); + } + + //exchange the meaning of horizontal and vertical for vertical text + if (bIsTableVertical) + { + int nHorzTopAttach = m_xHorzBox->get_grid_top_attach(); + int nVertTopAttach = m_xVertBox->get_grid_top_attach(); + m_xHorzBox->set_grid_top_attach(nVertTopAttach); + m_xVertBox->set_grid_top_attach(nHorzTopAttach); + m_xHorzBox->set_active(m_xVertBox->get_active()); + } +} + +IMPL_LINK(SvxSplitTableDlg, ToggleHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + const bool bIsVert = m_xVertBox->get_active(); + tools::Long nMax = bIsVert ? mnMaxVertical : mnMaxHorizontal; + m_xPropCB->set_sensitive(!bIsVert); + m_xCountEdit->set_max(nMax); +} + +bool SvxSplitTableDlg::IsHorizontal() const +{ + return m_xHorzBox->get_active(); +} + +bool SvxSplitTableDlg::IsProportional() const +{ + return m_xPropCB->get_active() && m_xHorzBox->get_active(); +} + +tools::Long SvxSplitTableDlg::GetCount() const +{ + return m_xCountEdit->get_value(); +} + +void SvxSplitTableDlg::SetSplitVerticalByDefault() +{ + if( mnMaxVertical >= 2 ) + m_xVertBox->set_active(true); // tdf#60242 +} + +bool SvxAbstractSplitTableDialog_Impl::IsHorizontal() const +{ + return m_xDlg->IsHorizontal(); +} + +bool SvxAbstractSplitTableDialog_Impl::IsProportional() const +{ + return m_xDlg->IsProportional(); +} + +tools::Long SvxAbstractSplitTableDialog_Impl::GetCount() const +{ + return m_xDlg->GetCount(); +} + +void SvxAbstractSplitTableDialog_Impl::SetSplitVerticalByDefault() +{ + m_xDlg->SetSplitVerticalByDefault(); +} + +short SvxAbstractSplitTableDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool SvxAbstractSplitTableDialog_Impl::StartExecuteAsync(AsyncContext& rContext) +{ + return weld::DialogController::runAsync(m_xDlg, rContext.maEndDialogFn); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/srchxtra.cxx b/cui/source/dialogs/srchxtra.cxx new file mode 100644 index 0000000000..4c672b9839 --- /dev/null +++ b/cui/source/dialogs/srchxtra.cxx @@ -0,0 +1,232 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SvxSearchFormatDialog::SvxSearchFormatDialog(weld::Window* pParent, const SfxItemSet& rSet) + : SfxTabDialogController(pParent, "cui/ui/searchformatdialog.ui", "SearchFormatDialog", &rSet) +{ + AddTabPage("font", SvxCharNamePage::Create, nullptr); + AddTabPage("fonteffects", SvxCharEffectsPage::Create, nullptr); + AddTabPage("position", SvxCharPositionPage::Create, nullptr); + AddTabPage("asianlayout", SvxCharTwoLinesPage::Create, nullptr); + AddTabPage("labelTP_PARA_STD", SvxStdParagraphTabPage::Create, nullptr); + AddTabPage("labelTP_PARA_ALIGN", SvxParaAlignTabPage::Create, nullptr); + AddTabPage("labelTP_PARA_EXT", SvxExtParagraphTabPage::Create, nullptr); + AddTabPage("labelTP_PARA_ASIAN", SvxAsianTabPage::Create, nullptr ); + AddTabPage("background", SvxBkgTabPage::Create, nullptr); + + // remove asian tabpages if necessary + if ( !SvtCJKOptions::IsDoubleLinesEnabled() ) + RemoveTabPage("asianlayout"); + if ( !SvtCJKOptions::IsAsianTypographyEnabled() ) + RemoveTabPage("labelTP_PARA_ASIAN"); +} + +SvxSearchFormatDialog::~SvxSearchFormatDialog() +{ +} + +void SvxSearchFormatDialog::PageCreated(const OUString& rId, SfxTabPage& rPage) +{ + if (rId == "font") + { + const FontList* pApm_pFontList = nullptr; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxFontListItem* pFLItem = static_cast( + pSh->GetItem( SID_ATTR_CHAR_FONTLIST )); + if ( pFLItem ) + pApm_pFontList = pFLItem->GetFontList(); + } + + const FontList* pList = pApm_pFontList; + + if ( !pList ) + { + if ( !m_pFontList ) + m_pFontList.reset(new FontList(Application::GetDefaultDevice())); + pList = m_pFontList.get(); + } + + static_cast(rPage). + SetFontList( SvxFontListItem( pList, SID_ATTR_CHAR_FONTLIST ) ); + static_cast(rPage).EnableSearchMode(); + } + else if (rId == "labelTP_PARA_STD") + { + static_cast(rPage).EnableAutoFirstLine(); + } + else if (rId == "labelTP_PARA_ALIGN") + { + static_cast(rPage).EnableJustifyExt(); + } + else if (rId == "background") + { + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE,static_cast(SvxBackgroundTabFlags::SHOW_HIGHLIGHTING))); + rPage.PageCreated(aSet); + } +} + +SvxSearchAttributeDialog::SvxSearchAttributeDialog(weld::Window* pParent, + SearchAttrItemList& rLst, const WhichRangesContainer& pWhRanges) + : GenericDialogController(pParent, "cui/ui/searchattrdialog.ui", "SearchAttrDialog") + , rList(rLst) + , m_xAttrLB(m_xBuilder->weld_tree_view("treeview")) + , m_xOKBtn(m_xBuilder->weld_button("ok")) +{ + m_xAttrLB->set_size_request(m_xAttrLB->get_approximate_digit_width() * 50, + m_xAttrLB->get_height_rows(12)); + + m_xAttrLB->enable_toggle_buttons(weld::ColumnToggleType::Check); + + m_xOKBtn->connect_clicked(LINK( this, SvxSearchAttributeDialog, OKHdl)); + + SfxObjectShell* pSh = SfxObjectShell::Current(); + DBG_ASSERT( pSh, "No DocShell" ); + if (pSh) + { + SfxItemPool& rPool = pSh->GetPool(); + SfxItemSet aSet( rPool, pWhRanges ); + SfxWhichIter aIter( aSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while ( nWhich ) + { + sal_uInt16 nSlot = rPool.GetSlotId( nWhich ); + if ( nSlot >= SID_SVX_START ) + { + bool bChecked = false, bFound = false; + for ( sal_uInt16 i = 0; !bFound && i < rList.Count(); ++i ) + { + if ( nSlot == rList[i].nSlot ) + { + bFound = true; + if ( IsInvalidItem( rList[i].pItemPtr ) ) + bChecked = true; + } + } + + // item resources are in svx + sal_uInt32 nId = SvxAttrNameTable::FindIndex(nSlot); + if (RESARRAY_INDEX_NOTFOUND != nId) + { + m_xAttrLB->append(); + const int nRow = m_xAttrLB->n_children() - 1; + m_xAttrLB->set_toggle(nRow, bChecked ? TRISTATE_TRUE : TRISTATE_FALSE); + m_xAttrLB->set_text(nRow, SvxAttrNameTable::GetString(nId), 0); + m_xAttrLB->set_id(nRow, OUString::number(nSlot)); + } + else + SAL_WARN( "cui.dialogs", "no resource for slot id " << static_cast(nSlot) ); + } + nWhich = aIter.NextWhich(); + } + } + + m_xAttrLB->make_sorted(); + m_xAttrLB->select(0); +} + +SvxSearchAttributeDialog::~SvxSearchAttributeDialog() +{ +} + +IMPL_LINK_NOARG(SvxSearchAttributeDialog, OKHdl, weld::Button&, void) +{ + SearchAttrInfo aInvalidItem; + aInvalidItem.pItemPtr = INVALID_POOL_ITEM; + + for (int i = 0, nCount = m_xAttrLB->n_children(); i < nCount; ++i) + { + sal_uInt16 nSlot = m_xAttrLB->get_id(i).toUInt32(); + bool bChecked = m_xAttrLB->get_toggle(i) == TRISTATE_TRUE; + + sal_uInt16 j; + for ( j = rList.Count(); j; ) + { + SearchAttrInfo& rItem = rList[ --j ]; + if( rItem.nSlot == nSlot ) + { + if( bChecked ) + { + if( !IsInvalidItem( rItem.pItemPtr ) ) + delete rItem.pItemPtr; + rItem.pItemPtr = INVALID_POOL_ITEM; + } + else if( IsInvalidItem( rItem.pItemPtr ) ) + rItem.pItemPtr = nullptr; + j = 1; + break; + } + } + + if ( !j && bChecked ) + { + aInvalidItem.nSlot = nSlot; + rList.Insert( aInvalidItem ); + } + } + + // remove invalid items (pItem == NULL) + for ( sal_uInt16 n = rList.Count(); n; ) + if ( !rList[ --n ].pItemPtr ) + rList.Remove( n ); + + m_xDialog->response(RET_OK); +} + +// class SvxSearchSimilarityDialog --------------------------------------- + +SvxSearchSimilarityDialog::SvxSearchSimilarityDialog(weld::Window* pParent, bool bRelax, + sal_uInt16 nOther, sal_uInt16 nShorter, sal_uInt16 nLonger) + : GenericDialogController(pParent, "cui/ui/similaritysearchdialog.ui", "SimilaritySearchDialog") + , m_xOtherFld(m_xBuilder->weld_spin_button("otherfld")) + , m_xLongerFld(m_xBuilder->weld_spin_button("longerfld")) + , m_xShorterFld(m_xBuilder->weld_spin_button("shorterfld")) + , m_xRelaxBox(m_xBuilder->weld_check_button("relaxbox")) +{ + m_xOtherFld->set_value(nOther); + m_xShorterFld->set_value(nShorter); + m_xLongerFld->set_value(nLonger); + m_xRelaxBox->set_active(bRelax); +} + +SvxSearchSimilarityDialog::~SvxSearchSimilarityDialog() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/thesdlg.cxx b/cui/source/dialogs/thesdlg.cxx new file mode 100644 index 0000000000..ea98a44a3c --- /dev/null +++ b/cui/source/dialogs/thesdlg.cxx @@ -0,0 +1,352 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace ::com::sun::star; + +IMPL_LINK_NOARG( SvxThesaurusDialog, ModifyTimer_Hdl, Timer *, void ) +{ + LookUp(m_xWordCB->get_active_text()); + m_aModifyIdle.Stop(); +} + +IMPL_LINK_NOARG(SvxThesaurusDialog, ReplaceEditHdl_Impl, weld::Entry&, void) +{ + m_xReplaceBtn->set_sensitive(!m_xReplaceEdit->get_text().isEmpty()); +} + +IMPL_LINK(SvxThesaurusDialog, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + const vcl::KeyCode& rKey = rKEvt.GetKeyCode(); + + if (rKey.GetCode() == KEY_RETURN) + { + m_xDialog->response(RET_OK); + return true; + } + + return false; +} + +uno::Sequence< uno::Reference< linguistic2::XMeaning > > SvxThesaurusDialog::queryMeanings_Impl( + OUString& rTerm, + const lang::Locale& rLocale, + const beans::PropertyValues& rProperties ) +{ + uno::Sequence< uno::Reference< linguistic2::XMeaning > > aMeanings( + xThesaurus->queryMeanings( rTerm, rLocale, rProperties ) ); + + // text with '.' at the end? + if ( !aMeanings.hasElements() && rTerm.endsWith(".") ) + { + // try again without trailing '.' chars. It may be a word at the + // end of a sentence and not an abbreviation... + OUString aTxt(comphelper::string::stripEnd(rTerm, '.')); + aMeanings = xThesaurus->queryMeanings( aTxt, rLocale, rProperties ); + if (aMeanings.hasElements()) + { + rTerm = aTxt; + } + } + + return aMeanings; +} + +bool SvxThesaurusDialog::UpdateAlternativesBox_Impl() +{ + lang::Locale aLocale( LanguageTag::convertToLocale( nLookUpLanguage ) ); + uno::Sequence< uno::Reference< linguistic2::XMeaning > > aMeanings = queryMeanings_Impl( + aLookUpText, aLocale, uno::Sequence< beans::PropertyValue >() ); + const sal_Int32 nMeanings = aMeanings.getLength(); + const uno::Reference< linguistic2::XMeaning > *pMeanings = aMeanings.getConstArray(); + + m_xAlternativesCT->freeze(); + + m_xAlternativesCT->clear(); + int nRow = 0; + for (sal_Int32 i = 0; i < nMeanings; ++i) + { + OUString rMeaningTxt = pMeanings[i]->getMeaning(); + uno::Sequence< OUString > aSynonyms( pMeanings[i]->querySynonyms() ); + const sal_Int32 nSynonyms = aSynonyms.getLength(); + const OUString *pSynonyms = aSynonyms.getConstArray(); + DBG_ASSERT( !rMeaningTxt.isEmpty(), "meaning with empty text" ); + DBG_ASSERT( nSynonyms > 0, "meaning without synonym" ); + + OUString sHeading = OUString::number(i + 1) + ". " + rMeaningTxt; + m_xAlternativesCT->append_text(sHeading); + m_xAlternativesCT->set_text_emphasis(nRow, true, 0); + ++nRow; + + for (sal_Int32 k = 0; k < nSynonyms; ++k) + { + // GetThesaurusReplaceText will strip the leading spaces + m_xAlternativesCT->append_text(" " + pSynonyms[k]); + m_xAlternativesCT->set_text_emphasis(nRow, false, 0); + ++nRow; + } + } + + m_xAlternativesCT->thaw(); + + return nMeanings > 0; +} + +void SvxThesaurusDialog::LookUp( const OUString &rText ) +{ + if (rText != m_xWordCB->get_active_text()) // avoid moving of the cursor if the text is the same + m_xWordCB->set_entry_text(rText); + LookUp_Impl(); +} + +IMPL_LINK_NOARG(SvxThesaurusDialog, LeftBtnHdl_Impl, weld::Button&, void) +{ + if (aLookUpHistory.size() >= 2) + { + aLookUpHistory.pop(); // remove current look up word from stack + m_xWordCB->set_entry_text(aLookUpHistory.top()); // retrieve previous look up word + aLookUpHistory.pop(); + LookUp_Impl(); + } +} + +IMPL_LINK( SvxThesaurusDialog, LanguageHdl_Impl, weld::ComboBox&, rLB, void ) +{ + OUString aLangText(rLB.get_active_text()); + LanguageType nLang = SvtLanguageTable::GetLanguageType( aLangText ); + DBG_ASSERT( nLang != LANGUAGE_NONE && nLang != LANGUAGE_DONTKNOW, "failed to get language" ); + if (xThesaurus->hasLocale( LanguageTag::convertToLocale( nLang ) )) + nLookUpLanguage = nLang; + SetWindowTitle( nLang ); + LookUp_Impl(); +} + +void SvxThesaurusDialog::LookUp_Impl() +{ + OUString aText(m_xWordCB->get_active_text()); + + aLookUpText = aText; + if (!aLookUpText.isEmpty() && + (aLookUpHistory.empty() || aLookUpText != aLookUpHistory.top())) + aLookUpHistory.push( aLookUpText ); + + m_bWordFound = UpdateAlternativesBox_Impl(); + m_xAlternativesCT->set_visible(m_bWordFound); + m_xNotFound->set_visible(!m_bWordFound); + + if (m_bWordFound && !m_nSelectFirstEvent) + m_nSelectFirstEvent = Application::PostUserEvent(LINK(this, SvxThesaurusDialog, SelectFirstHdl_Impl)); + + if (m_xWordCB->find_text(aText) == -1) + m_xWordCB->append_text(aText); + + m_xReplaceEdit->set_text( OUString() ); + ReplaceEditHdl_Impl(*m_xReplaceEdit); + m_xLeftBtn->set_sensitive( aLookUpHistory.size() > 1 ); +} + +IMPL_LINK_NOARG(SvxThesaurusDialog, WordSelectHdl_Impl, weld::ComboBox&, void) +{ + m_aModifyIdle.Start(); +} + +IMPL_LINK( SvxThesaurusDialog, AlternativesSelectHdl_Impl, weld::TreeView&, rBox, void ) +{ + int nEntry = rBox.get_selected_index(); + if (nEntry != -1) + { + bool bIsHeader = rBox.get_text_emphasis(nEntry, 0); + if (bIsHeader) + { + ++nEntry; + rBox.select(nEntry); + } + OUString aStr = linguistic::GetThesaurusReplaceText(rBox.get_text(nEntry)); + m_xReplaceEdit->set_text(aStr); + ReplaceEditHdl_Impl(*m_xReplaceEdit); + } +} + +IMPL_LINK( SvxThesaurusDialog, AlternativesDoubleClickHdl_Impl, weld::TreeView&, rBox, bool ) +{ + int nEntry = rBox.get_selected_index(); + if (nEntry != -1) + { + bool bIsHeader = rBox.get_text_emphasis(nEntry, 0); + if (bIsHeader) + { + ++nEntry; + rBox.select(nEntry); + } + OUString aStr = linguistic::GetThesaurusReplaceText(rBox.get_text(nEntry)); + m_xWordCB->set_entry_text(aStr); + if (!aStr.isEmpty()) + LookUp_Impl(); + } + + //! workaround to set the selection since calling SelectEntryPos within + //! the double click handler does not work + if (!m_nSelectFirstEvent) + m_nSelectFirstEvent = Application::PostUserEvent(LINK(this, SvxThesaurusDialog, SelectFirstHdl_Impl)); + + return true; +} + +IMPL_LINK_NOARG(SvxThesaurusDialog, SelectFirstHdl_Impl, void *, void) +{ + m_nSelectFirstEvent = nullptr; + if (m_xAlternativesCT->n_children() >= 2) + { + m_xAlternativesCT->select(1); // pos 0 is a 'header' that is not selectable + AlternativesSelectHdl_Impl(*m_xAlternativesCT); + } +} + +// class SvxThesaurusDialog ---------------------------------------------- + +SvxThesaurusDialog::SvxThesaurusDialog( + weld::Widget* pParent, + uno::Reference< linguistic2::XThesaurus > const & xThes, + const OUString &rWord, + LanguageType nLanguage) + : SfxDialogController(pParent, "cui/ui/thesaurus.ui", "ThesaurusDialog") + , m_aModifyIdle("cui SvxThesaurusDialog LookUp Modify") + , nLookUpLanguage(LANGUAGE_NONE) + , m_bWordFound(false) + , m_xLeftBtn(m_xBuilder->weld_button("left")) + , m_xWordCB(m_xBuilder->weld_combo_box("wordcb")) + , m_xAlternativesCT(m_xBuilder->weld_tree_view("alternatives")) + , m_xNotFound(m_xBuilder->weld_label("notfound")) + , m_xReplaceEdit(m_xBuilder->weld_entry("replaceed")) + , m_xLangLB(m_xBuilder->weld_combo_box("langcb")) + , m_xReplaceBtn(m_xBuilder->weld_button("ok")) + , m_nSelectFirstEvent(nullptr) +{ + m_aModifyIdle.SetInvokeHandler( LINK( this, SvxThesaurusDialog, ModifyTimer_Hdl ) ); + m_aModifyIdle.SetPriority( TaskPriority::LOWEST ); + + m_xReplaceEdit->connect_changed( LINK( this, SvxThesaurusDialog, ReplaceEditHdl_Impl ) ); + m_xReplaceBtn->connect_clicked( LINK( this, SvxThesaurusDialog, ReplaceBtnHdl_Impl ) ); + m_xLeftBtn->connect_clicked( LINK( this, SvxThesaurusDialog, LeftBtnHdl_Impl ) ); + m_xWordCB->set_entry_completion(false); + m_xWordCB->connect_changed( LINK( this, SvxThesaurusDialog, WordSelectHdl_Impl ) ); + m_xLangLB->connect_changed( LINK( this, SvxThesaurusDialog, LanguageHdl_Impl ) ); + m_xAlternativesCT->connect_changed( LINK( this, SvxThesaurusDialog, AlternativesSelectHdl_Impl )); + m_xAlternativesCT->connect_row_activated( LINK( this, SvxThesaurusDialog, AlternativesDoubleClickHdl_Impl )); + m_xAlternativesCT->connect_key_press(LINK(this, SvxThesaurusDialog, KeyInputHdl)); + + xThesaurus = xThes; + aLookUpText = rWord; + nLookUpLanguage = nLanguage; + if (!rWord.isEmpty()) + aLookUpHistory.push( rWord ); + + OUString aTmp( rWord ); + (void)linguistic::RemoveHyphens( aTmp ); + (void)linguistic::ReplaceControlChars( aTmp ); + m_xReplaceEdit->set_text( aTmp ); + ReplaceEditHdl_Impl(*m_xReplaceEdit); + m_xWordCB->append_text( aTmp ); + + LookUp( aTmp ); + m_xAlternativesCT->grab_focus(); + m_xLeftBtn->set_sensitive(false); + + // fill language menu button list + uno::Sequence< lang::Locale > aLocales; + if (xThesaurus.is()) + aLocales = xThesaurus->getLocales(); + const sal_Int32 nLocales = aLocales.getLength(); + const lang::Locale *pLocales = aLocales.getConstArray(); + m_xLangLB->clear(); + std::vector< OUString > aLangVec; + for (sal_Int32 i = 0; i < nLocales; ++i) + { + const LanguageType nLang = LanguageTag::convertToLanguageType( pLocales[i] ); + DBG_ASSERT( nLang != LANGUAGE_NONE && nLang != LANGUAGE_DONTKNOW, "failed to get language" ); + aLangVec.push_back( SvtLanguageTable::GetLanguageString( nLang ) ); + } + std::sort( aLangVec.begin(), aLangVec.end() ); + m_xLangLB->freeze(); + for (const OUString & i : aLangVec) + m_xLangLB->append_text(i); + m_xLangLB->thaw(); + + std::vector< OUString >::iterator aI = std::find(aLangVec.begin(), aLangVec.end(), + SvtLanguageTable::GetLanguageString(nLanguage)); + if (aI != aLangVec.end()) + { + m_xLangLB->set_active_text(*aI); + } + + SetWindowTitle(nLanguage); + + // disable controls if service is missing + if (!xThesaurus.is()) + m_xDialog->set_sensitive(false); + else + m_xWordCB->grab_focus(); +} + +SvxThesaurusDialog::~SvxThesaurusDialog() +{ + if (m_nSelectFirstEvent) + { + Application::RemoveUserEvent(m_nSelectFirstEvent); + m_nSelectFirstEvent = nullptr; + } +} + +IMPL_LINK_NOARG(SvxThesaurusDialog, ReplaceBtnHdl_Impl, weld::Button&, void) +{ + m_xDialog->response(RET_OK); +} + +void SvxThesaurusDialog::SetWindowTitle( LanguageType nLanguage ) +{ + // adjust language + OUString aStr(m_xDialog->get_title()); + sal_Int32 nIndex = aStr.indexOf( '(' ); + if( nIndex != -1 ) + aStr = aStr.copy( 0, nIndex - 1 ); + aStr += " (" + SvtLanguageTable::GetLanguageString( nLanguage ) + ")"; + m_xDialog->set_title(aStr); // set window title +} + +OUString SvxThesaurusDialog::GetWord() const +{ + return m_xReplaceEdit->get_text(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/tipofthedaydlg.cxx b/cui/source/dialogs/tipofthedaydlg.cxx new file mode 100644 index 0000000000..f1cb7afc2c --- /dev/null +++ b/cui/source/dialogs/tipofthedaydlg.cxx @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//size of preview +const Size ThumbSize(150, 150); + +TipOfTheDayDialog::TipOfTheDayDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/tipofthedaydialog.ui", "TipOfTheDayDialog") + , m_pParent(pParent) + , m_pText(m_xBuilder->weld_label("lbText")) + , m_pShowTip(m_xBuilder->weld_check_button("cbShowTip")) + , m_pNext(m_xBuilder->weld_button("btnNext")) + , m_pLink(m_xBuilder->weld_link_button("btnLink")) + , m_pPreview(new weld::CustomWeld(*m_xBuilder, "imPreview", m_aPreview)) +{ + m_pShowTip->set_active(officecfg::Office::Common::Misc::ShowTipOfTheDay::get()); + m_pNext->connect_clicked(LINK(this, TipOfTheDayDialog, OnNextClick)); + m_nCurrentTip = officecfg::Office::Common::Misc::LastTipOfTheDayID::get(); + m_pPreview->set_size_request(ThumbSize.Width(), ThumbSize.Height()); + + if (pParent != nullptr) + { + css::uno::Reference xWindow = pParent->GetXWindow(); + if (xWindow.is()) + { + VclPtr xVclWin(VCLUnoHelper::GetWindow(xWindow)); + if (xVclWin != nullptr) + xVclWin->AddEventListener(LINK(this, TipOfTheDayDialog, Terminated)); + } + } + + const auto t0 = std::chrono::system_clock::now().time_since_epoch(); + sal_Int32 nDay = std::chrono::duration_cast(t0).count() / 24; + //show next tip after one day + if (nDay > officecfg::Office::Common::Misc::LastTipOfTheDayShown::get()) + m_nCurrentTip++; + + // save this time to the config now instead of in the dtor otherwise we + // end up with multiple copies of this dialog every time we open a new + // document if the first one isn't closed + std::shared_ptr xChanges( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Misc::LastTipOfTheDayShown::set(nDay, xChanges); + xChanges->commit(); + + UpdateTip(); +} + +IMPL_LINK(TipOfTheDayDialog, Terminated, VclWindowEvent&, rEvent, void) +{ + if (rEvent.GetId() == VclEventId::ObjectDying) + { + m_pParent = nullptr; + TipOfTheDayDialog::response(RET_OK); + } +} + +TipOfTheDayDialog::~TipOfTheDayDialog() +{ + std::shared_ptr xChanges( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Misc::LastTipOfTheDayID::set(m_nCurrentTip, xChanges); + officecfg::Office::Common::Misc::ShowTipOfTheDay::set(m_pShowTip->get_active(), xChanges); + xChanges->commit(); + + if (m_pParent != nullptr) + { + css::uno::Reference xWindow = m_pParent->GetXWindow(); + if (xWindow.is()) + { + VclPtr xVclWin(VCLUnoHelper::GetWindow(xWindow)); + if (xVclWin != nullptr) + xVclWin->RemoveEventListener(LINK(this, TipOfTheDayDialog, Terminated)); + } + } +} + +static bool file_exists(const OUString& fileName) +{ + ::osl::File aFile(fileName); + return aFile.open(osl_File_OpenFlag_Read) == osl::FileBase::E_None; +} + +void TipOfTheDayDialog::UpdateTip() +{ + constexpr sal_Int32 nNumberOfTips = std::size(TIPOFTHEDAY_STRINGARRAY); + + if ((m_nCurrentTip >= nNumberOfTips) || (m_nCurrentTip < 0)) + m_nCurrentTip = 0; + + //title + m_xDialog->set_title(CuiResId(STR_TITLE) + .replaceFirst("%CURRENT", OUString::number(m_nCurrentTip + 1)) + .replaceFirst("%TOTAL", OUString::number(nNumberOfTips))); + + auto[sTip, sLink, sImage, nType] = TIPOFTHEDAY_STRINGARRAY[m_nCurrentTip]; + + // text +//replace MOD1 & MOD2 shortcuts depending on platform +#ifdef MACOSX + const OUString aMOD1 = CuiResId(STR_CMD); + const OUString aMOD2 = CuiResId(STR_Option); +#else + const OUString aMOD1 = CuiResId(STR_CTRL); + const OUString aMOD2 = CuiResId(STR_Alt); +#endif + m_pText->set_label(CuiResId(sTip).replaceAll("%MOD1", aMOD1).replaceAll("%MOD2", aMOD2)); + + // hyperlink + if (sLink.isEmpty()) + { + m_pLink->set_visible(false); + } + else if (sLink.startsWith(".uno:")) + { + m_pLink->set_visible(false); + //show the link only if the UNO command is available in the current module + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + { + const auto xFrame = pViewFrame->GetFrame().GetFrameInterface(); + const css::uno::Reference xDispatchProvider( + xFrame, css::uno::UNO_QUERY); + if (xDispatchProvider.is()) + { + css::util::URL aCommandURL; + aCommandURL.Complete = sLink; + const css::uno::Reference xContext + = comphelper::getProcessComponentContext(); + const css::uno::Reference xParser + = css::util::URLTransformer::create(xContext); + xParser->parseStrict(aCommandURL); + const css::uno::Reference xDisp + = xDispatchProvider->queryDispatch(aCommandURL, OUString(), 0); + if (xDisp.is()) + { + m_pLink->set_label(CuiResId(STR_UNO_LINK)); + m_pLink->set_uri(sLink); + + const OUString aModuleName( + vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)); + const auto aProperties + = vcl::CommandInfoProvider::GetCommandProperties(sLink, aModuleName); + m_pLink->set_tooltip_text( + vcl::CommandInfoProvider::GetTooltipForCommand(sLink, aProperties, xFrame)); + + m_pLink->set_visible(true); + m_pLink->connect_activate_link(LINK(this, TipOfTheDayDialog, OnLinkClick)); + } + } + } + } + else if (sLink.startsWith("http")) + { + // Links may have some %PRODUCTVERSION which need to be expanded + OUString aText = Translate::ExpandVariables(sLink); + OUString aLang = LanguageTag(utl::ConfigManager::getUILocale()).getLanguage(); + if (aLang == "en" || aLang == "pt" || aLang == "zh") //en-US/GB, pt-BR, zh-CH/TW + aLang = LanguageTag(utl::ConfigManager::getUILocale()).getBcp47(); + m_pLink->set_uri(aText.replaceFirst("%LANGUAGENAME", aLang)); + m_pLink->set_label(CuiResId(STR_MORE_LINK)); + m_pLink->set_visible(true); + m_pLink->connect_activate_link(Link()); + } + else + { + m_pLink->set_uri(sLink); + m_pLink->set_label(CuiResId(STR_HELP_LINK)); + m_pLink->set_visible(true); + m_pLink->connect_activate_link(LINK(this, TipOfTheDayDialog, OnLinkClick)); + } + // image + OUString aURL("$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/tipoftheday/"); + rtl::Bootstrap::expandMacros(aURL); + OUString aImageName = sImage; + Graphic aGraphic; + + if (!aImageName.isEmpty() && file_exists(aURL + aImageName)) + GraphicFilter::LoadGraphic(aURL + aImageName, OUString(), aGraphic); + else + { + const OUString sModuleImage[5] + = { RID_SVXBMP_TOTD_WRITER, RID_SVXBMP_TOTD_CALC, RID_SVXBMP_TOTD_DRAW, + RID_SVXBMP_TOTD_IMPRESS, RID_SVXBMP_TOTD_SOFFICE }; + const OUString aIconTheme + = Application::GetSettings().GetStyleSettings().DetermineIconTheme(); + BitmapEx aBmpEx; + ImageTree::get().loadImage(sModuleImage[nType], aIconTheme, aBmpEx, true, + ImageLoadFlags::IgnoreDarkTheme); + aGraphic = aBmpEx; + } + + if (!aGraphic.IsAnimated()) + { + BitmapEx aBmpEx(aGraphic.GetBitmapEx()); + if (aBmpEx.Scale(ThumbSize)) + aGraphic = aBmpEx; + } + m_aPreview.SetPreview(aGraphic); +} + +IMPL_LINK(TipOfTheDayDialog, OnLinkClick, weld::LinkButton&, rButton, bool) +{ + const OUString sLink = rButton.get_uri(); + if (sLink.startsWith(".uno:")) + { + comphelper::dispatchCommand(sLink, {}); + TipOfTheDayDialog::response(RET_OK); + } + else + { + Application::GetHelp()->Start(sLink, static_cast(nullptr)); + } + return true; +} + +IMPL_LINK_NOARG(TipOfTheDayDialog, OnNextClick, weld::Button&, void) +{ + m_nCurrentTip++; //zeroed at updatetip when out of range + UpdateTip(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/toolbarmodedlg.cxx b/cui/source/dialogs/toolbarmodedlg.cxx new file mode 100644 index 0000000000..54918e7fdc --- /dev/null +++ b/cui/source/dialogs/toolbarmodedlg.cxx @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static OUString GetCurrentApp() +{ + OUString sResult; + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + { + const auto xCurrentFrame = pViewFrame->GetFrame().GetFrameInterface(); + const auto xContext = comphelper::getProcessComponentContext(); + const auto xModuleManager = css::frame::ModuleManager::create(xContext); + switch (vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(xCurrentFrame))) + { + case vcl::EnumContext::Application::Writer: + sResult = "Writer"; + break; + case vcl::EnumContext::Application::Calc: + sResult = "Calc"; + break; + case vcl::EnumContext::Application::Impress: + sResult = "Impress"; + break; + case vcl::EnumContext::Application::Draw: + sResult = "Draw"; + break; + case vcl::EnumContext::Application::Formula: + sResult = "Formula"; + break; + case vcl::EnumContext::Application::Base: + sResult = "Base"; + break; + default: + sResult = "Unsupported"; + } + } + return sResult; +} + +static OUString GetCurrentMode() +{ + OUString sResult; + if (SfxViewFrame::Current()) + { + const auto xContext = comphelper::getProcessComponentContext(); + const utl::OConfigurationTreeRoot aAppNode( + xContext, "org.openoffice.Office.UI.ToolbarMode/Applications/" + GetCurrentApp(), true); + if (aAppNode.isValid()) + sResult = comphelper::getString(aAppNode.getNodeValue("Active")); + }; + return sResult; +} + +ToolbarmodeDialog::ToolbarmodeDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/toolbarmodedialog.ui", "ToolbarmodeDialog") + , m_pImage(m_xBuilder->weld_image("imImage")) + , m_pApply(m_xBuilder->weld_button("btnApply")) + , m_pApplyAll(m_xBuilder->weld_button("btnApplyAll")) + , m_pRadioButtons{ (m_xBuilder->weld_radio_button("rbButton1")), + (m_xBuilder->weld_radio_button("rbButton2")), + (m_xBuilder->weld_radio_button("rbButton3")), + (m_xBuilder->weld_radio_button("rbButton4")), + (m_xBuilder->weld_radio_button("rbButton5")), + (m_xBuilder->weld_radio_button("rbButton6")), + (m_xBuilder->weld_radio_button("rbButton7")), + (m_xBuilder->weld_radio_button("rbButton8")), + (m_xBuilder->weld_radio_button("rbButton9")) } + , m_pInfoLabel(m_xBuilder->weld_label("lbInfo")) +{ + static_assert(SAL_N_ELEMENTS(m_pRadioButtons) == std::size(TOOLBARMODES_ARRAY)); + + Link aLink = LINK(this, ToolbarmodeDialog, SelectToolbarmode); + + const OUString sCurrentMode = GetCurrentMode(); + for (std::size_t i = 0; i < std::size(m_pRadioButtons); ++i) + { + m_pRadioButtons[i]->connect_toggled(aLink); + if (sCurrentMode == std::get<1>(TOOLBARMODES_ARRAY[i])) + { + m_pRadioButtons[i]->set_active(true); + UpdateImage(std::get<2>(TOOLBARMODES_ARRAY[i])); + m_pInfoLabel->set_label(CuiResId(std::get<0>(TOOLBARMODES_ARRAY[i]))); + } + } + + m_pApply->set_label(CuiResId(RID_CUISTR_UI_APPLYALL).replaceFirst("%MODULE", GetCurrentApp())); + m_pApply->connect_clicked(LINK(this, ToolbarmodeDialog, OnApplyClick)); + m_pApplyAll->connect_clicked(LINK(this, ToolbarmodeDialog, OnApplyClick)); + + if (!officecfg::Office::Common::Misc::ExperimentalMode::get()) + { + m_pRadioButtons[nGroupedbarFull]->set_visible(false); + m_pRadioButtons[nContextualGroups]->set_visible(false); + } +} + +ToolbarmodeDialog::~ToolbarmodeDialog() = default; + +static bool file_exists(const OUString& fileName) +{ + osl::File aFile(fileName); + return aFile.open(osl_File_OpenFlag_Read) == osl::FileBase::E_None; +} + +int ToolbarmodeDialog::GetActiveRadioButton() +{ + for (std::size_t i = 0; i < std::size(m_pRadioButtons); ++i) + { + if (m_pRadioButtons[i]->get_active()) + return i; + } + return -1; +} + +void ToolbarmodeDialog::UpdateImage(std::u16string_view sFileName) +{ + // load image + OUString aURL("$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/toolbarmode/"); + rtl::Bootstrap::expandMacros(aURL); + aURL += sFileName; + if (sFileName.empty() || !file_exists(aURL)) + return; + // draw image + Graphic aGraphic; + if (GraphicFilter::LoadGraphic(aURL, OUString(), aGraphic) == ERRCODE_NONE) + { + ScopedVclPtr m_pVirDev = m_pImage->create_virtual_device(); + m_pVirDev->SetOutputSizePixel(aGraphic.GetSizePixel()); + m_pVirDev->DrawBitmapEx(Point(0, 0), aGraphic.GetBitmapEx()); + m_pImage->set_image(m_pVirDev.get()); + m_pVirDev.disposeAndClear(); + } +} + +IMPL_LINK_NOARG(ToolbarmodeDialog, SelectToolbarmode, weld::Toggleable&, void) +{ + const int i = GetActiveRadioButton(); + if (i > -1) + { + UpdateImage(std::get<2>(TOOLBARMODES_ARRAY[i])); + m_pInfoLabel->set_label(CuiResId(std::get<0>(TOOLBARMODES_ARRAY[i]))); + } +} + +IMPL_LINK(ToolbarmodeDialog, OnApplyClick, weld::Button&, rButton, void) +{ + const int i = GetActiveRadioButton(); + if (i == -1) + return; + const OUString sCmd = std::get<1>(TOOLBARMODES_ARRAY[i]); + //apply to all except current module + if (&rButton == m_pApplyAll.get()) + { + std::shared_ptr aBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::UI::ToolbarMode::ActiveWriter::set(sCmd, aBatch); + officecfg::Office::UI::ToolbarMode::ActiveCalc::set(sCmd, aBatch); + officecfg::Office::UI::ToolbarMode::ActiveImpress::set(sCmd, aBatch); + officecfg::Office::UI::ToolbarMode::ActiveDraw::set(sCmd, aBatch); + aBatch->commit(); + + OUString sCurrentApp = GetCurrentApp(); + if (SfxViewFrame::Current()) + { + const auto xContext = comphelper::getProcessComponentContext(); + const utl::OConfigurationTreeRoot aAppNode( + xContext, "org.openoffice.Office.UI.ToolbarMode/Applications/", true); + if (sCurrentApp != "Writer") + aAppNode.setNodeValue("Writer/Active", css::uno::Any(sCmd)); + if (sCurrentApp != "Calc") + aAppNode.setNodeValue("Calc/Active", css::uno::Any(sCmd)); + if (sCurrentApp != "Impress") + aAppNode.setNodeValue("Impress/Active", css::uno::Any(sCmd)); + if (sCurrentApp != "Draw") + aAppNode.setNodeValue("Draw/Active", css::uno::Any(sCmd)); + aAppNode.commit(); + }; + } + //apply to current module + comphelper::dispatchCommand(".uno:ToolbarMode?Mode:string=" + sCmd, {}); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/cui/source/dialogs/widgettestdlg.cxx b/cui/source/dialogs/widgettestdlg.cxx new file mode 100644 index 0000000000..51ecef3105 --- /dev/null +++ b/cui/source/dialogs/widgettestdlg.cxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* +* This file is part of the LibreOffice project. +* +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include +#include + +WidgetTestDialog::WidgetTestDialog(weld::Window* pParent) + : GenericDialogController(pParent, "cui/ui/widgettestdialog.ui", "WidgetTestDialog") +{ + m_xOKButton = m_xBuilder->weld_button("ok_btn"); + m_xCancelButton = m_xBuilder->weld_button("cancel_btn"); + m_xTreeView = m_xBuilder->weld_tree_view("contenttree"); + m_xTreeView2 = m_xBuilder->weld_tree_view("contenttree2"); + + m_xOKButton->connect_clicked(LINK(this, WidgetTestDialog, OkHdl)); + m_xCancelButton->connect_clicked(LINK(this, WidgetTestDialog, CancelHdl)); + + FillTreeView(); +} + +WidgetTestDialog::~WidgetTestDialog() {} + +IMPL_LINK_NOARG(WidgetTestDialog, OkHdl, weld::Button&, void) { m_xDialog->response(RET_OK); } + +IMPL_LINK_NOARG(WidgetTestDialog, CancelHdl, weld::Button&, void) +{ + m_xDialog->response(RET_CANCEL); +} + +void WidgetTestDialog::FillTreeView() +{ + OUString aImage1(RID_SVXBMP_CELL_LR); + OUString aImage2(RID_SVXBMP_SHADOW_BOT_LEFT); + + for (size_t nCount = 0; nCount < 4; nCount++) + { + OUString sText = OUString::Concat("Test ") + OUString::Concat(OUString::number(nCount)); + std::unique_ptr xEntry = m_xTreeView->make_iterator(); + m_xTreeView->insert(nullptr, -1, &sText, &sText, nullptr, nullptr, false, xEntry.get()); + m_xTreeView->set_image(*xEntry, (nCount % 2 == 0) ? aImage1 : aImage2); + + m_xTreeView2->append(); + m_xTreeView2->set_image(nCount, (nCount % 2 == 0) ? aImage1 : aImage2); + m_xTreeView2->set_text(nCount, "First Column", 0); + m_xTreeView2->set_text( + nCount, OUString::Concat("Row ") + OUString::Concat(OUString::number(nCount)), 1); + m_xTreeView2->set_id(nCount, OUString::number(nCount)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/zoom.cxx b/cui/source/dialogs/zoom.cxx new file mode 100644 index 0000000000..7bef6a5b98 --- /dev/null +++ b/cui/source/dialogs/zoom.cxx @@ -0,0 +1,397 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +const sal_uInt16 SPECIAL_FACTOR = 0xFFFF; + +} // anonymous namespace + +sal_uInt16 SvxZoomDialog::GetFactor() const +{ + if (m_x100Btn->get_active()) + return 100; + + if (m_xUserBtn->get_active()) + return static_cast(m_xUserEdit->get_value(FieldUnit::PERCENT)); + else + return SPECIAL_FACTOR; +} + +void SvxZoomDialog::SetFactor(sal_uInt16 nNewFactor, ZoomButtonId nButtonId) +{ + m_xUserEdit->set_sensitive(false); + + if (nButtonId == ZoomButtonId::NONE) + { + if (nNewFactor == 100) + { + m_x100Btn->set_active(true); + m_x100Btn->grab_focus(); + } + else + { + m_xUserBtn->set_active(true); + m_xUserEdit->set_sensitive(true); + m_xUserEdit->set_value(nNewFactor, FieldUnit::PERCENT); + m_xUserEdit->grab_focus(); + } + } + else + { + m_xUserEdit->set_value(nNewFactor, FieldUnit::PERCENT); + switch (nButtonId) + { + case ZoomButtonId::OPTIMAL: + { + m_xOptimalBtn->set_active(true); + m_xOptimalBtn->grab_focus(); + break; + } + case ZoomButtonId::PAGEWIDTH: + { + m_xPageWidthBtn->set_active(true); + m_xPageWidthBtn->grab_focus(); + break; + } + case ZoomButtonId::WHOLEPAGE: + { + m_xWholePageBtn->set_active(true); + m_xWholePageBtn->grab_focus(); + break; + } + default: + break; + } + } +} + +void SvxZoomDialog::HideButton(ZoomButtonId nButtonId) +{ + switch (nButtonId) + { + case ZoomButtonId::OPTIMAL: + m_xOptimalBtn->hide(); + break; + + case ZoomButtonId::PAGEWIDTH: + m_xPageWidthBtn->hide(); + break; + + case ZoomButtonId::WHOLEPAGE: + m_xWholePageBtn->hide(); + break; + + default: + OSL_FAIL("Wrong button number!"); + } +} + +void SvxZoomDialog::SetLimits(sal_uInt16 nMin, sal_uInt16 nMax) +{ + DBG_ASSERT(nMin < nMax, "invalid limits"); + m_xUserEdit->set_range(nMin, nMax, FieldUnit::PERCENT); +} + +const SfxItemSet* SvxZoomDialog::GetOutputItemSet() const { return m_pOutSet.get(); } + +SvxZoomDialog::SvxZoomDialog(weld::Window* pParent, const SfxItemSet& rCoreSet) + : SfxDialogController(pParent, "cui/ui/zoomdialog.ui", "ZoomDialog") + , m_rSet(rCoreSet) + , m_bModified(false) + , m_xOptimalBtn(m_xBuilder->weld_radio_button("optimal")) + , m_xWholePageBtn(m_xBuilder->weld_radio_button("fitwandh")) + , m_xPageWidthBtn(m_xBuilder->weld_radio_button("fitw")) + , m_x100Btn(m_xBuilder->weld_radio_button("100pc")) + , m_xUserBtn(m_xBuilder->weld_radio_button("variable")) + , m_xUserEdit(m_xBuilder->weld_metric_spin_button("zoomsb", FieldUnit::PERCENT)) + , m_xViewFrame(m_xBuilder->weld_widget("viewframe")) + , m_xAutomaticBtn(m_xBuilder->weld_radio_button("automatic")) + , m_xSingleBtn(m_xBuilder->weld_radio_button("singlepage")) + , m_xColumnsBtn(m_xBuilder->weld_radio_button("columns")) + , m_xColumnsEdit(m_xBuilder->weld_spin_button("columnssb")) + , m_xBookModeChk(m_xBuilder->weld_check_button("bookmode")) + , m_xOKBtn(m_xBuilder->weld_button("ok")) +{ + Link aLink = LINK(this, SvxZoomDialog, UserHdl); + m_x100Btn->connect_toggled(aLink); + m_xOptimalBtn->connect_toggled(aLink); + m_xPageWidthBtn->connect_toggled(aLink); + m_xWholePageBtn->connect_toggled(aLink); + m_xUserBtn->connect_toggled(aLink); + + Link aViewLayoutLink = LINK(this, SvxZoomDialog, ViewLayoutUserHdl); + m_xAutomaticBtn->connect_toggled(aViewLayoutLink); + m_xSingleBtn->connect_toggled(aViewLayoutLink); + m_xColumnsBtn->connect_toggled(aViewLayoutLink); + + Link aViewLayoutSpinLink + = LINK(this, SvxZoomDialog, ViewLayoutSpinHdl); + m_xColumnsEdit->connect_value_changed(aViewLayoutSpinLink); + + Link aViewLayoutCheckLink + = LINK(this, SvxZoomDialog, ViewLayoutCheckHdl); + m_xBookModeChk->connect_toggled(aViewLayoutCheckLink); + + m_xOKBtn->connect_clicked(LINK(this, SvxZoomDialog, OKHdl)); + m_xUserEdit->connect_value_changed(LINK(this, SvxZoomDialog, SpinHdl)); + + // default values + sal_uInt16 nValue = 100; + sal_uInt16 nMin = 10; + sal_uInt16 nMax = 1000; + + // maybe get the old value first + const SfxUInt16Item* pOldUserItem = nullptr; + if (SfxObjectShell* pShell = SfxObjectShell::Current()) + pOldUserItem = pShell->GetItem(SID_ATTR_ZOOM_USER); + + if (pOldUserItem) + nValue = pOldUserItem->GetValue(); + + // initialize UserEdit + if (nMin > nValue) + nMin = nValue; + if (nMax < nValue) + nMax = nValue; + + SetLimits(nMin, nMax); + m_xUserEdit->set_value(nValue, FieldUnit::PERCENT); + + const SfxPoolItem& rItem = m_rSet.Get(SID_ATTR_ZOOM); + + if (auto pZoomItem = dynamic_cast(&rItem)) + { + const sal_uInt16 nZoom = pZoomItem->GetValue(); + const SvxZoomType eType = pZoomItem->GetType(); + const SvxZoomEnableFlags nValSet = pZoomItem->GetValueSet(); + ZoomButtonId nButtonId = ZoomButtonId::NONE; + + switch (eType) + { + case SvxZoomType::OPTIMAL: + nButtonId = ZoomButtonId::OPTIMAL; + break; + case SvxZoomType::PAGEWIDTH: + nButtonId = ZoomButtonId::PAGEWIDTH; + break; + case SvxZoomType::WHOLEPAGE: + nButtonId = ZoomButtonId::WHOLEPAGE; + break; + case SvxZoomType::PERCENT: + break; + case SvxZoomType::PAGEWIDTH_NOBORDER: + break; + } + + if (!(SvxZoomEnableFlags::N100 & nValSet)) + m_x100Btn->set_sensitive(false); + if (!(SvxZoomEnableFlags::OPTIMAL & nValSet)) + m_xOptimalBtn->set_sensitive(false); + if (!(SvxZoomEnableFlags::PAGEWIDTH & nValSet)) + m_xPageWidthBtn->set_sensitive(false); + if (!(SvxZoomEnableFlags::WHOLEPAGE & nValSet)) + m_xWholePageBtn->set_sensitive(false); + + SetFactor(nZoom, nButtonId); + } + else + { + const sal_uInt16 nZoom = static_cast(rItem).GetValue(); + SetFactor(nZoom); + } + + if (const SvxViewLayoutItem* pViewLayoutItem = m_rSet.GetItemIfSet(SID_ATTR_VIEWLAYOUT, false)) + { + const sal_uInt16 nColumns = pViewLayoutItem->GetValue(); + const bool bBookMode = pViewLayoutItem->IsBookMode(); + + if (0 == nColumns) + { + m_xAutomaticBtn->set_active(true); + m_xColumnsEdit->set_value(2); + m_xColumnsEdit->set_sensitive(false); + m_xBookModeChk->set_sensitive(false); + } + else if (1 == nColumns) + { + m_xSingleBtn->set_active(true); + m_xColumnsEdit->set_value(2); + m_xColumnsEdit->set_sensitive(false); + m_xBookModeChk->set_sensitive(false); + } + else + { + m_xColumnsBtn->set_active(true); + if (!bBookMode) + { + m_xColumnsEdit->set_value(nColumns); + if (nColumns % 2 != 0) + m_xBookModeChk->set_sensitive(false); + } + else + { + m_xColumnsEdit->set_value(nColumns); + m_xBookModeChk->set_active(true); + } + } + } + else + { + // hide view layout related controls: + m_xViewFrame->set_visible(false); + } +} + +IMPL_LINK_NOARG(SvxZoomDialog, UserHdl, weld::Toggleable&, void) +{ + m_bModified = true; + + if (m_xUserBtn->get_active()) + { + m_xUserEdit->set_sensitive(true); + m_xUserEdit->grab_focus(); + } + else + { + m_xUserEdit->set_sensitive(false); + } +} + +IMPL_LINK_NOARG(SvxZoomDialog, SpinHdl, weld::MetricSpinButton&, void) +{ + if (!m_xUserBtn->get_active()) + return; + + m_bModified = true; +} + +IMPL_LINK_NOARG(SvxZoomDialog, ViewLayoutUserHdl, weld::Toggleable&, void) +{ + m_bModified = true; + + if (m_xAutomaticBtn->get_active() || m_xSingleBtn->get_active()) + { + m_xColumnsEdit->set_sensitive(false); + m_xBookModeChk->set_sensitive(false); + } + else if (m_xColumnsBtn->get_active()) + { + m_xColumnsEdit->set_sensitive(true); + m_xColumnsEdit->grab_focus(); + if (m_xColumnsEdit->get_value() % 2 == 0) + m_xBookModeChk->set_sensitive(true); + } +} + +IMPL_LINK_NOARG(SvxZoomDialog, ViewLayoutSpinHdl, weld::SpinButton&, void) +{ + if (!m_xColumnsBtn->get_active()) + return; + + if (m_xColumnsEdit->get_value() % 2 == 0) + { + m_xBookModeChk->set_sensitive(true); + } + else + { + m_xBookModeChk->set_active(false); + m_xBookModeChk->set_sensitive(false); + } + + m_bModified = true; +} + +IMPL_LINK_NOARG(SvxZoomDialog, ViewLayoutCheckHdl, weld::Toggleable&, void) +{ + if (!m_xColumnsBtn->get_active()) + return; + + m_bModified = true; +} + +IMPL_LINK_NOARG(SvxZoomDialog, OKHdl, weld::Button&, void) +{ + if (m_bModified) + { + SvxZoomItem aZoomItem(SvxZoomType::PERCENT, 0, SID_ATTR_ZOOM); + SvxViewLayoutItem aViewLayoutItem(0, false, SID_ATTR_VIEWLAYOUT); + + sal_uInt16 nFactor = GetFactor(); + + if (SPECIAL_FACTOR == nFactor) + { + if (m_xOptimalBtn->get_active()) + aZoomItem.SetType(SvxZoomType::OPTIMAL); + else if (m_xPageWidthBtn->get_active()) + aZoomItem.SetType(SvxZoomType::PAGEWIDTH); + else if (m_xWholePageBtn->get_active()) + aZoomItem.SetType(SvxZoomType::WHOLEPAGE); + } + else + { + aZoomItem.SetValue(nFactor); + } + + if (m_xAutomaticBtn->get_active()) + { + aViewLayoutItem.SetValue(0); + aViewLayoutItem.SetBookMode(false); + } + if (m_xSingleBtn->get_active()) + { + aViewLayoutItem.SetValue(1); + aViewLayoutItem.SetBookMode(false); + } + else if (m_xColumnsBtn->get_active()) + { + aViewLayoutItem.SetValue(static_cast(m_xColumnsEdit->get_value())); + aViewLayoutItem.SetBookMode(m_xBookModeChk->get_active()); + } + + m_pOutSet.reset(new SfxItemSet(m_rSet)); + m_pOutSet->Put(aZoomItem); + + // don't set attribute in case the whole viewlayout stuff is disabled: + if (m_xViewFrame->get_sensitive()) + m_pOutSet->Put(aViewLayoutItem); + + // memorize value from the UserEdit beyond the dialog + if (SfxObjectShell* pShell = SfxObjectShell::Current()) + { + sal_uInt16 nZoomValue + = static_cast(m_xUserEdit->get_value(FieldUnit::PERCENT)); + pShell->PutItem(SfxUInt16Item(SID_ATTR_ZOOM_USER, nZoomValue)); + } + m_xDialog->response(RET_OK); + } + else + m_xDialog->response(RET_CANCEL); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3