xref: /core/dbaccess/source/ui/tabledesign/TableController.cxx (revision 79c3a093854ac49f4c5e1b7d0797cd7dca5dad66)
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 <FieldDescriptions.hxx>
21 #include "TEditControl.hxx"
22 #include <TableController.hxx>
23 #include <TableDesignView.hxx>
24 #include <TableRow.hxx>
25 #include <TypeInfo.hxx>
26 #include <UITools.hxx>
27 #include <browserids.hxx>
28 #include <core_resource.hxx>
29 #include <strings.hrc>
30 #include <strings.hxx>
31 #include <defaultobjectnamecheck.hxx>
32 #include <dlgsave.hxx>
33 #include <indexdialog.hxx>
34 #include <sqlmessage.hxx>
35 
36 #include <com/sun/star/frame/XTitleChangeListener.hpp>
37 #include <com/sun/star/sdb/CommandType.hpp>
38 #include <com/sun/star/sdb/SQLContext.hpp>
39 #include <com/sun/star/sdbc/ColumnValue.hpp>
40 #include <com/sun/star/sdbc/SQLWarning.hpp>
41 #include <com/sun/star/sdbcx/KeyType.hpp>
42 #include <com/sun/star/sdbcx/XAlterTable.hpp>
43 #include <com/sun/star/sdbcx/XAppend.hpp>
44 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
45 #include <com/sun/star/sdbcx/XDrop.hpp>
46 #include <com/sun/star/sdbcx/XIndexesSupplier.hpp>
47 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
48 
49 #include <connectivity/dbexception.hxx>
50 #include <connectivity/dbtools.hxx>
51 #include <connectivity/dbmetadata.hxx>
52 #include <cppuhelper/exc_hlp.hxx>
53 #include <comphelper/diagnose_ex.hxx>
54 #include <vcl/svapp.hxx>
55 #include <vcl/weld.hxx>
56 #include <o3tl/string_view.hxx>
57 
58 #include <algorithm>
59 #include <functional>
60 #include <set>
61 
62 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
org_openoffice_comp_dbu_OTableDesign_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)63 org_openoffice_comp_dbu_OTableDesign_get_implementation(
64     css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
65 {
66     return cppu::acquire(new ::dbaui::OTableController(context));
67 }
68 
69 using namespace ::com::sun::star;
70 using namespace ::com::sun::star::uno;
71 using namespace ::com::sun::star::beans;
72 using namespace ::com::sun::star::frame;
73 using namespace ::com::sun::star::lang;
74 using namespace ::com::sun::star::container;
75 using namespace ::com::sun::star::sdbcx;
76 using namespace ::com::sun::star::sdbc;
77 using namespace ::com::sun::star::sdb;
78 using namespace ::com::sun::star::util;
79 using namespace ::dbtools;
80 using namespace ::dbaui;
81 using namespace ::comphelper;
82 
83 // number of columns when creating it from scratch
84 #define NEWCOLS        128
85 
86 namespace
87 {
dropTable(const Reference<XNameAccess> & _rxTable,const OUString & _sTableName)88     void dropTable(const Reference<XNameAccess>& _rxTable,const OUString& _sTableName)
89     {
90         if ( _rxTable->hasByName(_sTableName) )
91         {
92             Reference<XDrop> xNameCont(_rxTable,UNO_QUERY);
93             OSL_ENSURE(xNameCont.is(),"No drop interface for tables!");
94             if ( xNameCont.is() )
95                 xNameCont->dropByName(_sTableName);
96         }
97     }
98 }
99 
getImplementationName()100 OUString SAL_CALL OTableController::getImplementationName()
101 {
102     return u"org.openoffice.comp.dbu.OTableDesign"_ustr;
103 }
104 
getSupportedServiceNames()105 Sequence< OUString> OTableController::getSupportedServiceNames()
106 {
107     return { u"com.sun.star.sdb.TableDesign"_ustr };
108 }
109 
OTableController(const Reference<XComponentContext> & _rM)110 OTableController::OTableController(const Reference< XComponentContext >& _rM) : OTableController_BASE(_rM)
111     ,m_sTypeNames(DBA_RES(STR_TABLEDESIGN_DBFIELDTYPES))
112     ,m_bAllowAutoIncrementValue(false)
113     ,m_bNew(true)
114 {
115 
116     InvalidateAll();
117     m_pTypeInfo = std::make_shared<OTypeInfo>();
118     m_pTypeInfo->aUIName = m_sTypeNames.getToken(TYPE_OTHER, ';');
119 }
120 
~OTableController()121 OTableController::~OTableController()
122 {
123     m_aTypeInfoIndex.clear();
124     m_aTypeInfo.clear();
125 
126 }
127 
startTableListening()128 void OTableController::startTableListening()
129 {
130     Reference< XComponent >  xComponent(m_xTable, UNO_QUERY);
131     if (xComponent.is())
132         xComponent->addEventListener(static_cast<XModifyListener*>(this));
133 }
134 
stopTableListening()135 void OTableController::stopTableListening()
136 {
137     Reference< XComponent >  xComponent(m_xTable, UNO_QUERY);
138     if (xComponent.is())
139         xComponent->removeEventListener(static_cast<XModifyListener*>(this));
140 }
141 
disposing()142 void OTableController::disposing()
143 {
144     OTableController_BASE::disposing();
145     clearView();
146 
147     m_vRowList.clear();
148 }
149 
GetState(sal_uInt16 _nId) const150 FeatureState OTableController::GetState(sal_uInt16 _nId) const
151 {
152     FeatureState aReturn;
153     // disabled automatically
154 
155     switch (_nId)
156     {
157         case ID_BROWSER_CLOSE:
158             aReturn.bEnabled = true;
159             break;
160         case ID_BROWSER_EDITDOC:
161             aReturn.bChecked = isEditable();
162             aReturn.bEnabled = true;
163             break;
164         case ID_BROWSER_SAVEDOC:
165             aReturn.bEnabled = isEditable() && std::any_of(m_vRowList.begin(),m_vRowList.end(),std::mem_fn(&OTableRow::isValid));
166             break;
167         case ID_BROWSER_SAVEASDOC:
168             aReturn.bEnabled = isConnected() && isEditable();
169             if ( aReturn.bEnabled )
170             {
171                 aReturn.bEnabled = std::any_of(m_vRowList.begin(),m_vRowList.end(),
172                                                  std::mem_fn(&OTableRow::isValid));
173             }
174             break;
175 
176         case ID_BROWSER_CUT:
177             aReturn.bEnabled = isEditable() && getView() && static_cast<OTableDesignView*>(getView())->isCutAllowed();
178             break;
179         case ID_BROWSER_COPY:
180             aReturn.bEnabled = getView() && static_cast<OTableDesignView*>(getView())->isCopyAllowed();
181             break;
182         case ID_BROWSER_PASTE:
183             aReturn.bEnabled = isEditable() && getView() && static_cast<OTableDesignView*>(getView())->isPasteAllowed();
184             break;
185         case SID_INDEXDESIGN:
186             aReturn.bEnabled =
187                 (   (   ((!m_bNew && impl_isModified()) || impl_isModified())
188                     ||  Reference< XIndexesSupplier >(m_xTable, UNO_QUERY).is()
189                     )
190                 &&  isConnected()
191                 );
192             if ( aReturn.bEnabled )
193             {
194                 aReturn.bEnabled = std::any_of(m_vRowList.begin(),m_vRowList.end(),
195                                                  std::mem_fn(&OTableRow::isValid));
196             }
197             break;
198         default:
199             aReturn = OTableController_BASE::GetState(_nId);
200     }
201     return aReturn;
202 }
203 
Execute(sal_uInt16 _nId,const Sequence<PropertyValue> & aArgs)204 void OTableController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs)
205 {
206     switch(_nId)
207     {
208         case ID_BROWSER_EDITDOC:
209             setEditable(!isEditable());
210             static_cast<OTableDesignView*>(getView())->setReadOnly(!isEditable());
211             InvalidateFeature(ID_BROWSER_SAVEDOC);
212             InvalidateFeature(ID_BROWSER_PASTE);
213             InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
214             break;
215         case ID_BROWSER_SAVEASDOC:
216             doSaveDoc(true);
217             break;
218         case ID_BROWSER_SAVEDOC:
219             static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->SaveCurRow();
220             doSaveDoc(false);
221             break;
222         case ID_BROWSER_CUT:
223             static_cast<OTableDesignView*>(getView())->cut();
224             break;
225         case ID_BROWSER_COPY:
226             static_cast<OTableDesignView*>(getView())->copy();
227             break;
228         case ID_BROWSER_PASTE:
229             static_cast<OTableDesignView*>(getView())->paste();
230             break;
231         case SID_INDEXDESIGN:
232             doEditIndexes();
233             break;
234         default:
235             OTableController_BASE::Execute(_nId,aArgs);
236     }
237     InvalidateFeature(_nId);
238 }
239 
doSaveDoc(bool _bSaveAs)240 bool OTableController::doSaveDoc(bool _bSaveAs)
241 {
242     if (!isConnected())
243         reconnect(true); // ask the user for a new connection
244     Reference<XTablesSupplier> xTablesSup(getConnection(),UNO_QUERY);
245 
246     if (!xTablesSup.is())
247     {
248         OUString aMessage(DBA_RES(STR_TABLEDESIGN_CONNECTION_MISSING));
249         OSQLWarningBox aWarning(getFrameWeld(), aMessage);
250         aWarning.run();
251         return false;
252     }
253 
254     // check if a column exists
255     // TODO
256 
257     Reference<XNameAccess> xTables;
258     OUString sCatalog, sSchema;
259 
260     bool bNew = m_sName.isEmpty();
261     bNew = bNew || m_bNew || _bSaveAs;
262 
263     try
264     {
265         xTables = xTablesSup->getTables();
266         OSL_ENSURE(xTables.is(),"The tables can't be null!");
267         bNew = bNew || (xTables.is() && !xTables->hasByName(m_sName));
268 
269         // first we need a name for our query so ask the user
270         if(bNew)
271         {
272             OUString aName = DBA_RES(STR_TBL_TITLE);
273             OUString aDefaultName = aName.getToken(0,' ');
274             aDefaultName = ::dbtools::createUniqueName(xTables,aDefaultName);
275 
276             DynamicTableOrQueryNameCheck aNameChecker( getConnection(), CommandType::TABLE );
277             OSaveAsDlg aDlg(getFrameWeld(), CommandType::TABLE, getORB(), getConnection(), aDefaultName, aNameChecker, SADFlags::NONE);
278             if (aDlg.run() != RET_OK)
279                 return false;
280 
281             m_sName = aDlg.getName();
282             sCatalog = aDlg.getCatalog();
283             sSchema  = aDlg.getSchema();
284         }
285 
286         // did we get a name
287         if(m_sName.isEmpty())
288             return false;
289     }
290     catch(Exception&)
291     {
292         OSL_FAIL("OTableController::doSaveDoc: nothing is expected to happen here!");
293     }
294 
295     bool bAlter = false;
296     bool bError = false;
297     SQLExceptionInfo aInfo;
298     try
299     {
300         // check the columns for double names
301         if(!checkColumns(bNew || !xTables->hasByName(m_sName)))
302         {
303             return false;
304         }
305 
306         Reference<XPropertySet> xTable;
307         if(bNew || !xTables->hasByName(m_sName)) // just to make sure the table already exists
308         {
309             dropTable(xTables,m_sName);
310 
311             Reference<XDataDescriptorFactory> xFact(xTables,UNO_QUERY);
312             OSL_ENSURE(xFact.is(),"OTableController::doSaveDoc: No XDataDescriptorFactory available!");
313             xTable = xFact->createDataDescriptor();
314             OSL_ENSURE(xTable.is(),"OTableController::doSaveDoc: Create query failed!");
315             // to set the name is only allowed when the query is new
316             xTable->setPropertyValue(PROPERTY_CATALOGNAME,Any(sCatalog));
317             xTable->setPropertyValue(PROPERTY_SCHEMANAME,Any(sSchema));
318             xTable->setPropertyValue(PROPERTY_NAME,Any(m_sName));
319 
320             // now append the columns
321             Reference<XColumnsSupplier> xColSup(xTable,UNO_QUERY);
322             appendColumns(xColSup,bNew);
323             // now append the primary key
324             Reference<XKeysSupplier> xKeySup(xTable,UNO_QUERY);
325             appendPrimaryKey(xKeySup,bNew);
326         }
327         // now set the properties
328         if(bNew)
329         {
330             Reference<XAppend> xAppend(xTables,UNO_QUERY);
331             OSL_ENSURE(xAppend.is(),"OTableController::doSaveDoc: No XAppend Interface!");
332             xAppend->appendByDescriptor(xTable);
333 
334             assignTable();
335             if(!m_xTable.is()) // correct name and try again
336             {
337                 // it can be that someone inserted new data for us
338                 m_sName = ::dbtools::composeTableName( getConnection()->getMetaData(), xTable, ::dbtools::EComposeRule::InDataManipulation, false );
339                 assignTable();
340             }
341             // now check if our datasource has set a tablefilter and if append the new table name to it
342             ::dbaui::appendToFilter(getConnection(), m_sName, getORB(), getFrameWeld()); // we are not interested in the return value
343             Reference< frame::XTitleChangeListener> xEventListener(impl_getTitleHelper_throw(),UNO_QUERY);
344             if ( xEventListener.is() )
345             {
346                 frame::TitleChangedEvent aEvent;
347                 xEventListener->titleChanged(aEvent);
348             }
349             releaseNumberForComponent();
350         }
351         else if(m_xTable.is())
352         {
353             bAlter = true;
354             alterColumns();
355         }
356         reSyncRows();
357     }
358     catch(const SQLContext& e)
359     {
360         aInfo = SQLExceptionInfo(e);
361     }
362     catch(const SQLWarning& e)
363     {
364         aInfo = SQLExceptionInfo(e);
365     }
366     catch(const SQLException& e)
367     {
368         aInfo = SQLExceptionInfo(e);
369     }
370     catch(const ElementExistException& )
371     {
372         OUString sText( DBA_RES( STR_NAME_ALREADY_EXISTS ) );
373         sText = sText.replaceFirst( "#" , m_sName);
374         OSQLMessageBox aDlg(getFrameWeld(), DBA_RES( STR_ERROR_DURING_CREATION ), sText, MessBoxStyle::Ok, MessageType::Error);
375         aDlg.run();
376         bError = true;
377     }
378     catch( const Exception& )
379     {
380         DBG_UNHANDLED_EXCEPTION("dbaccess");
381         bError = true;
382     }
383 
384     if ( aInfo.isValid() )
385         aInfo.prepend( DBA_RES( STR_TABLEDESIGN_SAVE_ERROR ) );
386     showError(aInfo);
387 
388     if (aInfo.isValid() || bError)
389     {
390         if(!bAlter || bNew)
391         {
392             m_sName.clear();
393             stopTableListening();
394             m_xTable = nullptr;
395         }
396     }
397     return ! (aInfo.isValid() || bError);
398 }
399 
doEditIndexes()400 void OTableController::doEditIndexes()
401 {
402     // table needs to be saved before editing indexes
403     if (m_bNew || isModified())
404     {
405         std::unique_ptr<weld::MessageDialog> xAsk(Application::CreateMessageDialog(getFrameWeld(),
406                                                   VclMessageType::Question, VclButtonsType::YesNo,
407                                                   DBA_RES(STR_QUERY_SAVE_TABLE_EDIT_INDEXES)));
408         if (RET_YES != xAsk->run())
409             return;
410 
411         if (!doSaveDoc(false))
412             return;
413 
414         OSL_ENSURE(!m_bNew && !isModified(), "OTableController::doEditIndexes: what the hell did doSaveDoc do?");
415     }
416 
417     Reference< XNameAccess > xIndexes;          // will be the keys of the table
418     Sequence< OUString > aFieldNames;    // will be the column names of the table
419     try
420     {
421         // get the keys
422         Reference< XIndexesSupplier > xIndexesSupp(m_xTable, UNO_QUERY);
423         if (xIndexesSupp.is())
424         {
425             xIndexes = xIndexesSupp->getIndexes();
426             OSL_ENSURE(xIndexes.is(), "OTableController::doEditIndexes: no keys got from the indexes supplier!");
427         }
428         else
429             OSL_FAIL("OTableController::doEditIndexes: should never have reached this (no indexes supplier)!");
430 
431         // get the field names
432         Reference< XColumnsSupplier > xColSupp(m_xTable, UNO_QUERY);
433         OSL_ENSURE(xColSupp.is(), "OTableController::doEditIndexes: no columns supplier!");
434         if (xColSupp.is())
435         {
436             Reference< XNameAccess > xCols = xColSupp->getColumns();
437             OSL_ENSURE(xCols.is(), "OTableController::doEditIndexes: no columns!");
438             if (xCols.is())
439                 aFieldNames = xCols->getElementNames();
440         }
441     }
442     catch( const Exception& )
443     {
444         DBG_UNHANDLED_EXCEPTION("dbaccess");
445     }
446 
447     if (!xIndexes.is())
448         return;
449 
450     DbaIndexDialog aDialog(getFrameWeld(), aFieldNames, xIndexes, getConnection(), getORB());
451     if (RET_OK != aDialog.run())
452         return;
453 
454 }
455 
impl_initialize(const::comphelper::NamedValueCollection & rArguments)456 void OTableController::impl_initialize(const ::comphelper::NamedValueCollection& rArguments)
457 {
458     try
459     {
460         OTableController_BASE::impl_initialize(rArguments);
461 
462         rArguments.get_ensureType( PROPERTY_CURRENTTABLE, m_sName );
463 
464         // read autoincrement value set in the datasource
465         ::dbaui::fillAutoIncrementValue(getDataSource(),m_bAllowAutoIncrementValue,m_sAutoIncrementValue);
466 
467         assignTable();
468     }
469     catch( const Exception& )
470     {
471         DBG_UNHANDLED_EXCEPTION("dbaccess");
472     }
473 
474     try
475     {
476         ::dbaui::fillTypeInfo(getConnection(),m_sTypeNames,m_aTypeInfo,m_aTypeInfoIndex);               // fill the needed type information
477     }
478     catch(const SQLException&)
479     {
480         OSQLWarningBox aWarning(getFrameWeld(), DBA_RES( STR_NO_TYPE_INFO_AVAILABLE));
481         aWarning.run();
482         throw;
483     }
484     try
485     {
486         loadData();                 // fill the column information from the table
487         getView()->initialize();    // show the windows and fill with our information
488         ClearUndoManager();
489         setModified(false);     // and we are not modified yet
490     }
491     catch( const Exception& )
492     {
493         DBG_UNHANDLED_EXCEPTION("dbaccess");
494     }
495 }
496 
Construct(vcl::Window * pParent)497 bool OTableController::Construct(vcl::Window* pParent)
498 {
499     setView( VclPtr<OTableDesignView>::Create( pParent, getORB(), *this ) );
500     OTableController_BASE::Construct(pParent);
501     return true;
502 }
503 
suspend(sal_Bool)504 sal_Bool SAL_CALL OTableController::suspend(sal_Bool /*_bSuspend*/)
505 {
506     if ( getBroadcastHelper().bInDispose || getBroadcastHelper().bDisposed )
507         return true;
508 
509     SolarMutexGuard aSolarGuard;
510     ::osl::MutexGuard aGuard( getMutex() );
511     if ( getView() && getView()->IsInModalMode() )
512         return false;
513     if ( getView() )
514         static_cast<OTableDesignView*>(getView())->GrabFocus();
515     bool bCheck = true;
516     if ( isModified() )
517     {
518         if ( std::any_of(m_vRowList.begin(),m_vRowList.end(),
519                            std::mem_fn(&OTableRow::isValid)) )
520         {
521             std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), u"dbaccess/ui/tabledesignsavemodifieddialog.ui"_ustr));
522             std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog(u"TableDesignSaveModifiedDialog"_ustr));
523             switch (xQuery->run())
524             {
525                 case RET_YES:
526                     Execute(ID_BROWSER_SAVEDOC,Sequence<PropertyValue>());
527                     if ( isModified() )
528                         bCheck = false; // when we save the table this must be false else some press cancel
529                     break;
530                 case RET_CANCEL:
531                     bCheck = false;
532                     break;
533                 default:
534                     break;
535             }
536         }
537         else if ( !m_bNew )
538         {
539             std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), u"dbaccess/ui/deleteallrowsdialog.ui"_ustr));
540             std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog(u"DeleteAllRowsDialog"_ustr));
541             switch (xQuery->run())
542             {
543                 case RET_YES:
544                     {
545                         try
546                         {
547                             Reference<XTablesSupplier> xTablesSup(getConnection(),UNO_QUERY);
548                             Reference<XNameAccess> xTables = xTablesSup->getTables();
549                             dropTable(xTables,m_sName);
550                         }
551                         catch(const Exception&)
552                         {
553                             OSL_FAIL("OTableController::suspend: nothing is expected to happen here!");
554                         }
555 
556                     }
557                     break;
558                 case RET_CANCEL:
559                     bCheck = false;
560                     break;
561                 default:
562                     break;
563             }
564         }
565     }
566 
567     return bCheck;
568 }
569 
describeSupportedFeatures()570 void OTableController::describeSupportedFeatures()
571 {
572     OSingleDocumentController::describeSupportedFeatures();
573 
574     implDescribeSupportedFeature( u".uno:Redo"_ustr,          ID_BROWSER_REDO,        CommandGroup::EDIT );
575     implDescribeSupportedFeature( u".uno:Save"_ustr,          ID_BROWSER_SAVEDOC,     CommandGroup::EDIT );
576     implDescribeSupportedFeature( u".uno:Undo"_ustr,          ID_BROWSER_UNDO,        CommandGroup::EDIT );
577     implDescribeSupportedFeature( u".uno:NewDoc"_ustr,        SID_NEWDOC,             CommandGroup::DOCUMENT );
578     implDescribeSupportedFeature( u".uno:SaveAs"_ustr,        ID_BROWSER_SAVEASDOC,   CommandGroup::DOCUMENT );
579     implDescribeSupportedFeature( u".uno:DBIndexDesign"_ustr, SID_INDEXDESIGN,        CommandGroup::APPLICATION );
580     implDescribeSupportedFeature( u".uno:EditDoc"_ustr,       ID_BROWSER_EDITDOC,     CommandGroup::EDIT );
581     implDescribeSupportedFeature( u".uno:GetUndoStrings"_ustr, SID_GETUNDOSTRINGS );
582     implDescribeSupportedFeature( u".uno:GetRedoStrings"_ustr, SID_GETREDOSTRINGS );
583 }
584 
impl_onModifyChanged()585 void OTableController::impl_onModifyChanged()
586 {
587     OSingleDocumentController::impl_onModifyChanged();
588     InvalidateFeature( SID_INDEXDESIGN );
589 }
590 
disposing(const EventObject & _rSource)591 void SAL_CALL OTableController::disposing( const EventObject& _rSource )
592 {
593     if ( _rSource.Source == m_xTable )
594     {   // some deleted our table so we have a new one
595         stopTableListening();
596         m_xTable    = nullptr;
597         m_bNew      = true;
598         setModified(true);
599     }
600     else
601         OTableController_BASE::disposing( _rSource );
602 }
603 
losingConnection()604 void OTableController::losingConnection( )
605 {
606     // let the base class do its reconnect
607     OTableController_BASE::losingConnection( );
608 
609     // remove from the table
610     Reference< XComponent >  xComponent(m_xTable, UNO_QUERY);
611     if (xComponent.is())
612     {
613         Reference<XEventListener> xEvtL( static_cast< ::cppu::OWeakObject*>(this), UNO_QUERY);
614         xComponent->removeEventListener(xEvtL);
615     }
616     stopTableListening();
617     m_xTable    = nullptr;
618     assignTable();
619     if(!m_xTable.is())
620     {
621         m_bNew      = true;
622         setModified(true);
623     }
624     InvalidateAll();
625 }
626 
getTypeInfoByType(sal_Int32 _nDataType) const627 TOTypeInfoSP OTableController::getTypeInfoByType(sal_Int32 _nDataType) const
628 {
629     return queryTypeInfoByType(_nDataType,m_aTypeInfo);
630 }
631 
appendColumns(Reference<XColumnsSupplier> const & _rxColSup,bool _bNew,bool _bKeyColumns)632 void OTableController::appendColumns(Reference<XColumnsSupplier> const & _rxColSup, bool _bNew, bool _bKeyColumns)
633 {
634     try
635     {
636         // now append the columns
637         OSL_ENSURE(_rxColSup.is(),"No columns supplier");
638         if(!_rxColSup.is())
639             return;
640         Reference<XNameAccess> xColumns = _rxColSup->getColumns();
641         OSL_ENSURE(xColumns.is(),"No columns");
642         Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY);
643 
644         Reference<XAppend> xAppend(xColumns,UNO_QUERY);
645         OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
646 
647         for (auto const& row : m_vRowList)
648         {
649             OSL_ENSURE(row,"OTableRow is null!");
650             OFieldDescription* pField = row->GetActFieldDescr();
651             if ( !pField || (!_bNew && row->IsReadOnly() && !_bKeyColumns) )
652                 continue;
653 
654             Reference<XPropertySet> xColumn;
655             if(pField->IsPrimaryKey() || !_bKeyColumns)
656                 xColumn = xColumnFactory->createDataDescriptor();
657             if(xColumn.is())
658             {
659                 if(!_bKeyColumns)
660                     ::dbaui::setColumnProperties(xColumn,pField);
661                 else
662                     xColumn->setPropertyValue(PROPERTY_NAME,Any(pField->GetName()));
663 
664                 xAppend->appendByDescriptor(xColumn);
665                 xColumn = nullptr;
666                 // now only the settings are missing
667                 if(xColumns->hasByName(pField->GetName()))
668                 {
669                     xColumns->getByName(pField->GetName()) >>= xColumn;
670                     if(xColumn.is())
671                         pField->copyColumnSettingsTo(xColumn);
672                 }
673                 else
674                 {
675                     OSL_FAIL("OTableController::appendColumns: invalid field name!");
676                 }
677 
678             }
679         }
680     }
681     catch(const SQLException& )
682     {
683         showError( SQLExceptionInfo( ::cppu::getCaughtException() ) );
684     }
685     catch( const Exception& )
686     {
687         DBG_UNHANDLED_EXCEPTION("dbaccess");
688     }
689 }
690 
appendPrimaryKey(Reference<XKeysSupplier> const & _rxSup,bool _bNew)691 void OTableController::appendPrimaryKey(Reference<XKeysSupplier> const & _rxSup, bool _bNew)
692 {
693     if(!_rxSup.is())
694         return; // the database doesn't support keys
695 
696     OSL_ENSURE(_rxSup.is(),"No XKeysSupplier!");
697     Reference<XIndexAccess> xKeys = _rxSup->getKeys();
698     Reference<XPropertySet> xProp;
699     if (!xKeys.is())
700         return;
701     const sal_Int32 nCount = xKeys->getCount();
702     for(sal_Int32 i=0;i< nCount ;++i)
703     {
704         xKeys->getByIndex(i) >>= xProp;
705         sal_Int32 nKeyType = 0;
706         xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
707         if(KeyType::PRIMARY == nKeyType)
708         {
709             return; // primary key already exists after appending a column
710         }
711     }
712     Reference<XDataDescriptorFactory> xKeyFactory(xKeys,UNO_QUERY);
713     OSL_ENSURE(xKeyFactory.is(),"No XDataDescriptorFactory Interface!");
714     if ( !xKeyFactory.is() )
715         return;
716     Reference<XAppend> xAppend(xKeyFactory,UNO_QUERY);
717     OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
718 
719     Reference<XPropertySet> xKey = xKeyFactory->createDataDescriptor();
720     OSL_ENSURE(xKey.is(),"Key is null!");
721     xKey->setPropertyValue(PROPERTY_TYPE,Any(KeyType::PRIMARY));
722 
723     Reference<XColumnsSupplier> xColSup(xKey,UNO_QUERY);
724     if(xColSup.is())
725     {
726         appendColumns(xColSup,_bNew,true);
727         Reference<XNameAccess> xColumns = xColSup->getColumns();
728         if(xColumns->hasElements())
729             xAppend->appendByDescriptor(xKey);
730     }
731 }
732 
loadData()733 void OTableController::loadData()
734 {
735     // if the data structure already exists, empty it
736     m_vRowList.clear();
737 
738     std::shared_ptr<OTableRow>  pTabEdRow;
739     Reference< XDatabaseMetaData> xMetaData = getMetaData( );
740     // fill data structure with data from DataDefinitionObject
741     if(m_xTable.is() && xMetaData.is())
742     {
743         Reference<XColumnsSupplier> xColSup(m_xTable,UNO_QUERY);
744         OSL_ENSURE(xColSup.is(),"No XColumnsSupplier!");
745         Reference<XNameAccess> xColumns = xColSup->getColumns();
746         // ReadOnly-Flag
747         // For Drop no row may be editable
748         // For Add only the empty rows may be editable
749         // For Add and Drop all rows can be edited
750         //  sal_Bool bReadOldRow = xMetaData->supportsAlterTableWithAddColumn() && xMetaData->supportsAlterTableWithDropColumn();
751         bool bIsAlterAllowed = isAlterAllowed();
752 
753         const Sequence<OUString> aColNames = xColumns->getElementNames();
754         for(const OUString& rColumn : aColNames)
755         {
756             Reference<XPropertySet> xColumn;
757             xColumns->getByName(rColumn) >>= xColumn;
758             sal_Int32 nType         = 0;
759             sal_Int32 nScale        = 0;
760             sal_Int32 nPrecision    = 0;
761             sal_Int32 nNullable     = 0;
762             sal_Int32 nFormatKey    = 0;
763             sal_Int32 nAlign        = 0;
764 
765             bool bIsAutoIncrement = false, bIsCurrency = false;
766             OUString sName,sDescription,sTypeName,sHelpText;
767             Any aControlDefault;
768 
769             // get the properties from the column
770             xColumn->getPropertyValue(PROPERTY_NAME)            >>= sName;
771             xColumn->getPropertyValue(PROPERTY_TYPENAME)        >>= sTypeName;
772             xColumn->getPropertyValue(PROPERTY_ISNULLABLE)      >>= nNullable;
773             xColumn->getPropertyValue(PROPERTY_ISAUTOINCREMENT) >>= bIsAutoIncrement;
774             xColumn->getPropertyValue(PROPERTY_ISCURRENCY)      >>= bIsCurrency;
775             xColumn->getPropertyValue(PROPERTY_TYPE)            >>= nType;
776             xColumn->getPropertyValue(PROPERTY_SCALE)           >>= nScale;
777             xColumn->getPropertyValue(PROPERTY_PRECISION)       >>= nPrecision;
778             xColumn->getPropertyValue(PROPERTY_DESCRIPTION)     >>= sDescription;
779 
780             if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_HELPTEXT))
781                 xColumn->getPropertyValue(PROPERTY_HELPTEXT)    >>= sHelpText;
782 
783             if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_CONTROLDEFAULT))
784                 aControlDefault = xColumn->getPropertyValue(PROPERTY_CONTROLDEFAULT);
785             if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_FORMATKEY))
786                 xColumn->getPropertyValue(PROPERTY_FORMATKEY)   >>= nFormatKey;
787             if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_ALIGN))
788                 xColumn->getPropertyValue(PROPERTY_ALIGN)       >>= nAlign;
789 
790             pTabEdRow = std::make_shared<OTableRow>();
791             pTabEdRow->SetReadOnly(!bIsAlterAllowed);
792             // search for type
793             bool bForce;
794             TOTypeInfoSP pTypeInfo = ::dbaui::getTypeInfoFromType(m_aTypeInfo,nType,sTypeName,u"x"_ustr,nPrecision,nScale,bIsAutoIncrement,bForce);
795             if ( !pTypeInfo )
796                 pTypeInfo = m_pTypeInfo;
797             pTabEdRow->SetFieldType( pTypeInfo, bForce );
798 
799             OFieldDescription* pActFieldDescr = pTabEdRow->GetActFieldDescr();
800             OSL_ENSURE(pActFieldDescr, "OTableController::loadData: invalid field description generated by the table row!");
801             if ( pActFieldDescr )
802             {
803                 pActFieldDescr->SetName(sName);
804                 pActFieldDescr->SetFormatKey(nFormatKey);
805                 pActFieldDescr->SetDescription(sDescription);
806                 pActFieldDescr->SetHelpText(sHelpText);
807                 pActFieldDescr->SetAutoIncrement(bIsAutoIncrement);
808                 pActFieldDescr->SetHorJustify(dbaui::mapTextJustify(nAlign));
809                 pActFieldDescr->SetCurrency(bIsCurrency);
810 
811                 // special data
812                 pActFieldDescr->SetIsNullable(nNullable);
813                 pActFieldDescr->SetControlDefault(aControlDefault);
814                 pActFieldDescr->SetPrecision(nPrecision);
815                 pActFieldDescr->SetScale(nScale);
816             }
817             m_vRowList.push_back( pTabEdRow);
818         }
819         // fill the primary  key information
820         Reference<XNameAccess> xKeyColumns  = getKeyColumns();
821         if(xKeyColumns.is())
822         {
823             const Sequence<OUString> aKeyColumnNames = xKeyColumns->getElementNames();
824             for(const OUString& rKeyColumn : aKeyColumnNames)
825             {
826                 for(std::shared_ptr<OTableRow> const& pRow : m_vRowList)
827                 {
828                     if(pRow->GetActFieldDescr()->GetName() == rKeyColumn)
829                     {
830                         pRow->SetPrimaryKey(true);
831                         break;
832                     }
833                 }
834             }
835         }
836     }
837 
838     // fill empty rows
839 
840     OTypeInfoMap::const_iterator aTypeIter = m_aTypeInfo.find(DataType::VARCHAR);
841     if(aTypeIter == m_aTypeInfo.end())
842         aTypeIter = m_aTypeInfo.begin();
843 
844     OSL_ENSURE(aTypeIter != m_aTypeInfo.end(),"We have no type information!");
845 
846     bool bReadRow = !isAddAllowed();
847     for(sal_Int32 i=m_vRowList.size(); i < NEWCOLS; i++ )
848     {
849         pTabEdRow = std::make_shared<OTableRow>();
850         pTabEdRow->SetReadOnly(bReadRow);
851         m_vRowList.push_back( pTabEdRow);
852     }
853 }
854 
getKeyColumns() const855 Reference<XNameAccess> OTableController::getKeyColumns() const
856 {
857     return getPrimaryKeyColumns_throw(m_xTable);
858 }
859 
checkColumns(bool _bNew)860 bool OTableController::checkColumns(bool _bNew)
861 {
862     bool bOk = true;
863     bool bFoundPKey = false;
864     Reference< XDatabaseMetaData > xMetaData = getMetaData( );
865     DatabaseMetaData aMetaData( getConnection() );
866 
867     ::comphelper::UStringMixEqual bCase(!xMetaData.is() || xMetaData->supportsMixedCaseQuotedIdentifiers());
868     std::vector< std::shared_ptr<OTableRow> >::const_iterator aIter = m_vRowList.begin();
869     std::vector< std::shared_ptr<OTableRow> >::const_iterator aEnd = m_vRowList.end();
870     for(;aIter != aEnd;++aIter)
871     {
872         OFieldDescription* pFieldDesc = (*aIter)->GetActFieldDescr();
873         if (pFieldDesc && !pFieldDesc->GetName().isEmpty())
874         {
875             bFoundPKey |=  (*aIter)->IsPrimaryKey();
876             // first check for duplicate names
877             bool bDuplicateNameFound = std::any_of(aIter+1, aEnd,
878                 [&bCase, &pFieldDesc](const std::shared_ptr<OTableRow>& rxRow) {
879                     OFieldDescription* pCompareDesc = rxRow->GetActFieldDescr();
880                     return pCompareDesc && bCase(pCompareDesc->GetName(),pFieldDesc->GetName());
881                 });
882             if (bDuplicateNameFound)
883             {
884                 OUString strMessage = DBA_RES(STR_TABLEDESIGN_DUPLICATE_NAME);
885                 strMessage = strMessage.replaceFirst("$column$", pFieldDesc->GetName());
886                 OSQLWarningBox aWarning(getFrameWeld(), strMessage);
887                 aWarning.run();
888                 return false;
889             }
890         }
891     }
892     if ( _bNew && !bFoundPKey && aMetaData.supportsPrimaryKeys() )
893     {
894         OUString sTitle(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY_HEAD));
895         OUString sMsg(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY));
896         OSQLMessageBox aBox(getFrameWeld(), sTitle,sMsg, MessBoxStyle::YesNoCancel | MessBoxStyle::DefaultYes);
897 
898         switch (aBox.run())
899         {
900         case RET_YES:
901         {
902             TOTypeInfoSP pTypeInfo = ::dbaui::queryPrimaryKeyType(m_aTypeInfo);
903             if ( !pTypeInfo )
904                 break;
905 
906             auto pNewRow = std::make_shared<OTableRow>();
907             pNewRow->SetFieldType( pTypeInfo );
908             OFieldDescription* pActFieldDescr = pNewRow->GetActFieldDescr();
909 
910             pActFieldDescr->SetAutoIncrement(pTypeInfo->bAutoIncrement);
911             pActFieldDescr->SetIsNullable(ColumnValue::NO_NULLS);
912 
913             pActFieldDescr->SetName( createUniqueName(u"ID"_ustr ));
914             pActFieldDescr->SetPrimaryKey( true );
915             m_vRowList.insert(m_vRowList.begin(),pNewRow);
916 
917             static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->Invalidate();
918             static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->RowInserted(0);
919         }
920         break;
921         case RET_CANCEL:
922             bOk = false;
923             break;
924         }
925     }
926     return bOk;
927 }
928 
alterColumns()929 void OTableController::alterColumns()
930 {
931     Reference<XColumnsSupplier> xColSup(m_xTable,UNO_QUERY_THROW);
932 
933     Reference<XNameAccess> xColumns = xColSup->getColumns();
934     Reference<XIndexAccess> xIdxColumns(xColumns,UNO_QUERY_THROW);
935     OSL_ENSURE(xColumns.is(),"No columns");
936     if ( !xColumns.is() )
937         return;
938     Reference<XAlterTable> xAlter(m_xTable,UNO_QUERY);  // can be null
939 
940     sal_Int32 nColumnCount = xIdxColumns->getCount();
941     Reference<XDrop> xDrop(xColumns,UNO_QUERY);         // can be null
942     Reference<XAppend> xAppend(xColumns,UNO_QUERY);     // can be null
943     Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY); // can be null
944 
945     bool bReload = false; // refresh the data
946 
947     // contains all columns names which are already handled those which are not in the list will be deleted
948     Reference< XDatabaseMetaData> xMetaData = getMetaData( );
949 
950     std::set<OUString, comphelper::UStringMixLess> aColumns(
951         comphelper::UStringMixLess(
952             !xMetaData.is()
953             || xMetaData->supportsMixedCaseQuotedIdentifiers()));
954     std::vector< std::shared_ptr<OTableRow> >::const_iterator aIter = m_vRowList.begin();
955     std::vector< std::shared_ptr<OTableRow> >::const_iterator aEnd = m_vRowList.end();
956     // first look for columns where something other than the name changed
957     for(sal_Int32 nPos = 0;aIter != aEnd;++aIter,++nPos)
958     {
959         OSL_ENSURE(*aIter,"OTableRow is null!");
960         OFieldDescription* pField = (*aIter)->GetActFieldDescr();
961         if ( !pField )
962             continue;
963         if ( (*aIter)->IsReadOnly() )
964         {
965             aColumns.insert(pField->GetName());
966             continue;
967         }
968 
969         Reference<XPropertySet> xColumn;
970         if ( xColumns->hasByName(pField->GetName()) )
971         {
972             aColumns.insert(pField->GetName());
973             xColumns->getByName(pField->GetName()) >>= xColumn;
974             OSL_ENSURE(xColumn.is(),"Column is null!");
975 
976             sal_Int32 nType=0,nPrecision=0,nScale=0,nNullable=0;
977             bool bAutoIncrement = false;
978             OUString sTypeName,sDescription;
979 
980             xColumn->getPropertyValue(PROPERTY_TYPE)            >>= nType;
981             xColumn->getPropertyValue(PROPERTY_PRECISION)       >>= nPrecision;
982             xColumn->getPropertyValue(PROPERTY_SCALE)           >>= nScale;
983             xColumn->getPropertyValue(PROPERTY_ISNULLABLE)      >>= nNullable;
984             xColumn->getPropertyValue(PROPERTY_ISAUTOINCREMENT) >>= bAutoIncrement;
985             xColumn->getPropertyValue(PROPERTY_DESCRIPTION)     >>= sDescription;
986 
987             try { xColumn->getPropertyValue(PROPERTY_TYPENAME) >>= sTypeName; }
988             catch( const Exception& )
989             {
990                 OSL_FAIL( "no TypeName property?!" );
991                 // since this is a last minute fix for #i41785#, I want to be on the safe side,
992                 // and catch errors here as early as possible (instead of the whole process of altering
993                 // the columns failing)
994                 // Normally, sdbcx::Column objects are expected to have a TypeName property
995             }
996 
997             // check if something changed
998             if((nType != pField->GetType()                  ||
999                 sTypeName != pField->GetTypeName()         ||
1000                 (nPrecision != pField->GetPrecision() && nPrecision )       ||
1001                 nScale != pField->GetScale()                ||
1002                 nNullable != pField->GetIsNullable()        ||
1003                 sDescription != pField->GetDescription()        ||
1004                 bAutoIncrement != pField->IsAutoIncrement())&&
1005                 xColumnFactory.is())
1006             {
1007                 Reference<XPropertySet> xNewColumn = xColumnFactory->createDataDescriptor();
1008                 ::dbaui::setColumnProperties(xNewColumn,pField);
1009                 // first try to alter the column
1010                 bool bNotOk = false;
1011                 try
1012                 {
1013                     // first try if we can alter the column
1014                     if(xAlter.is())
1015                         xAlter->alterColumnByName(pField->GetName(),xNewColumn);
1016                 }
1017                 catch(const SQLException&)
1018                 {
1019                     if(xDrop.is() && xAppend.is())
1020                     {
1021                         OUString aMessage( DBA_RES( STR_TABLEDESIGN_ALTER_ERROR ) );
1022                         aMessage = aMessage.replaceFirst( "$column$", pField->GetName() );
1023 
1024                         SQLExceptionInfo aError( ::cppu::getCaughtException() );
1025                         OSQLWarningBox aMsg(getFrameWeld(), aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes , &aError);
1026                         bNotOk = aMsg.run() == RET_YES;
1027                     }
1028                     else
1029                         throw;
1030                 }
1031                 // if something went wrong or we can't alter columns
1032                 // drop and append a new one
1033                 if((!xAlter.is() || bNotOk) && xDrop.is() && xAppend.is())
1034                 {
1035                     xDrop->dropByName(pField->GetName());
1036                     try
1037                     {
1038                         xAppend->appendByDescriptor(xNewColumn);
1039                     }
1040                     catch(const SQLException&)
1041                     { // an error occurred so we try to reactivate the old one
1042                         xAppend->appendByDescriptor(xColumn);
1043                         throw;
1044                     }
1045                 }
1046                 // exceptions are caught outside
1047                 xNewColumn = nullptr;
1048                 if(xColumns->hasByName(pField->GetName()))
1049                     xColumns->getByName(pField->GetName()) >>= xColumn;
1050                 bReload = true;
1051             }
1052 
1053         }
1054         else if(xColumnFactory.is() && xAlter.is() && nPos < nColumnCount)
1055         { // we can't find the column so we could try it with the index before we drop and append a new column
1056             try
1057             {
1058                 Reference<XPropertySet> xNewColumn = xColumnFactory->createDataDescriptor();
1059                 ::dbaui::setColumnProperties(xNewColumn,pField);
1060                 xAlter->alterColumnByIndex(nPos,xNewColumn);
1061                 if(xColumns->hasByName(pField->GetName()))
1062                 {   // ask for the append by name
1063                     aColumns.insert(pField->GetName());
1064                     xColumns->getByName(pField->GetName()) >>= xColumn;
1065                     if(xColumn.is())
1066                         pField->copyColumnSettingsTo(xColumn);
1067                 }
1068                 else
1069                 {
1070                     OSL_FAIL("OTableController::alterColumns: invalid column (2)!");
1071                 }
1072             }
1073             catch(const SQLException&)
1074             { // we couldn't alter the column so we have to add new columns
1075                 SQLExceptionInfo aError( ::cppu::getCaughtException() );
1076                 bReload = true;
1077                 if(xDrop.is() && xAppend.is())
1078                 {
1079                     OUString aMessage(DBA_RES(STR_TABLEDESIGN_ALTER_ERROR));
1080                     aMessage = aMessage.replaceFirst("$column$",pField->GetName());
1081                     OSQLWarningBox aMsg(getFrameWeld(), aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes, &aError);
1082                     if (aMsg.run() != RET_YES)
1083                     {
1084                         Reference<XPropertySet> xNewColumn(xIdxColumns->getByIndex(nPos),UNO_QUERY_THROW);
1085                         OUString sName;
1086                         xNewColumn->getPropertyValue(PROPERTY_NAME) >>= sName;
1087                         aColumns.insert(sName);
1088                         aColumns.insert(pField->GetName());
1089                         continue;
1090                     }
1091                 }
1092                 else
1093                     throw;
1094             }
1095         }
1096         else
1097             bReload = true;
1098     }
1099     // alter column settings
1100 
1101     // first look for columns where something other than the name changed
1102     for (auto const& row : m_vRowList)
1103     {
1104         OSL_ENSURE(row,"OTableRow is null!");
1105         OFieldDescription* pField = row->GetActFieldDescr();
1106         if ( !pField )
1107             continue;
1108         if ( row->IsReadOnly() )
1109         {
1110             aColumns.insert(pField->GetName());
1111             continue;
1112         }
1113 
1114         Reference<XPropertySet> xColumn;
1115         if ( xColumns->hasByName(pField->GetName()) )
1116         {
1117             xColumns->getByName(pField->GetName()) >>= xColumn;
1118             Reference<XPropertySetInfo> xInfo = xColumn->getPropertySetInfo();
1119             if ( xInfo->hasPropertyByName(PROPERTY_HELPTEXT) )
1120                 xColumn->setPropertyValue(PROPERTY_HELPTEXT,Any(pField->GetHelpText()));
1121 
1122             if(xInfo->hasPropertyByName(PROPERTY_CONTROLDEFAULT))
1123                 xColumn->setPropertyValue(PROPERTY_CONTROLDEFAULT,pField->GetControlDefault());
1124             if(xInfo->hasPropertyByName(PROPERTY_FORMATKEY))
1125                 xColumn->setPropertyValue(PROPERTY_FORMATKEY,Any(pField->GetFormatKey()));
1126             if(xInfo->hasPropertyByName(PROPERTY_ALIGN))
1127                 xColumn->setPropertyValue(PROPERTY_ALIGN,Any(dbaui::mapTextAlign(pField->GetHorJustify())));
1128         }
1129     }
1130     // second drop all columns which could be found by name
1131     Reference<XNameAccess> xKeyColumns  = getKeyColumns();
1132     // now we have to look for the columns who could be deleted
1133     if ( xDrop.is() )
1134     {
1135         const Sequence<OUString> aColNames = xColumns->getElementNames();
1136         for(const OUString& rColumnName : aColNames)
1137         {
1138             if(aColumns.find(rColumnName) == aColumns.end()) // found a column to delete
1139             {
1140                 if(xKeyColumns.is() && xKeyColumns->hasByName(rColumnName)) // check if this column is a member of the primary key
1141                 {
1142                     OUString aMsgT(DBA_RES(STR_TBL_COLUMN_IS_KEYCOLUMN));
1143                     aMsgT = aMsgT.replaceFirst("$column$",rColumnName);
1144                     OUString aTitle(DBA_RES(STR_TBL_COLUMN_IS_KEYCOLUMN_TITLE));
1145                     OSQLMessageBox aMsg(getFrameWeld(), aTitle, aMsgT, MessBoxStyle::YesNo| MessBoxStyle::DefaultYes);
1146                     if (aMsg.run() == RET_YES)
1147                     {
1148                         xKeyColumns = nullptr;
1149                         dropPrimaryKey();
1150                     }
1151                     else
1152                     {
1153                         bReload = true;
1154                         continue;
1155                     }
1156                 }
1157                 try
1158                 {
1159                     xDrop->dropByName(rColumnName);
1160                 }
1161                 catch (const SQLException&)
1162                 {
1163                     const auto caughtException = ::cppu::getCaughtException();
1164                     OUString sError( DBA_RES( STR_TABLEDESIGN_COULD_NOT_DROP_COL ) );
1165                     sError = sError.replaceFirst( "$column$", rColumnName );
1166 
1167                     throw SQLException(sError, {}, u"S1000"_ustr, 0, caughtException);
1168                 }
1169             }
1170         }
1171     }
1172 
1173     // third append the new columns
1174     for(const auto& rxRow : m_vRowList)
1175     {
1176         OSL_ENSURE(rxRow,"OTableRow is null!");
1177         OFieldDescription* pField = rxRow->GetActFieldDescr();
1178         if ( !pField || rxRow->IsReadOnly() || aColumns.find(pField->GetName()) != aColumns.end() )
1179             continue;
1180 
1181         Reference<XPropertySet> xColumn;
1182         if(!xColumns->hasByName(pField->GetName()))
1183         {
1184             if(xColumnFactory.is() && xAppend.is())
1185             {// column not found by its name so we assume it is new
1186                 // Column is new
1187                 xColumn = xColumnFactory->createDataDescriptor();
1188                 ::dbaui::setColumnProperties(xColumn,pField);
1189                 xAppend->appendByDescriptor(xColumn);
1190                 if(xColumns->hasByName(pField->GetName()))
1191                 {   // ask for the append by name
1192                     aColumns.insert(pField->GetName());
1193                     xColumns->getByName(pField->GetName()) >>= xColumn;
1194                     if(xColumn.is())
1195                         pField->copyColumnSettingsTo(xColumn);
1196                 }
1197                 else
1198                 {
1199                     OSL_FAIL("OTableController::alterColumns: invalid column!");
1200                 }
1201             }
1202         }
1203     }
1204 
1205     // check if we have to do something with the primary key
1206     bool bNeedDropKey = false;
1207     bool bNeedAppendKey = false;
1208     if ( xKeyColumns.is() )
1209     {
1210         for(const auto& rxRow : m_vRowList)
1211         {
1212             OSL_ENSURE(rxRow,"OTableRow is null!");
1213             OFieldDescription* pField = rxRow->GetActFieldDescr();
1214             if ( !pField )
1215                 continue;
1216 
1217             if  (   pField->IsPrimaryKey()
1218                 &&  !xKeyColumns->hasByName( pField->GetName() )
1219                 )
1220             {   // new primary key column inserted which isn't already in the columns selection
1221                 bNeedDropKey = bNeedAppendKey = true;
1222                 break;
1223             }
1224             else if (   !pField->IsPrimaryKey()
1225                     &&  xKeyColumns->hasByName( pField->GetName() )
1226                     )
1227             {   // found a column which currently is in the primary key, but is marked not to be anymore
1228                 bNeedDropKey = bNeedAppendKey = true;
1229                 break;
1230             }
1231         }
1232     }
1233     else
1234     {   // no primary key available so we check if we should create one
1235         bNeedAppendKey = true;
1236     }
1237 
1238     if ( bNeedDropKey && xKeyColumns.is() && xKeyColumns->getElementNames().hasElements() )
1239         dropPrimaryKey();
1240 
1241     if ( bNeedAppendKey )
1242     {
1243         Reference< XKeysSupplier > xKeySup( m_xTable, UNO_QUERY );
1244         appendPrimaryKey( xKeySup ,false);
1245     }
1246 
1247     reSyncRows();
1248 
1249     if ( bReload )
1250         reload();
1251 }
1252 
dropPrimaryKey()1253 void OTableController::dropPrimaryKey()
1254 {
1255     SQLExceptionInfo aInfo;
1256     try
1257     {
1258         Reference<XKeysSupplier> xKeySup(m_xTable,UNO_QUERY);
1259         Reference<XIndexAccess> xKeys;
1260         if(xKeySup.is())
1261             xKeys = xKeySup->getKeys();
1262 
1263         if(xKeys.is())
1264         {
1265             Reference<XPropertySet> xProp;
1266             for(sal_Int32 i=0;i< xKeys->getCount();++i)
1267             {
1268                 xProp.set(xKeys->getByIndex(i),UNO_QUERY);
1269                 sal_Int32 nKeyType = 0;
1270                 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
1271                 if(KeyType::PRIMARY == nKeyType)
1272                 {
1273                     Reference<XDrop> xDrop(xKeys,UNO_QUERY);
1274                     xDrop->dropByIndex(i); // delete the key
1275                     break;
1276                 }
1277             }
1278         }
1279     }
1280     catch(const SQLContext& e)
1281     {
1282         aInfo = SQLExceptionInfo(e);
1283     }
1284     catch(const SQLWarning& e)
1285     {
1286         aInfo = SQLExceptionInfo(e);
1287     }
1288     catch(const SQLException& e)
1289     {
1290         aInfo = SQLExceptionInfo(e);
1291     }
1292     catch( const Exception& )
1293     {
1294         DBG_UNHANDLED_EXCEPTION("dbaccess");
1295     }
1296 
1297     showError(aInfo);
1298 }
1299 
assignTable()1300 void OTableController::assignTable()
1301 {
1302     // get the table
1303     if(m_sName.isEmpty())
1304         return;
1305 
1306     Reference<XNameAccess> xNameAccess;
1307     Reference<XTablesSupplier> xSup(getConnection(),UNO_QUERY);
1308     if(!xSup.is())
1309         return;
1310 
1311     xNameAccess = xSup->getTables();
1312     OSL_ENSURE(xNameAccess.is(),"no nameaccess for the queries!");
1313 
1314     if(!xNameAccess->hasByName(m_sName))
1315         return;
1316 
1317     Reference<XPropertySet> xProp(xNameAccess->getByName(m_sName), css::uno::UNO_QUERY);
1318     if (!xProp.is())
1319         return;
1320 
1321     m_xTable = std::move(xProp);
1322     startTableListening();
1323 
1324     // check if we set the table editable
1325     Reference<XDatabaseMetaData> xMeta = getConnection()->getMetaData();
1326     setEditable( xMeta.is() && !xMeta->isReadOnly() && (isAlterAllowed() || isDropAllowed() || isAddAllowed()) );
1327     if(!isEditable())
1328     {
1329         for( const auto& rTableRow : m_vRowList )
1330         {
1331             rTableRow->SetReadOnly();
1332         }
1333     }
1334     m_bNew = false;
1335     // be notified when the table is in disposing
1336     InvalidateAll();
1337 }
1338 
isAddAllowed() const1339 bool OTableController::isAddAllowed() const
1340 {
1341     Reference<XColumnsSupplier> xColsSup(m_xTable,UNO_QUERY);
1342     bool bAddAllowed = !m_xTable.is();
1343     if(xColsSup.is())
1344         bAddAllowed = Reference<XAppend>(xColsSup->getColumns(),UNO_QUERY).is();
1345 
1346     try
1347     {
1348         Reference< XDatabaseMetaData > xMetaData = getMetaData( );
1349         bAddAllowed = bAddAllowed || ( xMetaData.is() && xMetaData->supportsAlterTableWithAddColumn());
1350     }
1351     catch(Exception&)
1352     {
1353         DBG_UNHANDLED_EXCEPTION("dbaccess");
1354         bAddAllowed = false;
1355     }
1356 
1357     return bAddAllowed;
1358 }
1359 
isDropAllowed() const1360 bool OTableController::isDropAllowed() const
1361 {
1362     Reference<XColumnsSupplier> xColsSup(m_xTable,UNO_QUERY);
1363     bool bDropAllowed = !m_xTable.is();
1364     if(xColsSup.is())
1365     {
1366         Reference<XNameAccess> xNameAccess = xColsSup->getColumns();
1367         bDropAllowed = Reference<XDrop>(xNameAccess,UNO_QUERY).is() && xNameAccess->hasElements();
1368     }
1369 
1370     Reference< XDatabaseMetaData> xMetaData = getMetaData( );
1371     bDropAllowed = bDropAllowed || ( xMetaData.is() && xMetaData->supportsAlterTableWithDropColumn());
1372 
1373     return bDropAllowed;
1374 }
1375 
isAlterAllowed() const1376 bool OTableController::isAlterAllowed() const
1377 {
1378     bool bAllowed(!m_xTable.is() || Reference<XAlterTable>(m_xTable,UNO_QUERY).is());
1379     return bAllowed;
1380 }
1381 
reSyncRows()1382 void OTableController::reSyncRows()
1383 {
1384     bool bAlterAllowed  = isAlterAllowed();
1385     bool bAddAllowed    = isAddAllowed();
1386     for (auto const& row : m_vRowList)
1387     {
1388         OSL_ENSURE(row,"OTableRow is null!");
1389         OFieldDescription* pField = row->GetActFieldDescr();
1390         if ( pField )
1391             row->SetReadOnly(!bAlterAllowed);
1392         else
1393             row->SetReadOnly(!bAddAllowed);
1394 
1395     }
1396     static_cast<OTableDesignView*>(getView())->reSync();    // show the windows and fill with our information
1397 
1398     ClearUndoManager();
1399     setModified(false);     // and we are not modified yet
1400 }
1401 
createUniqueName(const OUString & _rName)1402 OUString OTableController::createUniqueName(const OUString& _rName)
1403 {
1404     OUString sName = _rName;
1405     Reference< XDatabaseMetaData> xMetaData = getMetaData( );
1406 
1407     ::comphelper::UStringMixEqual bCase(!xMetaData.is() || xMetaData->supportsMixedCaseQuotedIdentifiers());
1408 
1409     auto lHasName = [&bCase, &sName](const std::shared_ptr<OTableRow>& rxRow) {
1410         OFieldDescription* pFieldDesc = rxRow->GetActFieldDescr();
1411         return pFieldDesc && !pFieldDesc->GetName().isEmpty() && bCase(sName, pFieldDesc->GetName());
1412     };
1413 
1414     sal_Int32 i = 0;
1415     while(std::any_of(m_vRowList.begin(), m_vRowList.end(), lHasName))
1416     {
1417         // found a second name of _rName so we need another
1418         sName = _rName + OUString::number(++i);
1419     }
1420     return sName;
1421 }
1422 
getPrivateTitle() const1423 OUString OTableController::getPrivateTitle() const
1424 {
1425     OUString sTitle;
1426     try
1427     {
1428         // get the table
1429         if ( !m_sName.isEmpty() && getConnection().is() )
1430         {
1431             if ( m_xTable.is() )
1432                 sTitle = ::dbtools::composeTableName( getConnection()->getMetaData(), m_xTable, ::dbtools::EComposeRule::InDataManipulation, false );
1433             else
1434                 sTitle = m_sName;
1435         }
1436         if ( sTitle.isEmpty() )
1437         {
1438             OUString aName = DBA_RES(STR_TBL_TITLE);
1439             sTitle = o3tl::getToken(aName,0,' ') + OUString::number(getCurrentStartNumber());
1440         }
1441     }
1442     catch( const Exception& )
1443     {
1444         DBG_UNHANDLED_EXCEPTION("dbaccess");
1445     }
1446     return sTitle;
1447 }
1448 
reload()1449 void OTableController::reload()
1450 {
1451     loadData();                 // fill the column information from the table
1452     static_cast<OTableDesignView*>(getView())->reSync();    // show the windows and fill with our information
1453     ClearUndoManager();
1454     setModified(false);     // and we are not modified yet
1455     static_cast<OTableDesignView*>(getView())->Invalidate();
1456 }
1457 
getFirstEmptyRowPosition()1458 sal_Int32 OTableController::getFirstEmptyRowPosition()
1459 {
1460     sal_Int32 nRet = 0;
1461     bool bFoundElem = false;
1462     for (auto const& row : m_vRowList)
1463     {
1464         if ( !row || !row->GetActFieldDescr() || row->GetActFieldDescr()->GetName().isEmpty() )
1465         {
1466             bFoundElem = true;
1467             break;
1468         }
1469         ++nRet;
1470     }
1471     if (!bFoundElem)
1472     {
1473         bool bReadRow = !isAddAllowed();
1474         auto pTabEdRow = std::make_shared<OTableRow>();
1475         pTabEdRow->SetReadOnly(bReadRow);
1476         nRet = m_vRowList.size();
1477         m_vRowList.push_back(std::move(pTabEdRow));
1478     }
1479     return nRet;
1480 }
1481 
isAutoIncrementPrimaryKey() const1482 bool OTableController::isAutoIncrementPrimaryKey() const
1483 {
1484     return getSdbMetaData().isAutoIncrementPrimaryKey();
1485 }
1486 
1487 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1488