1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include "tablespage.hxx" 21 #include <dsitems.hxx> 22 #include <datasourceconnector.hxx> 23 #include <comphelper/types.hxx> 24 #include <connectivity/dbtools.hxx> 25 #include <connectivity/dbexception.hxx> 26 #include <stringlistitem.hxx> 27 #include <svl/stritem.hxx> 28 #include <strings.hxx> 29 #include <com/sun/star/sdbc/SQLException.hpp> 30 #include <com/sun/star/util/XModifiable.hpp> 31 #include <sqlmessage.hxx> 32 #include <UITools.hxx> 33 #include <osl/diagnose.h> 34 #include <TablesSingleDlg.hxx> 35 #include <tools/diagnose_ex.h> 36 #include <cppuhelper/exc_hlp.hxx> 37 38 namespace dbaui 39 { 40 41 using namespace ::com::sun::star::uno; 42 using namespace ::com::sun::star::sdbc; 43 using namespace ::com::sun::star::sdbcx; 44 using namespace ::com::sun::star::sdb; 45 using namespace ::com::sun::star::beans; 46 using namespace ::com::sun::star::lang; 47 using namespace ::com::sun::star::container; 48 using namespace ::com::sun::star::util; 49 using namespace ::dbtools; 50 using namespace ::comphelper; 51 52 // OTableSubscriptionPage OTableSubscriptionPage(weld::Container * pPage,OTableSubscriptionDialog * pTablesDlg,const SfxItemSet & _rCoreAttrs)53 OTableSubscriptionPage::OTableSubscriptionPage(weld::Container* pPage, OTableSubscriptionDialog* pTablesDlg, const SfxItemSet& _rCoreAttrs) 54 : OGenericAdministrationPage(pPage, pTablesDlg, "dbaccess/ui/tablesfilterpage.ui", "TablesFilterPage", _rCoreAttrs) 55 , m_bCatalogAtStart(true) 56 , m_pTablesDlg(pTablesDlg) 57 , m_xTables(m_xBuilder->weld_widget("TablesFilterPage")) 58 , m_xTablesList(new OTableTreeListBox(m_xBuilder->weld_tree_view("treeview"), true)) 59 { 60 m_xTablesList->init(); 61 62 weld::TreeView& rWidget = m_xTablesList->GetWidget(); 63 64 rWidget.set_size_request(rWidget.get_approximate_digit_width() * 48, 65 rWidget.get_height_rows(12)); 66 67 // initialize the TabListBox 68 rWidget.set_selection_mode(SelectionMode::Multiple); 69 70 rWidget.connect_toggled(LINK(this, OTableSubscriptionPage, OnTreeEntryChecked)); 71 } 72 ~OTableSubscriptionPage()73 OTableSubscriptionPage::~OTableSubscriptionPage() 74 { 75 // just to make sure that our connection will be removed 76 try 77 { 78 ::comphelper::disposeComponent(m_xCurrentConnection); 79 } 80 catch (RuntimeException&) { } 81 } 82 implCheckTables(const Sequence<OUString> & _rTables)83 void OTableSubscriptionPage::implCheckTables(const Sequence< OUString >& _rTables) 84 { 85 // the meta data for the current connection, used for splitting up table names 86 Reference< XDatabaseMetaData > xMeta; 87 try 88 { 89 if (m_xCurrentConnection.is()) 90 xMeta = m_xCurrentConnection->getMetaData(); 91 } 92 catch(SQLException&) 93 { 94 OSL_FAIL("OTableSubscriptionPage::implCheckTables : could not retrieve the current connection's meta data!"); 95 } 96 97 // uncheck all 98 CheckAll(false); 99 100 // check the ones which are in the list 101 OUString sCatalog, sSchema, sName; 102 103 std::unique_ptr<weld::TreeIter> xRootEntry(m_xTablesList->getAllObjectsEntry()); 104 105 for (const OUString& rIncludeTable : _rTables) 106 { 107 if (xMeta.is()) 108 qualifiedNameComponents(xMeta, rIncludeTable, sCatalog, sSchema, sName,::dbtools::EComposeRule::InDataManipulation); 109 else 110 sName = rIncludeTable; 111 112 bool bAllTables = (1 == sName.getLength()) && ('%' == sName[0]); 113 bool bAllSchemas = (1 == sSchema.getLength()) && ('%' == sSchema[0]); 114 115 // the catalog entry 116 std::unique_ptr<weld::TreeIter> xCatalog(m_xTablesList->GetEntryPosByName(sCatalog, xRootEntry.get())); 117 if (!(xCatalog || sCatalog.isEmpty())) 118 // the table (resp. its catalog) referred in this filter entry does not exist anymore 119 continue; 120 121 if (bAllSchemas && xCatalog) 122 { 123 m_xTablesList->checkWildcard(*xCatalog); 124 continue; 125 } 126 127 // the schema entry 128 std::unique_ptr<weld::TreeIter> xSchema = m_xTablesList->GetEntryPosByName(sSchema, (xCatalog ? xCatalog.get() : xRootEntry.get())); 129 if (!(xSchema || sSchema.isEmpty())) 130 // the table (resp. its schema) referred in this filter entry does not exist anymore 131 continue; 132 133 if (bAllTables && xSchema) 134 { 135 m_xTablesList->checkWildcard(*xSchema); 136 continue; 137 } 138 139 std::unique_ptr<weld::TreeIter> xEntry(m_xTablesList->GetEntryPosByName(sName, xSchema ? xSchema.get() : (xCatalog ? xCatalog.get() : xRootEntry.get()))); 140 if (xEntry) 141 m_xTablesList->GetWidget().set_toggle(*xEntry, TRISTATE_TRUE); 142 } 143 m_xTablesList->CheckButtons(); 144 } 145 implCompleteTablesCheck(const css::uno::Sequence<OUString> & _rTableFilter)146 void OTableSubscriptionPage::implCompleteTablesCheck( const css::uno::Sequence< OUString >& _rTableFilter ) 147 { 148 if (!_rTableFilter.hasElements()) 149 { // no tables visible 150 CheckAll(false); 151 } 152 else 153 { 154 if ((1 == _rTableFilter.getLength()) && _rTableFilter[0] == "%") 155 { // all tables visible 156 CheckAll(); 157 } 158 else 159 implCheckTables( _rTableFilter ); 160 } 161 } 162 implInitControls(const SfxItemSet & _rSet,bool _bSaveValue)163 void OTableSubscriptionPage::implInitControls(const SfxItemSet& _rSet, bool _bSaveValue) 164 { 165 // check whether or not the selection is invalid or readonly (invalid implies readonly, but not vice versa) 166 bool bValid, bReadonly; 167 getFlags(_rSet, bValid, bReadonly); 168 169 // get the name of the data source we're working for 170 const SfxStringItem* pNameItem = _rSet.GetItem<SfxStringItem>(DSID_NAME); 171 OSL_ENSURE(pNameItem, "OTableSubscriptionPage::implInitControls: missing the name attribute!"); 172 OUString sDSName = pNameItem->GetValue(); 173 174 if (bValid && !sDSName.isEmpty() && !m_xCurrentConnection.is() ) 175 { // get the current table list from the connection for the current settings 176 177 // the PropertyValues for the current dialog settings 178 Sequence< PropertyValue > aConnectionParams; 179 OSL_ENSURE(m_pTablesDlg, "OTableSubscriptionPage::implInitControls: need a parent dialog doing the translation!"); 180 if ( m_pTablesDlg ) 181 { 182 if (!m_pTablesDlg->getCurrentSettings(aConnectionParams)) 183 { 184 m_xTablesList->GetWidget().clear(); 185 m_pTablesDlg->endExecution(); 186 return; 187 } 188 } 189 190 // fill the table list with this connection information 191 SQLExceptionInfo aErrorInfo; 192 193 try 194 { 195 weld::WaitObject aWaitCursor(GetFrameWeld()); 196 197 Reference<XPropertySet> xProp = m_pTablesDlg->getCurrentDataSource(); 198 OSL_ENSURE(xProp.is(),"No data source set!"); 199 if ( xProp.is() ) 200 { 201 Any aTableFilter = xProp->getPropertyValue(PROPERTY_TABLEFILTER); 202 Any aTableTypeFilter = xProp->getPropertyValue(PROPERTY_TABLETYPEFILTER); 203 204 Reference<XModifiable> xModi(getDataSourceOrModel(xProp),UNO_QUERY); 205 bool bModified = ( xModi.is() && xModi->isModified() ); 206 207 Sequence< OUString > aNewTableFilter { "%" }; 208 xProp->setPropertyValue(PROPERTY_TABLEFILTER,makeAny(aNewTableFilter)); 209 210 xProp->setPropertyValue( PROPERTY_TABLETYPEFILTER, makeAny( Sequence< OUString >() ) ); 211 Reference< css::lang::XEventListener> xEvt; 212 aErrorInfo = ::dbaui::createConnection(xProp, m_xORB, xEvt, m_xCurrentConnection); 213 214 xProp->setPropertyValue(PROPERTY_TABLEFILTER,aTableFilter); 215 xProp->setPropertyValue(PROPERTY_TABLETYPEFILTER,aTableTypeFilter); 216 217 if ( xModi.is() && !bModified ) 218 xModi->setModified(false); 219 220 } 221 222 if ( m_xCurrentConnection.is() ) 223 { 224 m_xTablesList->UpdateTableList( m_xCurrentConnection ); 225 if (m_pTablesDlg) 226 m_pTablesDlg->successfullyConnected(); 227 } 228 } 229 catch (const SQLException&) 230 { 231 aErrorInfo = ::cppu::getCaughtException(); 232 } 233 234 if (aErrorInfo.isValid()) 235 { 236 // establishing the connection failed. Show an error window and exit. 237 OSQLMessageBox aMessageBox(GetFrameWeld(), aErrorInfo); 238 aMessageBox.run(); 239 m_xTables->set_sensitive(false); 240 m_xTablesList->GetWidget().clear(); 241 242 if ( m_pTablesDlg ) 243 { 244 m_pTablesDlg->clearPassword(); 245 m_pTablesDlg->endExecution(); 246 } 247 } 248 else 249 { 250 // in addition, we need some infos about the connection used 251 m_sCatalogSeparator = "."; // (default) 252 m_bCatalogAtStart = true; // (default) 253 try 254 { 255 Reference< XDatabaseMetaData > xMeta; 256 if (m_xCurrentConnection.is()) 257 xMeta = m_xCurrentConnection->getMetaData(); 258 if (xMeta.is() && xMeta->supportsCatalogsInDataManipulation()) 259 { 260 m_sCatalogSeparator = xMeta->getCatalogSeparator(); 261 m_bCatalogAtStart = xMeta->isCatalogAtStart(); 262 } 263 } 264 catch(Exception&) 265 { 266 DBG_UNHANDLED_EXCEPTION("dbaccess"); 267 } 268 } 269 } 270 271 // get the current table filter 272 const OStringListItem* pTableFilter = _rSet.GetItem<OStringListItem>(DSID_TABLEFILTER); 273 Sequence< OUString > aTableFilter; 274 if (pTableFilter) 275 aTableFilter = pTableFilter->getList(); 276 277 implCompleteTablesCheck( aTableFilter ); 278 279 // expand the first entry by default 280 std::unique_ptr<weld::TreeIter> xExpand = m_xTablesList->getAllObjectsEntry(); 281 while (xExpand) 282 { 283 m_xTablesList->GetWidget().expand_row(*xExpand); 284 if (!m_xTablesList->GetWidget().iter_children(*xExpand)) 285 break; 286 std::unique_ptr<weld::TreeIter> xSibling(m_xTablesList->GetWidget().make_iterator(xExpand.get())); 287 if (m_xTablesList->GetWidget().iter_next_sibling(*xSibling)) 288 xExpand.reset(); 289 } 290 291 // update the toolbox according the current selection and check state 292 OGenericAdministrationPage::implInitControls(_rSet, _bSaveValue); 293 } 294 CheckAll(bool _bCheck)295 void OTableSubscriptionPage::CheckAll( bool _bCheck ) 296 { 297 std::unique_ptr<weld::TreeIter> xEntry(m_xTablesList->GetWidget().make_iterator()); 298 if (m_xTablesList->GetWidget().get_iter_first(*xEntry)) 299 { 300 do 301 { 302 m_xTablesList->GetWidget().set_toggle(*xEntry, _bCheck ? TRISTATE_TRUE : TRISTATE_FALSE); 303 } 304 while (m_xTablesList->GetWidget().iter_next(*xEntry)); 305 } 306 307 if (_bCheck) 308 { 309 auto xRoot = m_xTablesList->getAllObjectsEntry(); 310 if (xRoot) 311 m_xTablesList->checkWildcard(*xRoot); 312 } 313 } 314 DeactivatePage(SfxItemSet * _pSet)315 DeactivateRC OTableSubscriptionPage::DeactivatePage(SfxItemSet* _pSet) 316 { 317 DeactivateRC nResult = OGenericAdministrationPage::DeactivatePage(_pSet); 318 319 // dispose the connection, we don't need it anymore, so we're not wasting resources 320 try 321 { 322 ::comphelper::disposeComponent(m_xCurrentConnection); 323 } 324 catch (RuntimeException&) { } 325 326 return nResult; 327 } 328 IMPL_LINK(OTableSubscriptionPage,OnTreeEntryChecked,const weld::TreeView::iter_col &,rRowCol,void)329 IMPL_LINK(OTableSubscriptionPage, OnTreeEntryChecked, const weld::TreeView::iter_col&, rRowCol, void) 330 { 331 m_xTablesList->checkedButton_noBroadcast(rRowCol.first); 332 callModifiedHdl(); 333 } 334 collectDetailedSelection() const335 Sequence< OUString > OTableSubscriptionPage::collectDetailedSelection() const 336 { 337 Sequence< OUString > aTableFilter; 338 constexpr OUStringLiteral sWildcard = u"%"; 339 340 std::unique_ptr<weld::TreeIter> xAllObjectsEntry(m_xTablesList->getAllObjectsEntry()); 341 if (!xAllObjectsEntry) 342 return aTableFilter; 343 std::unique_ptr<weld::TreeIter> xEntry(m_xTablesList->GetWidget().make_iterator(xAllObjectsEntry.get())); 344 if (!m_xTablesList->GetWidget().iter_next(*xEntry)) 345 xEntry.reset(); 346 while (xEntry) 347 { 348 bool bCatalogWildcard = false; 349 bool bSchemaWildcard = false; 350 std::unique_ptr<weld::TreeIter> xSchema; 351 std::unique_ptr<weld::TreeIter> xCatalog; 352 353 if (m_xTablesList->GetWidget().get_toggle(*xEntry) == TRISTATE_TRUE && !m_xTablesList->GetWidget().iter_has_child(*xEntry)) 354 { // checked and a leaf, which means it's no catalog, no schema, but a real table 355 OUStringBuffer sComposedName; 356 OUString sCatalog; 357 if (m_xTablesList->GetWidget().get_iter_depth(*xEntry)) 358 { 359 xSchema = m_xTablesList->GetWidget().make_iterator(xEntry.get()); 360 m_xTablesList->GetWidget().iter_parent(*xSchema); 361 if (xAllObjectsEntry->equal(*xSchema)) 362 { 363 // do not want to have the root entry 364 xSchema.reset(); 365 } 366 367 if (xSchema) 368 { // it's a real schema entry, not the "all objects" root 369 if (m_xTablesList->GetWidget().get_iter_depth(*xSchema)) 370 { 371 xCatalog = m_xTablesList->GetWidget().make_iterator(xSchema.get()); 372 m_xTablesList->GetWidget().iter_parent(*xCatalog); 373 if (xAllObjectsEntry->equal(*xCatalog)) 374 { 375 // do not want to have the root entry 376 xCatalog.reset(); 377 } 378 379 if (xCatalog) 380 { // it's a real catalog entry, not the "all objects" root 381 bCatalogWildcard = m_xTablesList->isWildcardChecked(*xCatalog); 382 if (m_bCatalogAtStart) 383 { 384 sComposedName.append(m_xTablesList->GetWidget().get_text(*xCatalog) + m_sCatalogSeparator); 385 if (bCatalogWildcard) 386 sComposedName.append(sWildcard); 387 } 388 else 389 { 390 if (bCatalogWildcard) 391 sCatalog = sWildcard; 392 else 393 sCatalog.clear(); 394 sCatalog += m_sCatalogSeparator + m_xTablesList->GetWidget().get_text(*xCatalog) ; 395 } 396 } 397 } 398 bSchemaWildcard = m_xTablesList->isWildcardChecked(*xSchema); 399 sComposedName.append(m_xTablesList->GetWidget().get_text(*xSchema) + "."); 400 } 401 402 if (bSchemaWildcard) 403 sComposedName.append(sWildcard); 404 } 405 if (!bSchemaWildcard && !bCatalogWildcard) 406 sComposedName.append(m_xTablesList->GetWidget().get_text(*xEntry)); 407 408 if (!m_bCatalogAtStart && !bCatalogWildcard) 409 sComposedName.append(sCatalog); 410 411 // need some space 412 sal_Int32 nOldLen = aTableFilter.getLength(); 413 aTableFilter.realloc(nOldLen + 1); 414 // add the new name 415 aTableFilter[nOldLen] = sComposedName.makeStringAndClear(); 416 } 417 418 if (bCatalogWildcard) 419 xEntry = implNextSibling(xCatalog.get()); 420 else if (bSchemaWildcard) 421 xEntry = implNextSibling(xSchema.get()); 422 else 423 { 424 if (!m_xTablesList->GetWidget().iter_next(*xEntry)) 425 xEntry.reset(); 426 } 427 } 428 429 return aTableFilter; 430 } 431 implNextSibling(const weld::TreeIter * pEntry) const432 std::unique_ptr<weld::TreeIter> OTableSubscriptionPage::implNextSibling(const weld::TreeIter* pEntry) const 433 { 434 std::unique_ptr<weld::TreeIter> xReturn; 435 if (pEntry) 436 { 437 xReturn = m_xTablesList->GetWidget().make_iterator(pEntry); 438 if (!m_xTablesList->GetWidget().iter_next_sibling(*xReturn)) 439 { 440 std::unique_ptr<weld::TreeIter> xParent = m_xTablesList->GetWidget().make_iterator(pEntry); 441 if (m_xTablesList->GetWidget().iter_parent(*xParent)) 442 xReturn = implNextSibling(xParent.get()); 443 else 444 xReturn.reset(); 445 } 446 } 447 return xReturn; 448 } 449 FillItemSet(SfxItemSet * _rCoreAttrs)450 bool OTableSubscriptionPage::FillItemSet( SfxItemSet* _rCoreAttrs ) 451 { 452 bool bValid, bReadonly; 453 getFlags(*_rCoreAttrs, bValid, bReadonly); 454 455 if (!bValid || bReadonly) 456 // don't store anything if the data we're working with is invalid or readonly 457 return true; 458 459 // create the output string which contains all the table names 460 if ( m_xCurrentConnection.is() ) 461 { // collect the table filter data only if we have a connection - else no tables are displayed at all 462 Sequence< OUString > aTableFilter; 463 auto xRoot = m_xTablesList->getAllObjectsEntry(); 464 if (xRoot && m_xTablesList->isWildcardChecked(*xRoot)) 465 { 466 aTableFilter.realloc(1); 467 aTableFilter[0] = "%"; 468 } 469 else 470 { 471 aTableFilter = collectDetailedSelection(); 472 } 473 _rCoreAttrs->Put( OStringListItem(DSID_TABLEFILTER, aTableFilter) ); 474 } 475 476 return true; 477 } 478 fillControls(std::vector<std::unique_ptr<ISaveValueWrapper>> &)479 void OTableSubscriptionPage::fillControls(std::vector< std::unique_ptr<ISaveValueWrapper> >& /*_rControlList*/) 480 { 481 } 482 fillWindows(std::vector<std::unique_ptr<ISaveValueWrapper>> & _rControlList)483 void OTableSubscriptionPage::fillWindows(std::vector< std::unique_ptr<ISaveValueWrapper> >& _rControlList) 484 { 485 _rControlList.emplace_back(new ODisableWidgetWrapper<weld::Widget>(m_xTables.get())); 486 } 487 } // namespace dbaui 488 489 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 490