diff options
Diffstat (limited to 'dbaccess/source/ui/tabledesign/TableController.cxx')
-rw-r--r-- | dbaccess/source/ui/tabledesign/TableController.cxx | 1511 |
1 files changed, 1511 insertions, 0 deletions
diff --git a/dbaccess/source/ui/tabledesign/TableController.cxx b/dbaccess/source/ui/tabledesign/TableController.cxx new file mode 100644 index 000000000..f88110b48 --- /dev/null +++ b/dbaccess/source/ui/tabledesign/TableController.cxx @@ -0,0 +1,1511 @@ +/* -*- 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 <FieldDescriptions.hxx> +#include "TEditControl.hxx" +#include <TableController.hxx> +#include <TableDesignView.hxx> +#include <TableRow.hxx> +#include <TypeInfo.hxx> +#include <UITools.hxx> +#include <browserids.hxx> +#include <core_resource.hxx> +#include <dbu_reghelper.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <defaultobjectnamecheck.hxx> +#include <dlgsave.hxx> +#include <indexdialog.hxx> +#include <sqlmessage.hxx> +#include <uiservices.hxx> + +#include <com/sun/star/frame/XTitleChangeListener.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/sdbcx/XAlterTable.hpp> +#include <com/sun/star/sdbcx/XAppend.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XDrop.hpp> +#include <com/sun/star/sdbcx/XIndexesSupplier.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> + +#include <comphelper/processfactory.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbmetadata.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <algorithm> +#include <functional> + +extern "C" void createRegistryInfo_OTableControl() +{ + static ::dbaui::OMultiInstanceAutoRegistration< ::dbaui::OTableController > aAutoRegistration; +} + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::util; +using namespace ::dbtools; +using namespace ::dbaui; +using namespace ::comphelper; + +// number of columns when creating it from scratch +#define NEWCOLS 128 + +namespace +{ + void dropTable(const Reference<XNameAccess>& _rxTable,const OUString& _sTableName) + { + if ( _rxTable->hasByName(_sTableName) ) + { + Reference<XDrop> xNameCont(_rxTable,UNO_QUERY); + OSL_ENSURE(xNameCont.is(),"No drop interface for tables!"); + if ( xNameCont.is() ) + xNameCont->dropByName(_sTableName); + } + } +} + +OUString SAL_CALL OTableController::getImplementationName() +{ + return getImplementationName_Static(); +} + +OUString OTableController::getImplementationName_Static() +{ + return "org.openoffice.comp.dbu.OTableDesign"; +} + +Sequence< OUString> OTableController::getSupportedServiceNames_Static() +{ + return { "com.sun.star.sdb.TableDesign" }; +} + +Sequence< OUString> SAL_CALL OTableController::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +Reference< XInterface > OTableController::Create(const Reference<XMultiServiceFactory >& _rxFactory) +{ + return *(new OTableController(comphelper::getComponentContext(_rxFactory))); +} + +OTableController::OTableController(const Reference< XComponentContext >& _rM) : OTableController_BASE(_rM) + ,m_sTypeNames(DBA_RES(STR_TABLEDESIGN_DBFIELDTYPES)) + ,m_pTypeInfo() + ,m_bAllowAutoIncrementValue(false) + ,m_bNew(true) +{ + + InvalidateAll(); + m_pTypeInfo = std::make_shared<OTypeInfo>(); + m_pTypeInfo->aUIName = m_sTypeNames.getToken(TYPE_OTHER, ';'); +} + +OTableController::~OTableController() +{ + m_aTypeInfoIndex.clear(); + m_aTypeInfo.clear(); + +} + +void OTableController::startTableListening() +{ + Reference< XComponent > xComponent(m_xTable, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast<XModifyListener*>(this)); +} + +void OTableController::stopTableListening() +{ + Reference< XComponent > xComponent(m_xTable, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(static_cast<XModifyListener*>(this)); +} + +void OTableController::disposing() +{ + OTableController_BASE::disposing(); + clearView(); + + m_vRowList.clear(); +} + +FeatureState OTableController::GetState(sal_uInt16 _nId) const +{ + FeatureState aReturn; + // disabled automatically + + switch (_nId) + { + case ID_BROWSER_CLOSE: + aReturn.bEnabled = true; + break; + case ID_BROWSER_EDITDOC: + aReturn.bChecked = isEditable(); + aReturn.bEnabled = true; + break; + case ID_BROWSER_SAVEDOC: + aReturn.bEnabled = isEditable() && std::any_of(m_vRowList.begin(),m_vRowList.end(),std::mem_fn(&OTableRow::isValid)); + break; + case ID_BROWSER_SAVEASDOC: + aReturn.bEnabled = isConnected() && isEditable(); + if ( aReturn.bEnabled ) + { + aReturn.bEnabled = std::any_of(m_vRowList.begin(),m_vRowList.end(), + std::mem_fn(&OTableRow::isValid)); + } + break; + + case ID_BROWSER_CUT: + aReturn.bEnabled = isEditable() && m_aCurrentFrame.isActive() && getView() && static_cast<OTableDesignView*>(getView())->isCutAllowed(); + break; + case ID_BROWSER_COPY: + aReturn.bEnabled = m_aCurrentFrame.isActive() && getView() && static_cast<OTableDesignView*>(getView())->isCopyAllowed(); + break; + case ID_BROWSER_PASTE: + aReturn.bEnabled = isEditable() && m_aCurrentFrame.isActive() && getView() && static_cast<OTableDesignView*>(getView())->isPasteAllowed(); + break; + case SID_INDEXDESIGN: + aReturn.bEnabled = + ( ( ((!m_bNew && impl_isModified()) || impl_isModified()) + || Reference< XIndexesSupplier >(m_xTable, UNO_QUERY).is() + ) + && isConnected() + ); + if ( aReturn.bEnabled ) + { + aReturn.bEnabled = std::any_of(m_vRowList.begin(),m_vRowList.end(), + std::mem_fn(&OTableRow::isValid)); + } + break; + default: + aReturn = OTableController_BASE::GetState(_nId); + } + return aReturn; +} + +void OTableController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs) +{ + switch(_nId) + { + case ID_BROWSER_EDITDOC: + setEditable(!isEditable()); + static_cast<OTableDesignView*>(getView())->setReadOnly(!isEditable()); + InvalidateFeature(ID_BROWSER_SAVEDOC); + InvalidateFeature(ID_BROWSER_PASTE); + InvalidateFeature(SID_BROWSER_CLEAR_QUERY); + break; + case ID_BROWSER_SAVEASDOC: + doSaveDoc(true); + break; + case ID_BROWSER_SAVEDOC: + static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->SaveCurRow(); + doSaveDoc(false); + break; + case ID_BROWSER_CUT: + static_cast<OTableDesignView*>(getView())->cut(); + break; + case ID_BROWSER_COPY: + static_cast<OTableDesignView*>(getView())->copy(); + break; + case ID_BROWSER_PASTE: + static_cast<OTableDesignView*>(getView())->paste(); + break; + case SID_INDEXDESIGN: + doEditIndexes(); + break; + default: + OTableController_BASE::Execute(_nId,aArgs); + } + InvalidateFeature(_nId); +} + +bool OTableController::doSaveDoc(bool _bSaveAs) +{ + if (!isConnected()) + reconnect(true); // ask the user for a new connection + Reference<XTablesSupplier> xTablesSup(getConnection(),UNO_QUERY); + + if (!xTablesSup.is()) + { + OUString aMessage(DBA_RES(STR_TABLEDESIGN_CONNECTION_MISSING)); + OSQLWarningBox aWarning(getFrameWeld(), aMessage); + aWarning.run(); + return false; + } + + // check if a column exists + // TODO + + Reference<XNameAccess> xTables; + OUString sCatalog, sSchema; + + bool bNew = m_sName.isEmpty(); + bNew = bNew || m_bNew || _bSaveAs; + + try + { + xTables = xTablesSup->getTables(); + OSL_ENSURE(xTables.is(),"The tables can't be null!"); + bNew = bNew || (xTables.is() && !xTables->hasByName(m_sName)); + + // first we need a name for our query so ask the user + if(bNew) + { + OUString aName = DBA_RES(STR_TBL_TITLE); + OUString aDefaultName = aName.getToken(0,' '); + aDefaultName = ::dbtools::createUniqueName(xTables,aDefaultName); + + DynamicTableOrQueryNameCheck aNameChecker( getConnection(), CommandType::TABLE ); + OSaveAsDlg aDlg(getFrameWeld(), CommandType::TABLE, getORB(), getConnection(), aDefaultName, aNameChecker, SADFlags::NONE); + if (aDlg.run() != RET_OK) + return false; + + m_sName = aDlg.getName(); + sCatalog = aDlg.getCatalog(); + sSchema = aDlg.getSchema(); + } + + // did we get a name + if(m_sName.isEmpty()) + return false; + } + catch(Exception&) + { + OSL_FAIL("OTableController::doSaveDoc: nothing is expected to happen here!"); + } + + bool bAlter = false; + bool bError = false; + SQLExceptionInfo aInfo; + try + { + // check the columns for double names + if(!checkColumns(bNew || !xTables->hasByName(m_sName))) + { + return false; + } + + Reference<XPropertySet> xTable; + if(bNew || !xTables->hasByName(m_sName)) // just to make sure the table already exists + { + dropTable(xTables,m_sName); + + Reference<XDataDescriptorFactory> xFact(xTables,UNO_QUERY); + OSL_ENSURE(xFact.is(),"OTableController::doSaveDoc: No XDataDescriptorFactory available!"); + xTable = xFact->createDataDescriptor(); + OSL_ENSURE(xTable.is(),"OTableController::doSaveDoc: Create query failed!"); + // to set the name is only allowed when the query is new + xTable->setPropertyValue(PROPERTY_CATALOGNAME,makeAny(sCatalog)); + xTable->setPropertyValue(PROPERTY_SCHEMANAME,makeAny(sSchema)); + xTable->setPropertyValue(PROPERTY_NAME,makeAny(m_sName)); + + // now append the columns + Reference<XColumnsSupplier> xColSup(xTable,UNO_QUERY); + appendColumns(xColSup,bNew); + // now append the primary key + Reference<XKeysSupplier> xKeySup(xTable,UNO_QUERY); + appendPrimaryKey(xKeySup,bNew); + } + // now set the properties + if(bNew) + { + Reference<XAppend> xAppend(xTables,UNO_QUERY); + OSL_ENSURE(xAppend.is(),"OTableController::doSaveDoc: No XAppend Interface!"); + xAppend->appendByDescriptor(xTable); + + assignTable(); + if(!m_xTable.is()) // correct name and try again + { + // it can be that someone inserted new data for us + m_sName = ::dbtools::composeTableName( getConnection()->getMetaData(), xTable, ::dbtools::EComposeRule::InDataManipulation, false ); + assignTable(); + } + // now check if our datasource has set a tablefilter and if append the new table name to it + ::dbaui::appendToFilter(getConnection(), m_sName, getORB(), getFrameWeld()); // we are not interested in the return value + Reference< frame::XTitleChangeListener> xEventListener(impl_getTitleHelper_throw(),UNO_QUERY); + if ( xEventListener.is() ) + { + frame::TitleChangedEvent aEvent; + xEventListener->titleChanged(aEvent); + } + releaseNumberForComponent(); + } + else if(m_xTable.is()) + { + bAlter = true; + alterColumns(); + } + reSyncRows(); + } + catch(const SQLContext& e) + { + aInfo = SQLExceptionInfo(e); + } + catch(const SQLWarning& e) + { + aInfo = SQLExceptionInfo(e); + } + catch(const SQLException& e) + { + aInfo = SQLExceptionInfo(e); + } + catch(const ElementExistException& ) + { + OUString sText( DBA_RES( STR_NAME_ALREADY_EXISTS ) ); + sText = sText.replaceFirst( "#" , m_sName); + OSQLMessageBox aDlg(getFrameWeld(), DBA_RES( STR_ERROR_DURING_CREATION ), sText, MessBoxStyle::Ok, MessageType::Error); + aDlg.run(); + bError = true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + bError = true; + } + + if ( aInfo.isValid() ) + aInfo.prepend( DBA_RES( STR_TABLEDESIGN_SAVE_ERROR ) ); + showError(aInfo); + + if (aInfo.isValid() || bError) + { + if(!bAlter || bNew) + { + m_sName.clear(); + stopTableListening(); + m_xTable = nullptr; + } + } + return ! (aInfo.isValid() || bError); +} + +void OTableController::doEditIndexes() +{ + // table needs to be saved before editing indexes + if (m_bNew || isModified()) + { + std::unique_ptr<weld::MessageDialog> xAsk(Application::CreateMessageDialog(getFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + DBA_RES(STR_QUERY_SAVE_TABLE_EDIT_INDEXES))); + if (RET_YES != xAsk->run()) + return; + + if (!doSaveDoc(false)) + return; + + OSL_ENSURE(!m_bNew && !isModified(), "OTableController::doEditIndexes: what the hell did doSaveDoc do?"); + } + + Reference< XNameAccess > xIndexes; // will be the keys of the table + Sequence< OUString > aFieldNames; // will be the column names of the table + try + { + // get the keys + Reference< XIndexesSupplier > xIndexesSupp(m_xTable, UNO_QUERY); + if (xIndexesSupp.is()) + { + xIndexes = xIndexesSupp->getIndexes(); + OSL_ENSURE(xIndexes.is(), "OTableController::doEditIndexes: no keys got from the indexes supplier!"); + } + else + OSL_FAIL("OTableController::doEditIndexes: should never have reached this (no indexes supplier)!"); + + // get the field names + Reference< XColumnsSupplier > xColSupp(m_xTable, UNO_QUERY); + OSL_ENSURE(xColSupp.is(), "OTableController::doEditIndexes: no columns supplier!"); + if (xColSupp.is()) + { + Reference< XNameAccess > xCols = xColSupp->getColumns(); + OSL_ENSURE(xCols.is(), "OTableController::doEditIndexes: no columns!"); + if (xCols.is()) + aFieldNames = xCols->getElementNames(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if (!xIndexes.is()) + return; + + DbaIndexDialog aDialog(getFrameWeld(), aFieldNames, xIndexes, getConnection(), getORB()); + if (RET_OK != aDialog.run()) + return; + +} + +void OTableController::impl_initialize() +{ + try + { + OTableController_BASE::impl_initialize(); + + const NamedValueCollection& rArguments( getInitParams() ); + + rArguments.get_ensureType( PROPERTY_CURRENTTABLE, m_sName ); + + // read autoincrement value set in the datasource + ::dbaui::fillAutoIncrementValue(getDataSource(),m_bAllowAutoIncrementValue,m_sAutoIncrementValue); + + assignTable(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + try + { + ::dbaui::fillTypeInfo(getConnection(),m_sTypeNames,m_aTypeInfo,m_aTypeInfoIndex); // fill the needed type information + } + catch(const SQLException&) + { + OSQLWarningBox aWarning(getFrameWeld(), DBA_RES( STR_NO_TYPE_INFO_AVAILABLE)); + aWarning.run(); + throw; + } + try + { + loadData(); // fill the column information from the table + getView()->initialize(); // show the windows and fill with our information + ClearUndoManager(); + setModified(false); // and we are not modified yet + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +bool OTableController::Construct(vcl::Window* pParent) +{ + setView( VclPtr<OTableDesignView>::Create( pParent, getORB(), *this ) ); + OTableController_BASE::Construct(pParent); + return true; +} + +sal_Bool SAL_CALL OTableController::suspend(sal_Bool /*_bSuspend*/) +{ + if ( getBroadcastHelper().bInDispose || getBroadcastHelper().bDisposed ) + return true; + + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + if ( getView() && getView()->IsInModalMode() ) + return false; + if ( getView() ) + static_cast<OTableDesignView*>(getView())->GrabFocus(); + bool bCheck = true; + if ( isModified() ) + { + if ( std::any_of(m_vRowList.begin(),m_vRowList.end(), + std::mem_fn(&OTableRow::isValid)) ) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), "dbaccess/ui/tabledesignsavemodifieddialog.ui")); + std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("TableDesignSaveModifiedDialog")); + switch (xQuery->run()) + { + case RET_YES: + Execute(ID_BROWSER_SAVEDOC,Sequence<PropertyValue>()); + if ( isModified() ) + bCheck = false; // when we save the table this must be false else some press cancel + break; + case RET_CANCEL: + bCheck = false; + break; + default: + break; + } + } + else if ( !m_bNew ) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), "dbaccess/ui/deleteallrowsdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("DeleteAllRowsDialog")); + switch (xQuery->run()) + { + case RET_YES: + { + try + { + Reference<XTablesSupplier> xTablesSup(getConnection(),UNO_QUERY); + Reference<XNameAccess> xTables = xTablesSup->getTables(); + dropTable(xTables,m_sName); + } + catch(const Exception&) + { + OSL_FAIL("OTableController::suspend: nothing is expected to happen here!"); + } + + } + break; + case RET_CANCEL: + bCheck = false; + break; + default: + break; + } + } + } + + return bCheck; +} + +void OTableController::describeSupportedFeatures() +{ + OSingleDocumentController::describeSupportedFeatures(); + + implDescribeSupportedFeature( ".uno:Redo", ID_BROWSER_REDO, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:Save", ID_BROWSER_SAVEDOC, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:Undo", ID_BROWSER_UNDO, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:NewDoc", SID_NEWDOC, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:SaveAs", ID_BROWSER_SAVEASDOC, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:DBIndexDesign", SID_INDEXDESIGN, CommandGroup::APPLICATION ); + implDescribeSupportedFeature( ".uno:EditDoc", ID_BROWSER_EDITDOC, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:GetUndoStrings", SID_GETUNDOSTRINGS ); + implDescribeSupportedFeature( ".uno:GetRedoStrings", SID_GETREDOSTRINGS ); +} + +void OTableController::impl_onModifyChanged() +{ + OSingleDocumentController::impl_onModifyChanged(); + InvalidateFeature( SID_INDEXDESIGN ); +} + +void SAL_CALL OTableController::disposing( const EventObject& _rSource ) +{ + if ( _rSource.Source == m_xTable ) + { // some deleted our table so we have a new one + stopTableListening(); + m_xTable = nullptr; + m_bNew = true; + setModified(true); + } + else + OTableController_BASE::disposing( _rSource ); +} + +void OTableController::losingConnection( ) +{ + // let the base class do its reconnect + OTableController_BASE::losingConnection( ); + + // remove from the table + Reference< XComponent > xComponent(m_xTable, UNO_QUERY); + if (xComponent.is()) + { + Reference<XEventListener> xEvtL( static_cast< ::cppu::OWeakObject*>(this), UNO_QUERY); + xComponent->removeEventListener(xEvtL); + } + stopTableListening(); + m_xTable = nullptr; + assignTable(); + if(!m_xTable.is()) + { + m_bNew = true; + setModified(true); + } + InvalidateAll(); +} + +TOTypeInfoSP OTableController::getTypeInfoByType(sal_Int32 _nDataType) const +{ + return queryTypeInfoByType(_nDataType,m_aTypeInfo); +} + +void OTableController::appendColumns(Reference<XColumnsSupplier> const & _rxColSup, bool _bNew, bool _bKeyColumns) +{ + try + { + // now append the columns + OSL_ENSURE(_rxColSup.is(),"No columns supplier"); + if(!_rxColSup.is()) + return; + Reference<XNameAccess> xColumns = _rxColSup->getColumns(); + OSL_ENSURE(xColumns.is(),"No columns"); + Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY); + + Reference<XAppend> xAppend(xColumns,UNO_QUERY); + OSL_ENSURE(xAppend.is(),"No XAppend Interface!"); + + for (auto const& row : m_vRowList) + { + OSL_ENSURE(row,"OTableRow is null!"); + OFieldDescription* pField = row->GetActFieldDescr(); + if ( !pField || (!_bNew && row->IsReadOnly() && !_bKeyColumns) ) + continue; + + Reference<XPropertySet> xColumn; + if(pField->IsPrimaryKey() || !_bKeyColumns) + xColumn = xColumnFactory->createDataDescriptor(); + if(xColumn.is()) + { + if(!_bKeyColumns) + ::dbaui::setColumnProperties(xColumn,pField); + else + xColumn->setPropertyValue(PROPERTY_NAME,makeAny(pField->GetName())); + + xAppend->appendByDescriptor(xColumn); + xColumn = nullptr; + // now only the settings are missing + if(xColumns->hasByName(pField->GetName())) + { + xColumns->getByName(pField->GetName()) >>= xColumn; + if(xColumn.is()) + pField->copyColumnSettingsTo(xColumn); + } + else + { + OSL_FAIL("OTableController::appendColumns: invalid field name!"); + } + + } + } + } + catch(const SQLException& ) + { + showError( SQLExceptionInfo( ::cppu::getCaughtException() ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void OTableController::appendPrimaryKey(Reference<XKeysSupplier> const & _rxSup, bool _bNew) +{ + if(!_rxSup.is()) + return; // the database doesn't support keys + + OSL_ENSURE(_rxSup.is(),"No XKeysSupplier!"); + Reference<XIndexAccess> xKeys = _rxSup->getKeys(); + Reference<XPropertySet> xProp; + if (!xKeys.is()) + return; + const sal_Int32 nCount = xKeys->getCount(); + for(sal_Int32 i=0;i< nCount ;++i) + { + xKeys->getByIndex(i) >>= xProp; + sal_Int32 nKeyType = 0; + xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType; + if(KeyType::PRIMARY == nKeyType) + { + return; // primary key already exists after appending a column + } + } + Reference<XDataDescriptorFactory> xKeyFactory(xKeys,UNO_QUERY); + OSL_ENSURE(xKeyFactory.is(),"No XDataDescriptorFactory Interface!"); + if ( !xKeyFactory.is() ) + return; + Reference<XAppend> xAppend(xKeyFactory,UNO_QUERY); + OSL_ENSURE(xAppend.is(),"No XAppend Interface!"); + + Reference<XPropertySet> xKey = xKeyFactory->createDataDescriptor(); + OSL_ENSURE(xKey.is(),"Key is null!"); + xKey->setPropertyValue(PROPERTY_TYPE,makeAny(KeyType::PRIMARY)); + + Reference<XColumnsSupplier> xColSup(xKey,UNO_QUERY); + if(xColSup.is()) + { + appendColumns(xColSup,_bNew,true); + Reference<XNameAccess> xColumns = xColSup->getColumns(); + if(xColumns->hasElements()) + xAppend->appendByDescriptor(xKey); + } +} + +void OTableController::loadData() +{ + // if the data structure already exists, empty it + m_vRowList.clear(); + + std::shared_ptr<OTableRow> pTabEdRow; + Reference< XDatabaseMetaData> xMetaData = getMetaData( ); + // fill data structure with data from DataDefinitionObject + if(m_xTable.is() && xMetaData.is()) + { + Reference<XColumnsSupplier> xColSup(m_xTable,UNO_QUERY); + OSL_ENSURE(xColSup.is(),"No XColumnsSupplier!"); + Reference<XNameAccess> xColumns = xColSup->getColumns(); + // ReadOnly-Flag + // For Drop no row may be editable + // For Add only the empty rows may be editable + // For Add and Drop all rows can be edited + // sal_Bool bReadOldRow = xMetaData->supportsAlterTableWithAddColumn() && xMetaData->supportsAlterTableWithDropColumn(); + bool bIsAlterAllowed = isAlterAllowed(); + + const Sequence<OUString> aColNames = xColumns->getElementNames(); + for(const OUString& rColumn : aColNames) + { + Reference<XPropertySet> xColumn; + xColumns->getByName(rColumn) >>= xColumn; + sal_Int32 nType = 0; + sal_Int32 nScale = 0; + sal_Int32 nPrecision = 0; + sal_Int32 nNullable = 0; + sal_Int32 nFormatKey = 0; + sal_Int32 nAlign = 0; + + bool bIsAutoIncrement = false, bIsCurrency = false; + OUString sName,sDescription,sTypeName,sHelpText; + Any aControlDefault; + + // get the properties from the column + xColumn->getPropertyValue(PROPERTY_NAME) >>= sName; + xColumn->getPropertyValue(PROPERTY_TYPENAME) >>= sTypeName; + xColumn->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullable; + xColumn->getPropertyValue(PROPERTY_ISAUTOINCREMENT) >>= bIsAutoIncrement; + xColumn->getPropertyValue(PROPERTY_ISCURRENCY) >>= bIsCurrency; + xColumn->getPropertyValue(PROPERTY_TYPE) >>= nType; + xColumn->getPropertyValue(PROPERTY_SCALE) >>= nScale; + xColumn->getPropertyValue(PROPERTY_PRECISION) >>= nPrecision; + xColumn->getPropertyValue(PROPERTY_DESCRIPTION) >>= sDescription; + + if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_HELPTEXT)) + xColumn->getPropertyValue(PROPERTY_HELPTEXT) >>= sHelpText; + + if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_CONTROLDEFAULT)) + aControlDefault = xColumn->getPropertyValue(PROPERTY_CONTROLDEFAULT); + if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_FORMATKEY)) + xColumn->getPropertyValue(PROPERTY_FORMATKEY) >>= nFormatKey; + if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_ALIGN)) + xColumn->getPropertyValue(PROPERTY_ALIGN) >>= nAlign; + + pTabEdRow = std::make_shared<OTableRow>(); + pTabEdRow->SetReadOnly(!bIsAlterAllowed); + // search for type + bool bForce; + OUString const sCreate("x"); + TOTypeInfoSP pTypeInfo = ::dbaui::getTypeInfoFromType(m_aTypeInfo,nType,sTypeName,sCreate,nPrecision,nScale,bIsAutoIncrement,bForce); + if ( !pTypeInfo ) + pTypeInfo = m_pTypeInfo; + pTabEdRow->SetFieldType( pTypeInfo, bForce ); + + OFieldDescription* pActFieldDescr = pTabEdRow->GetActFieldDescr(); + OSL_ENSURE(pActFieldDescr, "OTableController::loadData: invalid field description generated by the table row!"); + if ( pActFieldDescr ) + { + pActFieldDescr->SetName(sName); + pActFieldDescr->SetFormatKey(nFormatKey); + pActFieldDescr->SetDescription(sDescription); + pActFieldDescr->SetHelpText(sHelpText); + pActFieldDescr->SetAutoIncrement(bIsAutoIncrement); + pActFieldDescr->SetHorJustify(dbaui::mapTextJustify(nAlign)); + pActFieldDescr->SetCurrency(bIsCurrency); + + // special data + pActFieldDescr->SetIsNullable(nNullable); + pActFieldDescr->SetControlDefault(aControlDefault); + pActFieldDescr->SetPrecision(nPrecision); + pActFieldDescr->SetScale(nScale); + } + m_vRowList.push_back( pTabEdRow); + } + // fill the primary key information + Reference<XNameAccess> xKeyColumns = getKeyColumns(); + if(xKeyColumns.is()) + { + const Sequence<OUString> aKeyColumnNames = xKeyColumns->getElementNames(); + for(const OUString& rKeyColumn : aKeyColumnNames) + { + for(std::shared_ptr<OTableRow> const& pRow : m_vRowList) + { + if(pRow->GetActFieldDescr()->GetName() == rKeyColumn) + { + pRow->SetPrimaryKey(true); + break; + } + } + } + } + } + + // fill empty rows + + OTypeInfoMap::const_iterator aTypeIter = m_aTypeInfo.find(DataType::VARCHAR); + if(aTypeIter == m_aTypeInfo.end()) + aTypeIter = m_aTypeInfo.begin(); + + OSL_ENSURE(aTypeIter != m_aTypeInfo.end(),"We have no type information!"); + + bool bReadRow = !isAddAllowed(); + for(sal_Int32 i=m_vRowList.size(); i < NEWCOLS; i++ ) + { + pTabEdRow = std::make_shared<OTableRow>(); + pTabEdRow->SetReadOnly(bReadRow); + m_vRowList.push_back( pTabEdRow); + } +} + +Reference<XNameAccess> OTableController::getKeyColumns() const +{ + return getPrimaryKeyColumns_throw(m_xTable); +} + +bool OTableController::checkColumns(bool _bNew) +{ + bool bOk = true; + bool bFoundPKey = false; + Reference< XDatabaseMetaData > xMetaData = getMetaData( ); + DatabaseMetaData aMetaData( getConnection() ); + + ::comphelper::UStringMixEqual bCase(!xMetaData.is() || xMetaData->supportsMixedCaseQuotedIdentifiers()); + std::vector< std::shared_ptr<OTableRow> >::const_iterator aIter = m_vRowList.begin(); + std::vector< std::shared_ptr<OTableRow> >::const_iterator aEnd = m_vRowList.end(); + for(;aIter != aEnd;++aIter) + { + OFieldDescription* pFieldDesc = (*aIter)->GetActFieldDescr(); + if (pFieldDesc && !pFieldDesc->GetName().isEmpty()) + { + bFoundPKey |= (*aIter)->IsPrimaryKey(); + // first check for duplicate names + bool bDuplicateNameFound = std::any_of(aIter+1, aEnd, + [&bCase, &pFieldDesc](const std::shared_ptr<OTableRow>& rxRow) { + OFieldDescription* pCompareDesc = rxRow->GetActFieldDescr(); + return pCompareDesc && bCase(pCompareDesc->GetName(),pFieldDesc->GetName()); + }); + if (bDuplicateNameFound) + { + OUString strMessage = DBA_RES(STR_TABLEDESIGN_DUPLICATE_NAME); + strMessage = strMessage.replaceFirst("$column$", pFieldDesc->GetName()); + OSQLWarningBox aWarning(getFrameWeld(), strMessage); + aWarning.run(); + return false; + } + } + } + if ( _bNew && !bFoundPKey && aMetaData.supportsPrimaryKeys() ) + { + OUString sTitle(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY_HEAD)); + OUString sMsg(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY)); + OSQLMessageBox aBox(getFrameWeld(), sTitle,sMsg, MessBoxStyle::YesNoCancel | MessBoxStyle::DefaultYes); + + switch (aBox.run()) + { + case RET_YES: + { + auto pNewRow = std::make_shared<OTableRow>(); + TOTypeInfoSP pTypeInfo = ::dbaui::queryPrimaryKeyType(m_aTypeInfo); + if ( !pTypeInfo ) + break; + + pNewRow->SetFieldType( pTypeInfo ); + OFieldDescription* pActFieldDescr = pNewRow->GetActFieldDescr(); + + pActFieldDescr->SetAutoIncrement(false); + pActFieldDescr->SetIsNullable(ColumnValue::NO_NULLS); + + pActFieldDescr->SetName( createUniqueName("ID" )); + pActFieldDescr->SetPrimaryKey( true ); + m_vRowList.insert(m_vRowList.begin(),pNewRow); + + static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->Invalidate(); + static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->RowInserted(0); + } + break; + case RET_CANCEL: + bOk = false; + break; + } + } + return bOk; +} + +void OTableController::alterColumns() +{ + Reference<XColumnsSupplier> xColSup(m_xTable,UNO_QUERY_THROW); + + Reference<XNameAccess> xColumns = xColSup->getColumns(); + Reference<XIndexAccess> xIdxColumns(xColumns,UNO_QUERY_THROW); + OSL_ENSURE(xColumns.is(),"No columns"); + if ( !xColumns.is() ) + return; + Reference<XAlterTable> xAlter(m_xTable,UNO_QUERY); // can be null + + sal_Int32 nColumnCount = xIdxColumns->getCount(); + Reference<XDrop> xDrop(xColumns,UNO_QUERY); // can be null + Reference<XAppend> xAppend(xColumns,UNO_QUERY); // can be null + Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY); // can be null + + bool bReload = false; // refresh the data + + // contains all columns names which are already handled those which are not in the list will be deleted + Reference< XDatabaseMetaData> xMetaData = getMetaData( ); + + std::set<OUString, comphelper::UStringMixLess> aColumns( + comphelper::UStringMixLess( + !xMetaData.is() + || xMetaData->supportsMixedCaseQuotedIdentifiers())); + std::vector< std::shared_ptr<OTableRow> >::const_iterator aIter = m_vRowList.begin(); + std::vector< std::shared_ptr<OTableRow> >::const_iterator aEnd = m_vRowList.end(); + // first look for columns where something other than the name changed + for(sal_Int32 nPos = 0;aIter != aEnd;++aIter,++nPos) + { + OSL_ENSURE(*aIter,"OTableRow is null!"); + OFieldDescription* pField = (*aIter)->GetActFieldDescr(); + if ( !pField ) + continue; + if ( (*aIter)->IsReadOnly() ) + { + aColumns.insert(pField->GetName()); + continue; + } + + Reference<XPropertySet> xColumn; + if ( xColumns->hasByName(pField->GetName()) ) + { + aColumns.insert(pField->GetName()); + xColumns->getByName(pField->GetName()) >>= xColumn; + OSL_ENSURE(xColumn.is(),"Column is null!"); + + sal_Int32 nType=0,nPrecision=0,nScale=0,nNullable=0; + bool bAutoIncrement = false; + OUString sTypeName,sDescription; + + xColumn->getPropertyValue(PROPERTY_TYPE) >>= nType; + xColumn->getPropertyValue(PROPERTY_PRECISION) >>= nPrecision; + xColumn->getPropertyValue(PROPERTY_SCALE) >>= nScale; + xColumn->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullable; + xColumn->getPropertyValue(PROPERTY_ISAUTOINCREMENT) >>= bAutoIncrement; + xColumn->getPropertyValue(PROPERTY_DESCRIPTION) >>= sDescription; + + try { xColumn->getPropertyValue(PROPERTY_TYPENAME) >>= sTypeName; } + catch( const Exception& ) + { + OSL_FAIL( "no TypeName property?!" ); + // since this is a last minute fix for #i41785#, I want to be on the safe side, + // and catch errors here as early as possible (instead of the whole process of altering + // the columns failing) + // Normally, sdbcx::Column objects are expected to have a TypeName property + } + + // check if something changed + if((nType != pField->GetType() || + sTypeName != pField->GetTypeName() || + (nPrecision != pField->GetPrecision() && nPrecision ) || + nScale != pField->GetScale() || + nNullable != pField->GetIsNullable() || + sDescription != pField->GetDescription() || + bAutoIncrement != pField->IsAutoIncrement())&& + xColumnFactory.is()) + { + Reference<XPropertySet> xNewColumn = xColumnFactory->createDataDescriptor(); + ::dbaui::setColumnProperties(xNewColumn,pField); + // first try to alter the column + bool bNotOk = false; + try + { + // first try if we can alter the column + if(xAlter.is()) + xAlter->alterColumnByName(pField->GetName(),xNewColumn); + } + catch(const SQLException&) + { + if(xDrop.is() && xAppend.is()) + { + OUString aMessage( DBA_RES( STR_TABLEDESIGN_ALTER_ERROR ) ); + aMessage = aMessage.replaceFirst( "$column$", pField->GetName() ); + + SQLExceptionInfo aError( ::cppu::getCaughtException() ); + OSQLWarningBox aMsg(getFrameWeld(), aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes , &aError); + bNotOk = aMsg.run() == RET_YES; + } + else + throw; + } + // if something went wrong or we can't alter columns + // drop and append a new one + if((!xAlter.is() || bNotOk) && xDrop.is() && xAppend.is()) + { + xDrop->dropByName(pField->GetName()); + try + { + xAppend->appendByDescriptor(xNewColumn); + } + catch(const SQLException&) + { // an error occurred so we try to reactivate the old one + xAppend->appendByDescriptor(xColumn); + throw; + } + } + // exceptions are caught outside + xNewColumn = nullptr; + if(xColumns->hasByName(pField->GetName())) + xColumns->getByName(pField->GetName()) >>= xColumn; + bReload = true; + } + + } + else if(xColumnFactory.is() && xAlter.is() && nPos < nColumnCount) + { // we can't find the column so we could try it with the index before we drop and append a new column + try + { + Reference<XPropertySet> xNewColumn = xColumnFactory->createDataDescriptor(); + ::dbaui::setColumnProperties(xNewColumn,pField); + xAlter->alterColumnByIndex(nPos,xNewColumn); + if(xColumns->hasByName(pField->GetName())) + { // ask for the append by name + aColumns.insert(pField->GetName()); + xColumns->getByName(pField->GetName()) >>= xColumn; + if(xColumn.is()) + pField->copyColumnSettingsTo(xColumn); + } + else + { + OSL_FAIL("OTableController::alterColumns: invalid column (2)!"); + } + } + catch(const SQLException&) + { // we couldn't alter the column so we have to add new columns + SQLExceptionInfo aError( ::cppu::getCaughtException() ); + bReload = true; + if(xDrop.is() && xAppend.is()) + { + OUString aMessage(DBA_RES(STR_TABLEDESIGN_ALTER_ERROR)); + aMessage = aMessage.replaceFirst("$column$",pField->GetName()); + OSQLWarningBox aMsg(getFrameWeld(), aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes, &aError); + if (aMsg.run() != RET_YES) + { + Reference<XPropertySet> xNewColumn(xIdxColumns->getByIndex(nPos),UNO_QUERY_THROW); + OUString sName; + xNewColumn->getPropertyValue(PROPERTY_NAME) >>= sName; + aColumns.insert(sName); + aColumns.insert(pField->GetName()); + continue; + } + } + else + throw; + } + } + else + bReload = true; + } + // alter column settings + + // first look for columns where something other than the name changed + for (auto const& row : m_vRowList) + { + OSL_ENSURE(row,"OTableRow is null!"); + OFieldDescription* pField = row->GetActFieldDescr(); + if ( !pField ) + continue; + if ( row->IsReadOnly() ) + { + aColumns.insert(pField->GetName()); + continue; + } + + Reference<XPropertySet> xColumn; + if ( xColumns->hasByName(pField->GetName()) ) + { + xColumns->getByName(pField->GetName()) >>= xColumn; + Reference<XPropertySetInfo> xInfo = xColumn->getPropertySetInfo(); + if ( xInfo->hasPropertyByName(PROPERTY_HELPTEXT) ) + xColumn->setPropertyValue(PROPERTY_HELPTEXT,makeAny(pField->GetHelpText())); + + if(xInfo->hasPropertyByName(PROPERTY_CONTROLDEFAULT)) + xColumn->setPropertyValue(PROPERTY_CONTROLDEFAULT,pField->GetControlDefault()); + if(xInfo->hasPropertyByName(PROPERTY_FORMATKEY)) + xColumn->setPropertyValue(PROPERTY_FORMATKEY,makeAny(pField->GetFormatKey())); + if(xInfo->hasPropertyByName(PROPERTY_ALIGN)) + xColumn->setPropertyValue(PROPERTY_ALIGN,makeAny(dbaui::mapTextAllign(pField->GetHorJustify()))); + } + } + // second drop all columns which could be found by name + Reference<XNameAccess> xKeyColumns = getKeyColumns(); + // now we have to look for the columns who could be deleted + if ( xDrop.is() ) + { + const Sequence<OUString> aColNames = xColumns->getElementNames(); + for(const OUString& rColumnName : aColNames) + { + if(aColumns.find(rColumnName) == aColumns.end()) // found a column to delete + { + if(xKeyColumns.is() && xKeyColumns->hasByName(rColumnName)) // check if this column is a member of the primary key + { + OUString aMsgT(DBA_RES(STR_TBL_COLUMN_IS_KEYCOLUMN)); + aMsgT = aMsgT.replaceFirst("$column$",rColumnName); + OUString aTitle(DBA_RES(STR_TBL_COLUMN_IS_KEYCOLUMN_TITLE)); + OSQLMessageBox aMsg(getFrameWeld(), aTitle, aMsgT, MessBoxStyle::YesNo| MessBoxStyle::DefaultYes); + if (aMsg.run() == RET_YES) + { + xKeyColumns = nullptr; + dropPrimaryKey(); + } + else + { + bReload = true; + continue; + } + } + try + { + xDrop->dropByName(rColumnName); + } + catch (const SQLException&) + { + OUString sError( DBA_RES( STR_TABLEDESIGN_COULD_NOT_DROP_COL ) ); + sError = sError.replaceFirst( "$column$", rColumnName ); + + SQLException aNewException; + aNewException.Message = sError; + aNewException.SQLState = "S1000"; + aNewException.NextException = ::cppu::getCaughtException(); + + throw aNewException; + } + } + } + } + + // third append the new columns + for(const auto& rxRow : m_vRowList) + { + OSL_ENSURE(rxRow,"OTableRow is null!"); + OFieldDescription* pField = rxRow->GetActFieldDescr(); + if ( !pField || rxRow->IsReadOnly() || aColumns.find(pField->GetName()) != aColumns.end() ) + continue; + + Reference<XPropertySet> xColumn; + if(!xColumns->hasByName(pField->GetName())) + { + if(xColumnFactory.is() && xAppend.is()) + {// column not found by its name so we assume it is new + // Column is new + xColumn = xColumnFactory->createDataDescriptor(); + ::dbaui::setColumnProperties(xColumn,pField); + xAppend->appendByDescriptor(xColumn); + if(xColumns->hasByName(pField->GetName())) + { // ask for the append by name + aColumns.insert(pField->GetName()); + xColumns->getByName(pField->GetName()) >>= xColumn; + if(xColumn.is()) + pField->copyColumnSettingsTo(xColumn); + } + else + { + OSL_FAIL("OTableController::alterColumns: invalid column!"); + } + } + } + } + + // check if we have to do something with the primary key + bool bNeedDropKey = false; + bool bNeedAppendKey = false; + if ( xKeyColumns.is() ) + { + for(const auto& rxRow : m_vRowList) + { + OSL_ENSURE(rxRow,"OTableRow is null!"); + OFieldDescription* pField = rxRow->GetActFieldDescr(); + if ( !pField ) + continue; + + if ( pField->IsPrimaryKey() + && !xKeyColumns->hasByName( pField->GetName() ) + ) + { // new primary key column inserted which isn't already in the columns selection + bNeedDropKey = bNeedAppendKey = true; + break; + } + else if ( !pField->IsPrimaryKey() + && xKeyColumns->hasByName( pField->GetName() ) + ) + { // found a column which currently is in the primary key, but is marked not to be anymore + bNeedDropKey = bNeedAppendKey = true; + break; + } + } + } + else + { // no primary key available so we check if we should create one + bNeedAppendKey = true; + } + + if ( bNeedDropKey && xKeyColumns.is() && xKeyColumns->getElementNames().hasElements() ) + dropPrimaryKey(); + + if ( bNeedAppendKey ) + { + Reference< XKeysSupplier > xKeySup( m_xTable, UNO_QUERY ); + appendPrimaryKey( xKeySup ,false); + } + + reSyncRows(); + + if ( bReload ) + reload(); +} + +void OTableController::dropPrimaryKey() +{ + SQLExceptionInfo aInfo; + try + { + Reference<XKeysSupplier> xKeySup(m_xTable,UNO_QUERY); + Reference<XIndexAccess> xKeys; + if(xKeySup.is()) + xKeys = xKeySup->getKeys(); + + if(xKeys.is()) + { + Reference<XPropertySet> xProp; + for(sal_Int32 i=0;i< xKeys->getCount();++i) + { + xProp.set(xKeys->getByIndex(i),UNO_QUERY); + sal_Int32 nKeyType = 0; + xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType; + if(KeyType::PRIMARY == nKeyType) + { + Reference<XDrop> xDrop(xKeys,UNO_QUERY); + xDrop->dropByIndex(i); // delete the key + break; + } + } + } + } + catch(const SQLContext& e) + { + aInfo = SQLExceptionInfo(e); + } + catch(const SQLWarning& e) + { + aInfo = SQLExceptionInfo(e); + } + catch(const SQLException& e) + { + aInfo = SQLExceptionInfo(e); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + showError(aInfo); +} + +void OTableController::assignTable() +{ + // get the table + if(m_sName.isEmpty()) + return; + + Reference<XNameAccess> xNameAccess; + Reference<XTablesSupplier> xSup(getConnection(),UNO_QUERY); + if(!xSup.is()) + return; + + xNameAccess = xSup->getTables(); + OSL_ENSURE(xNameAccess.is(),"no nameaccess for the queries!"); + + if(!xNameAccess->hasByName(m_sName)) + return; + + Reference<XPropertySet> xProp(xNameAccess->getByName(m_sName), css::uno::UNO_QUERY); + if (!xProp.is()) + return; + + m_xTable = xProp; + startTableListening(); + + // check if we set the table editable + Reference<XDatabaseMetaData> xMeta = getConnection()->getMetaData(); + setEditable( xMeta.is() && !xMeta->isReadOnly() && (isAlterAllowed() || isDropAllowed() || isAddAllowed()) ); + if(!isEditable()) + { + for( const auto& rTableRow : m_vRowList ) + { + rTableRow->SetReadOnly(); + } + } + m_bNew = false; + // be notified when the table is in disposing + InvalidateAll(); +} + +bool OTableController::isAddAllowed() const +{ + Reference<XColumnsSupplier> xColsSup(m_xTable,UNO_QUERY); + bool bAddAllowed = !m_xTable.is(); + if(xColsSup.is()) + bAddAllowed = Reference<XAppend>(xColsSup->getColumns(),UNO_QUERY).is(); + + try + { + Reference< XDatabaseMetaData > xMetaData = getMetaData( ); + bAddAllowed = bAddAllowed || ( xMetaData.is() && xMetaData->supportsAlterTableWithAddColumn()); + } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + bAddAllowed = false; + } + + return bAddAllowed; +} + +bool OTableController::isDropAllowed() const +{ + Reference<XColumnsSupplier> xColsSup(m_xTable,UNO_QUERY); + bool bDropAllowed = !m_xTable.is(); + if(xColsSup.is()) + { + Reference<XNameAccess> xNameAccess = xColsSup->getColumns(); + bDropAllowed = Reference<XDrop>(xNameAccess,UNO_QUERY).is() && xNameAccess->hasElements(); + } + + Reference< XDatabaseMetaData> xMetaData = getMetaData( ); + bDropAllowed = bDropAllowed || ( xMetaData.is() && xMetaData->supportsAlterTableWithDropColumn()); + + return bDropAllowed; +} + +bool OTableController::isAlterAllowed() const +{ + bool bAllowed(!m_xTable.is() || Reference<XAlterTable>(m_xTable,UNO_QUERY).is()); + return bAllowed; +} + +void OTableController::reSyncRows() +{ + bool bAlterAllowed = isAlterAllowed(); + bool bAddAllowed = isAddAllowed(); + for (auto const& row : m_vRowList) + { + OSL_ENSURE(row,"OTableRow is null!"); + OFieldDescription* pField = row->GetActFieldDescr(); + if ( pField ) + row->SetReadOnly(!bAlterAllowed); + else + row->SetReadOnly(!bAddAllowed); + + } + static_cast<OTableDesignView*>(getView())->reSync(); // show the windows and fill with our information + + ClearUndoManager(); + setModified(false); // and we are not modified yet +} + +OUString OTableController::createUniqueName(const OUString& _rName) +{ + OUString sName = _rName; + Reference< XDatabaseMetaData> xMetaData = getMetaData( ); + + ::comphelper::UStringMixEqual bCase(!xMetaData.is() || xMetaData->supportsMixedCaseQuotedIdentifiers()); + + auto lHasName = [&bCase, &sName](const std::shared_ptr<OTableRow>& rxRow) { + OFieldDescription* pFieldDesc = rxRow->GetActFieldDescr(); + return pFieldDesc && !pFieldDesc->GetName().isEmpty() && bCase(sName, pFieldDesc->GetName()); + }; + + sal_Int32 i = 0; + while(std::any_of(m_vRowList.begin(), m_vRowList.end(), lHasName)) + { + // found a second name of _rName so we need another + sName = _rName + OUString::number(++i); + } + return sName; +} + +OUString OTableController::getPrivateTitle() const +{ + OUString sTitle; + try + { + // get the table + if ( !m_sName.isEmpty() && getConnection().is() ) + { + if ( m_xTable.is() ) + sTitle = ::dbtools::composeTableName( getConnection()->getMetaData(), m_xTable, ::dbtools::EComposeRule::InDataManipulation, false ); + else + sTitle = m_sName; + } + if ( sTitle.isEmpty() ) + { + OUString aName = DBA_RES(STR_TBL_TITLE); + sTitle = aName.getToken(0,' ') + OUString::number(getCurrentStartNumber()); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return sTitle; +} + +void OTableController::reload() +{ + loadData(); // fill the column information from the table + static_cast<OTableDesignView*>(getView())->reSync(); // show the windows and fill with our information + ClearUndoManager(); + setModified(false); // and we are not modified yet + static_cast<OTableDesignView*>(getView())->Invalidate(); +} + +sal_Int32 OTableController::getFirstEmptyRowPosition() +{ + sal_Int32 nRet = 0; + bool bFoundElem = false; + for (auto const& row : m_vRowList) + { + if ( !row || !row->GetActFieldDescr() || row->GetActFieldDescr()->GetName().isEmpty() ) + { + bFoundElem = true; + break; + } + ++nRet; + } + if (!bFoundElem) + { + bool bReadRow = !isAddAllowed(); + auto pTabEdRow = std::make_shared<OTableRow>(); + pTabEdRow->SetReadOnly(bReadRow); + nRet = m_vRowList.size(); + m_vRowList.push_back( pTabEdRow); + } + return nRet; +} + +bool OTableController::isAutoIncrementPrimaryKey() const +{ + return getSdbMetaData().isAutoIncrementPrimaryKey(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |