Update git submodules
[LibreOffice.git] / dbaccess / source / ui / tabledesign / TEditControl.cxx
blob28839d7a9ffd898bf83cab953e7027cfacb8366e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "TEditControl.hxx"
21 #include <com/sun/star/sdbc/ColumnValue.hpp>
22 #include <com/sun/star/sdbc/SQLException.hpp>
23 #include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
24 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
25 #include <com/sun/star/util/XNumberFormatTypes.hpp>
26 #include <core_resource.hxx>
27 #include <strings.hrc>
28 #include <strings.hxx>
29 #include <helpids.h>
30 #include <comphelper/types.hxx>
31 #include <FieldDescControl.hxx>
32 #include <FieldDescriptions.hxx>
33 #include "TableUndo.hxx"
34 #include <TableController.hxx>
35 #include <connectivity/dbmetadata.hxx>
36 #include <connectivity/dbtools.hxx>
37 #include <SqlNameEdit.hxx>
38 #include <TableRowExchange.hxx>
39 #include <o3tl/safeint.hxx>
40 #include <sot/storage.hxx>
41 #include <svx/svxids.hrc>
42 #include <UITools.hxx>
43 #include "TableFieldControl.hxx"
44 #include <dsntypes.hxx>
45 #include <vcl/commandevent.hxx>
46 #include <vcl/svapp.hxx>
48 using namespace ::dbaui;
49 using namespace ::comphelper;
50 using namespace ::svt;
51 using namespace ::com::sun::star::uno;
52 using namespace ::com::sun::star::io;
53 using namespace ::com::sun::star::beans;
54 using namespace ::com::sun::star::util;
55 using namespace ::com::sun::star::sdbc;
58 #define HANDLE_ID 0
60 // default field widths
61 #define FIELDNAME_WIDTH 100
62 #define FIELDTYPE_WIDTH 150
63 #define FIELDDESCR_WIDTH 300
65 // Maximum length in description field
66 #define MAX_DESCR_LEN 256
68 OTableEditorCtrl::ClipboardInvalidator::ClipboardInvalidator(OTableEditorCtrl* _pOwner)
69 : m_aInvalidateTimer("dbaccess ClipboardInvalidator")
70 , m_pOwner(_pOwner)
73 m_aInvalidateTimer.SetTimeout(500);
74 m_aInvalidateTimer.SetInvokeHandler(LINK(this, OTableEditorCtrl::ClipboardInvalidator, OnInvalidate));
75 m_aInvalidateTimer.Start();
78 OTableEditorCtrl::ClipboardInvalidator::~ClipboardInvalidator()
80 m_aInvalidateTimer.Stop();
83 void OTableEditorCtrl::ClipboardInvalidator::Stop()
85 m_aInvalidateTimer.Stop();
88 IMPL_LINK_NOARG(OTableEditorCtrl::ClipboardInvalidator, OnInvalidate, Timer *, void)
90 m_pOwner->GetView()->getController().InvalidateFeature(SID_CUT);
91 m_pOwner->GetView()->getController().InvalidateFeature(SID_COPY);
92 m_pOwner->GetView()->getController().InvalidateFeature(SID_PASTE);
95 void OTableEditorCtrl::Init()
97 OTableRowView::Init();
99 // Should it be opened ReadOnly?
100 bool bRead(GetView()->getController().isReadOnly());
102 SetReadOnly( bRead );
104 // Insert the columns
105 InsertDataColumn( FIELD_NAME, DBA_RES(STR_TAB_FIELD_COLUMN_NAME), FIELDNAME_WIDTH );
107 InsertDataColumn( FIELD_TYPE, DBA_RES(STR_TAB_FIELD_COLUMN_DATATYPE), FIELDTYPE_WIDTH );
109 ::dbaccess::ODsnTypeCollection aDsnTypes(GetView()->getController().getORB());
110 bool bShowColumnDescription = aDsnTypes.supportsColumnDescription(::comphelper::getString(GetView()->getController().getDataSource()->getPropertyValue(PROPERTY_URL)));
111 InsertDataColumn( HELP_TEXT, DBA_RES(STR_TAB_HELP_TEXT), bShowColumnDescription ? FIELDTYPE_WIDTH : FIELDDESCR_WIDTH );
113 if ( bShowColumnDescription )
115 InsertDataColumn( COLUMN_DESCRIPTION, DBA_RES(STR_COLUMN_DESCRIPTION), FIELDTYPE_WIDTH );
118 InitCellController();
120 // Insert the rows
121 RowInserted(0, m_pRowList->size());
124 OTableEditorCtrl::OTableEditorCtrl(vcl::Window* pWindow, OTableDesignView* pView)
125 :OTableRowView(pWindow)
126 ,m_pView(pView)
127 ,pNameCell(nullptr)
128 ,pTypeCell(nullptr)
129 ,pHelpTextCell(nullptr)
130 ,pDescrCell(nullptr)
131 ,pDescrWin(nullptr)
132 ,nCutEvent(nullptr)
133 ,nPasteEvent(nullptr)
134 ,nDeleteEvent(nullptr)
135 ,nInsNewRowsEvent(nullptr)
136 ,nInvalidateTypeEvent(nullptr)
137 ,m_eChildFocus(NONE)
138 ,nOldDataPos(-1)
139 ,bReadOnly(true)
140 ,m_aInvalidate(this)
142 SetHelpId(HID_TABDESIGN_BACKGROUND);
143 GetDataWindow().SetHelpId(HID_CTL_TABLEEDIT);
145 m_pRowList = &GetView()->getController().getRows();
146 m_nDataPos = 0;
149 SfxUndoManager& OTableEditorCtrl::GetUndoManager() const
151 return GetView()->getController().GetUndoManager();
155 void OTableEditorCtrl::SetReadOnly( bool bRead )
157 // nothing to do?
158 if (bRead == IsReadOnly())
159 // This check is important, as the underlying Def may be unnecessarily locked or unlocked
160 // or worse, this action may not be reversed afterwards
161 return;
163 bReadOnly = bRead;
165 // Disable active cells
166 sal_Int32 nRow(GetCurRow());
167 sal_uInt16 nCol(GetCurColumnId());
168 DeactivateCell();
170 // Select the correct Browsers cursor
171 BrowserMode nMode(BrowserMode::COLUMNSELECTION | BrowserMode::MULTISELECTION | BrowserMode::KEEPHIGHLIGHT |
172 BrowserMode::HLINES | BrowserMode::VLINES|BrowserMode::AUTOSIZE_LASTCOL);
173 if( !bReadOnly )
174 nMode |= BrowserMode::HIDECURSOR;
175 SetMode(nMode);
177 if( !bReadOnly )
178 ActivateCell( nRow, nCol );
181 void OTableEditorCtrl::InitCellController()
183 // Cell Field name
184 sal_Int32 nMaxTextLen = 0;
185 OUString sExtraNameChars;
186 Reference<XConnection> xCon;
189 xCon = GetView()->getController().getConnection();
190 Reference< XDatabaseMetaData> xMetaData = xCon.is() ? xCon->getMetaData() : Reference< XDatabaseMetaData>();
192 // length 0 is treated by Entry::set_max_length as unlimited
193 nMaxTextLen = xMetaData.is() ? xMetaData->getMaxColumnNameLength() : 0;
195 sExtraNameChars = xMetaData.is() ? xMetaData->getExtraNameCharacters() : OUString();
198 catch(SQLException&)
200 OSL_FAIL("getMaxColumnNameLength");
203 pNameCell = VclPtr<OSQLNameEditControl>::Create(&GetDataWindow(), sExtraNameChars);
204 pNameCell->get_widget().set_max_length(nMaxTextLen);
205 pNameCell->setCheck( isSQL92CheckEnabled(xCon) );
207 // Cell type
208 pTypeCell = VclPtr<ListBoxControl>::Create( &GetDataWindow() );
210 // Cell description
211 pDescrCell = VclPtr<EditControl>::Create(&GetDataWindow());
212 pDescrCell->get_widget().set_max_length(MAX_DESCR_LEN);
214 pHelpTextCell = VclPtr<EditControl>::Create(&GetDataWindow());
215 pHelpTextCell->get_widget().set_max_length(MAX_DESCR_LEN);
217 pNameCell->SetHelpId(HID_TABDESIGN_NAMECELL);
218 pTypeCell->SetHelpId(HID_TABDESIGN_TYPECELL);
219 pDescrCell->SetHelpId(HID_TABDESIGN_COMMENTCELL);
220 pHelpTextCell->SetHelpId(HID_TABDESIGN_HELPTEXT);
222 Size aHeight;
223 const Control* pControls[] = { pTypeCell,pDescrCell,pNameCell,pHelpTextCell};
224 for(const Control* pControl : pControls)
226 const Size aTemp(pControl->GetOptimalSize());
227 if ( aTemp.Height() > aHeight.Height() )
228 aHeight.setHeight( aTemp.Height() );
230 SetDataRowHeight(aHeight.Height());
232 ClearModified();
235 void OTableEditorCtrl::ClearModified()
237 pNameCell->get_widget().save_value();
238 pDescrCell->get_widget().save_value();
239 pHelpTextCell->get_widget().save_value();
240 pTypeCell->get_widget().save_value();
243 OTableEditorCtrl::~OTableEditorCtrl()
245 disposeOnce();
248 void OTableEditorCtrl::dispose()
250 // Reset the Undo-Manager
251 GetUndoManager().Clear();
253 m_aInvalidate.Stop();
255 // Take possible Events from the queue
256 if( nCutEvent )
257 Application::RemoveUserEvent( nCutEvent );
258 if( nPasteEvent )
259 Application::RemoveUserEvent( nPasteEvent );
260 if( nDeleteEvent )
261 Application::RemoveUserEvent( nDeleteEvent );
262 if( nInsNewRowsEvent )
263 Application::RemoveUserEvent( nInsNewRowsEvent );
264 if( nInvalidateTypeEvent )
265 Application::RemoveUserEvent( nInvalidateTypeEvent );
267 // Delete the control types
268 pNameCell.disposeAndClear();
269 pTypeCell.disposeAndClear();
270 pDescrCell.disposeAndClear();
271 pHelpTextCell.disposeAndClear();
272 pDescrWin = nullptr;
273 m_pView.clear();
274 OTableRowView::dispose();
277 bool OTableEditorCtrl::SetDataPtr( sal_Int32 nRow )
279 if(nRow == -1)
280 return false;
282 OSL_ENSURE(nRow < static_cast<tools::Long>(m_pRowList->size()),"Row is greater than size!");
283 if(nRow >= static_cast<tools::Long>(m_pRowList->size()))
284 return false;
285 pActRow = (*m_pRowList)[nRow];
286 return pActRow != nullptr;
289 bool OTableEditorCtrl::SeekRow(sal_Int32 _nRow)
291 // Call the Base class to remember which row must be repainted
292 EditBrowseBox::SeekRow(_nRow);
294 m_nCurrentPos = _nRow;
295 return SetDataPtr(_nRow);
298 void OTableEditorCtrl::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect,
299 sal_uInt16 nColumnId ) const
301 const OUString aText( GetCellText( m_nCurrentPos, nColumnId ));
303 rDev.Push( vcl::PushFlags::CLIPREGION );
304 rDev.SetClipRegion(vcl::Region(rRect));
305 rDev.DrawText( rRect, aText, DrawTextFlags::Left | DrawTextFlags::VCenter );
306 rDev.Pop();
309 CellController* OTableEditorCtrl::GetController(sal_Int32 nRow, sal_uInt16 nColumnId)
311 // If EditorCtrl is ReadOnly, editing is forbidden
312 Reference<XPropertySet> xTable = GetView()->getController().getTable();
313 if (IsReadOnly() || ( xTable.is() &&
314 xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_TYPE) &&
315 ::comphelper::getString(xTable->getPropertyValue(PROPERTY_TYPE)) == "VIEW"))
316 return nullptr;
318 // If the row is ReadOnly, editing is forbidden
319 SetDataPtr( nRow );
320 if( pActRow->IsReadOnly() )
321 return nullptr;
323 OFieldDescription* pActFieldDescr = pActRow->GetActFieldDescr();
324 switch (nColumnId)
326 case FIELD_NAME:
327 return new EditCellController( pNameCell );
328 case FIELD_TYPE:
329 if (pActFieldDescr && !pActFieldDescr->GetName().isEmpty())
330 return new ListBoxCellController( pTypeCell );
331 else return nullptr;
332 case HELP_TEXT:
333 if (pActFieldDescr && !pActFieldDescr->GetName().isEmpty())
334 return new EditCellController( pHelpTextCell );
335 else
336 return nullptr;
337 case COLUMN_DESCRIPTION:
338 if (pActFieldDescr && !pActFieldDescr->GetName().isEmpty())
339 return new EditCellController( pDescrCell );
340 else
341 return nullptr;
342 default:
343 return nullptr;
347 void OTableEditorCtrl::InitController(CellControllerRef&, sal_Int32 nRow, sal_uInt16 nColumnId)
349 SeekRow( nRow == -1 ? GetCurRow() : nRow);
350 OFieldDescription* pActFieldDescr = pActRow->GetActFieldDescr();
351 OUString aInitString;
353 switch (nColumnId)
355 case FIELD_NAME:
357 if( pActFieldDescr )
358 aInitString = pActFieldDescr->GetName();
360 weld::Entry& rEntry = pNameCell->get_widget();
361 rEntry.set_text(aInitString);
362 rEntry.save_value();
363 break;
365 case FIELD_TYPE:
367 if ( pActFieldDescr && pActFieldDescr->getTypeInfo() )
368 aInitString = pActFieldDescr->getTypeInfo()->aUIName;
370 // Set the ComboBox contents
371 weld::ComboBox& rTypeList = pTypeCell->get_widget();
372 rTypeList.clear();
373 if( !pActFieldDescr )
374 break;
376 const OTypeInfoMap& rTypeInfo = GetView()->getController().getTypeInfo();
377 for (auto const& elem : rTypeInfo)
378 rTypeList.append_text(elem.second->aUIName);
379 rTypeList.set_active_text(aInitString);
382 break;
383 case HELP_TEXT:
385 if( pActFieldDescr )
386 aInitString = pActFieldDescr->GetHelpText();
387 weld::Entry& rEntry = pHelpTextCell->get_widget();
388 rEntry.set_text(aInitString);
389 rEntry.save_value();
390 break;
392 case COLUMN_DESCRIPTION:
394 if( pActFieldDescr )
395 aInitString = pActFieldDescr->GetDescription();
396 weld::Entry& rEntry = pDescrCell->get_widget();
397 rEntry.set_text(aInitString);
398 rEntry.save_value();
399 break;
404 EditBrowseBox::RowStatus OTableEditorCtrl::GetRowStatus(sal_Int32 nRow) const
406 const_cast<OTableEditorCtrl*>(this)->SetDataPtr( nRow );
407 if( !pActRow )
408 return EditBrowseBox::CLEAN;
409 if (nRow >= 0 && nRow == m_nDataPos)
411 if( pActRow->IsPrimaryKey() )
412 return EditBrowseBox::CURRENT_PRIMARYKEY;
413 return EditBrowseBox::CURRENT;
415 else
417 if( pActRow->IsPrimaryKey() )
418 return EditBrowseBox::PRIMARYKEY;
419 return EditBrowseBox::CLEAN;
423 void OTableEditorCtrl::SaveCurRow()
425 if (GetFieldDescr(GetCurRow()) == nullptr)
426 // there is no data in the current row
427 return;
428 if (!SaveModified())
429 return;
431 SetDataPtr(GetCurRow());
432 pDescrWin->SaveData( pActRow->GetActFieldDescr() );
435 void OTableEditorCtrl::DisplayData(sal_Int32 nRow)
437 // go to the correct cell
438 SetDataPtr(nRow);
440 // Disable Edit-Mode temporarily
441 bool bWasEditing = IsEditing();
442 if (bWasEditing)
443 DeactivateCell();
445 CellControllerRef aTemp;
446 InitController(aTemp, nRow, FIELD_NAME);
447 InitController(aTemp, nRow, FIELD_TYPE);
448 InitController(aTemp, nRow, COLUMN_DESCRIPTION);
449 InitController(aTemp, nRow, HELP_TEXT);
451 GoToRow(nRow);
452 // Update the Description-Window
453 GetView()->GetDescWin()->DisplayData(GetFieldDescr(nRow));
454 // redraw the row
455 RowModified(nRow);
457 // and re-enable edit mode
458 ActivateCell(nRow, GetCurColumnId());
461 void OTableEditorCtrl::CursorMoved()
463 // New line?
464 m_nDataPos = GetCurRow();
465 if( m_nDataPos != nOldDataPos && m_nDataPos != -1)
467 CellControllerRef aTemp;
468 InitController(aTemp,m_nDataPos,FIELD_NAME);
469 InitController(aTemp,m_nDataPos,FIELD_TYPE);
470 InitController(aTemp,m_nDataPos,COLUMN_DESCRIPTION);
471 InitController(aTemp,m_nDataPos,HELP_TEXT);
474 OTableRowView::CursorMoved();
477 sal_Int32 OTableEditorCtrl::HasFieldName( std::u16string_view rFieldName )
480 Reference<XConnection> xCon = GetView()->getController().getConnection();
481 Reference< XDatabaseMetaData> xMetaData = xCon.is() ? xCon->getMetaData() : Reference< XDatabaseMetaData>();
483 ::comphelper::UStringMixEqual bCase(!xMetaData.is() || xMetaData->supportsMixedCaseQuotedIdentifiers());
485 sal_Int32 nCount(0);
486 for (auto const& row : *m_pRowList)
488 OFieldDescription* pFieldDescr = row->GetActFieldDescr();
489 if( pFieldDescr && bCase(rFieldName,pFieldDescr->GetName()))
490 nCount++;
492 return nCount;
495 void OTableEditorCtrl::SaveData(sal_Int32 nRow, sal_uInt16 nColId)
497 // Store the cell content
498 SetDataPtr( nRow == -1 ? GetCurRow() : nRow);
499 OFieldDescription* pActFieldDescr = pActRow->GetActFieldDescr();
501 switch( nColId)
503 // Store NameCell
504 case FIELD_NAME:
506 // If there is no name, do nothing
507 weld::Entry& rEntry = pNameCell->get_widget();
508 const OUString aName(rEntry.get_text());
510 if( aName.isEmpty() )
512 // If FieldDescr exists, the field is deleted and the old content restored
513 if (pActFieldDescr)
515 GetUndoManager().AddUndoAction(std::make_unique<OTableEditorTypeSelUndoAct>(this, nRow, FIELD_TYPE, pActFieldDescr->getTypeInfo()));
516 SwitchType(TOTypeInfoSP());
517 pActFieldDescr = pActRow->GetActFieldDescr();
519 else
520 return;
522 if(pActFieldDescr)
523 pActFieldDescr->SetName( aName );
524 rEntry.save_value();
526 break;
529 // Store the field type
530 case FIELD_TYPE:
531 break;
533 // Store DescrCell
534 case HELP_TEXT:
536 // if the current field description is NULL, set Default
537 weld::Entry& rEntry = pHelpTextCell->get_widget();
538 if( !pActFieldDescr )
540 rEntry.set_text(OUString());
541 rEntry.save_value();
543 else
544 pActFieldDescr->SetHelpText(rEntry.get_text());
545 break;
547 case COLUMN_DESCRIPTION:
549 // Set the default if the field description is null
550 weld::Entry& rEntry = pDescrCell->get_widget();
551 if( !pActFieldDescr )
553 rEntry.set_text(OUString());
554 rEntry.save_value();
556 else
557 pActFieldDescr->SetDescription(rEntry.get_text());
558 break;
560 case FIELD_PROPERTY_DEFAULT:
561 case FIELD_PROPERTY_REQUIRED:
562 case FIELD_PROPERTY_TEXTLEN:
563 case FIELD_PROPERTY_NUMTYPE:
564 case FIELD_PROPERTY_AUTOINC:
565 case FIELD_PROPERTY_LENGTH:
566 case FIELD_PROPERTY_SCALE:
567 case FIELD_PROPERTY_BOOL_DEFAULT:
568 pDescrWin->SaveData(pActFieldDescr);
570 if ( FIELD_PROPERTY_AUTOINC == nColId && pActFieldDescr->IsAutoIncrement() )
572 OTableController& rController = GetView()->getController();
573 if ( rController.isAutoIncrementPrimaryKey() )
575 pActFieldDescr->SetPrimaryKey( true );
576 InvalidateHandleColumn();
577 Invalidate();
580 break;
584 bool OTableEditorCtrl::SaveModified()
586 sal_uInt16 nColId = GetCurColumnId();
588 switch( nColId )
590 // Field type
591 case FIELD_TYPE:
593 // Reset the type
594 resetType();
595 } break;
598 return true;
601 bool OTableEditorCtrl::CursorMoving(sal_Int32 nNewRow, sal_uInt16 nNewCol)
604 if (!EditBrowseBox::CursorMoving(nNewRow, nNewCol))
605 return false;
607 // Called after SaveModified(), current row is still the old one
608 m_nDataPos = nNewRow;
609 nOldDataPos = GetCurRow();
611 // Reset the markers
612 InvalidateStatusCell( nOldDataPos );
613 InvalidateStatusCell( m_nDataPos );
615 // Store the data from the Property window
616 if( SetDataPtr(nOldDataPos) && pDescrWin)
617 pDescrWin->SaveData( pActRow->GetActFieldDescr() );
619 // Show new data in the Property window
620 if( SetDataPtr(m_nDataPos) && pDescrWin)
621 pDescrWin->DisplayData( pActRow->GetActFieldDescr() );
623 return true;
626 IMPL_LINK_NOARG( OTableEditorCtrl, InvalidateFieldType, void*, void )
628 nInvalidateTypeEvent = nullptr;
629 Invalidate( GetFieldRectPixel(nOldDataPos, FIELD_TYPE) );
632 void OTableEditorCtrl::CellModified( sal_Int32 nRow, sal_uInt16 nColId )
635 // If the description is null, use the default
636 if(nRow == -1)
637 nRow = GetCurRow();
638 SetDataPtr( nRow );
639 OFieldDescription* pActFieldDescr = pActRow->GetActFieldDescr();
641 OUString sActionDescription;
642 switch ( nColId )
644 case FIELD_NAME: sActionDescription = DBA_RES( STR_CHANGE_COLUMN_NAME ); break;
645 case FIELD_TYPE: sActionDescription = DBA_RES( STR_CHANGE_COLUMN_TYPE ); break;
646 case HELP_TEXT:
647 case COLUMN_DESCRIPTION: sActionDescription = DBA_RES( STR_CHANGE_COLUMN_DESCRIPTION ); break;
648 default: sActionDescription = DBA_RES( STR_CHANGE_COLUMN_ATTRIBUTE ); break;
651 GetUndoManager().EnterListAction( sActionDescription, OUString(), 0, ViewShellId(-1) );
652 if (!pActFieldDescr)
654 const OTypeInfoMap& rTypeInfoMap = GetView()->getController().getTypeInfo();
655 if ( !rTypeInfoMap.empty() )
657 OTypeInfoMap::const_iterator aTypeIter = rTypeInfoMap.find(DataType::VARCHAR);
658 if ( aTypeIter == rTypeInfoMap.end() )
659 aTypeIter = rTypeInfoMap.begin();
660 pActRow->SetFieldType( aTypeIter->second );
662 else
663 pActRow->SetFieldType( GetView()->getController().getTypeInfoFallBack() );
665 nInvalidateTypeEvent = Application::PostUserEvent( LINK(this, OTableEditorCtrl, InvalidateFieldType), nullptr, true );
666 pActFieldDescr = pActRow->GetActFieldDescr();
667 pDescrWin->DisplayData( pActFieldDescr );
668 GetUndoManager().AddUndoAction( std::make_unique<OTableEditorTypeSelUndoAct>(this, nRow, nColId+1, TOTypeInfoSP()) );
671 if( nColId != FIELD_TYPE )
672 GetUndoManager().AddUndoAction( std::make_unique<OTableDesignCellUndoAct>(this, nRow, nColId) );
673 else
675 GetUndoManager().AddUndoAction(std::make_unique<OTableEditorTypeSelUndoAct>(this, GetCurRow(), nColId, GetFieldDescr(GetCurRow())->getTypeInfo()));
676 resetType();
679 SaveData(nRow,nColId);
680 // SaveData could create an undo action as well
681 GetUndoManager().LeaveListAction();
682 RowModified(nRow);
684 // Set the Modify flag
685 GetView()->getController().setModified( true );
686 InvalidateFeatures();
689 void OTableEditorCtrl::resetType()
691 sal_Int32 nPos = pTypeCell->get_widget().get_active();
692 if(nPos != -1)
693 SwitchType( GetView()->getController().getTypeInfo(nPos) );
694 else
695 SwitchType(TOTypeInfoSP());
698 void OTableEditorCtrl::CellModified()
700 CellModified( GetCurRow(), GetCurColumnId() );
703 void OTableEditorCtrl::InvalidateFeatures()
705 GetView()->getController().InvalidateFeature(SID_UNDO);
706 GetView()->getController().InvalidateFeature(SID_REDO);
707 GetView()->getController().InvalidateFeature(SID_SAVEDOC);
710 void OTableEditorCtrl::CopyRows()
712 // set to the right row and save it
713 if( SetDataPtr(m_nDataPos) )
714 pDescrWin->SaveData( pActRow->GetActFieldDescr() );
716 // Copy selected rows to the ClipboardList
717 std::shared_ptr<OTableRow> pClipboardRow;
718 std::shared_ptr<OTableRow> pRow;
719 std::vector< std::shared_ptr<OTableRow> > vClipboardList;
720 vClipboardList.reserve(GetSelectRowCount());
722 for( tools::Long nIndex=FirstSelectedRow(); nIndex != SFX_ENDOFSELECTION; nIndex=NextSelectedRow() )
724 pRow = (*m_pRowList)[nIndex];
725 OSL_ENSURE(pRow,"OTableEditorCtrl::CopyRows: Row is NULL!");
726 if ( pRow && pRow->GetActFieldDescr() )
728 pClipboardRow = std::make_shared<OTableRow>( *pRow );
729 vClipboardList.push_back( pClipboardRow);
732 if(!vClipboardList.empty())
734 rtl::Reference<OTableRowExchange> pData = new OTableRowExchange(std::move(vClipboardList));
735 pData->CopyToClipboard(GetParent());
739 OUString OTableEditorCtrl::GenerateName( const OUString& rName )
741 // Create a base name for appending numbers to
742 OUString aBaseName;
743 Reference<XConnection> xCon = GetView()->getController().getConnection();
744 Reference< XDatabaseMetaData> xMetaData = xCon.is() ? xCon->getMetaData() : Reference< XDatabaseMetaData>();
746 sal_Int32 nMaxTextLen(xMetaData.is() ? xMetaData->getMaxColumnNameLength() : 0);
748 if( (rName.getLength()+2) >nMaxTextLen )
749 aBaseName = rName.copy( 0, nMaxTextLen-2 );
750 else
751 aBaseName = rName;
753 // append a sequential number to the base name (up to 99)
754 OUString aFieldName( rName);
755 sal_Int32 i=1;
756 while( HasFieldName(aFieldName) )
758 aFieldName = aBaseName + OUString::number(i);
759 i++;
762 return aFieldName;
765 void OTableEditorCtrl::InsertRows( sal_Int32 nRow )
768 std::vector< std::shared_ptr<OTableRow> > vInsertedUndoRedoRows; // need for undo/redo handling
769 // get rows from clipboard
770 TransferableDataHelper aTransferData(TransferableDataHelper::CreateFromSystemClipboard(GetParent()));
771 if(aTransferData.HasFormat(SotClipboardFormatId::SBA_TABED))
773 std::unique_ptr<SvStream> aStreamRef = aTransferData.GetSotStorageStream(SotClipboardFormatId::SBA_TABED);
774 if (aStreamRef)
776 aStreamRef->Seek(STREAM_SEEK_TO_BEGIN);
777 aStreamRef->ResetError();
778 sal_Int32 nInsertRow = nRow;
779 std::shared_ptr<OTableRow> pRow;
780 sal_Int32 nSize = 0;
781 (*aStreamRef).ReadInt32( nSize );
782 vInsertedUndoRedoRows.reserve(nSize);
783 for(sal_Int32 i=0;i < nSize;++i)
785 pRow = std::make_shared<OTableRow>();
786 ReadOTableRow( *aStreamRef, *pRow );
787 pRow->SetReadOnly( false );
788 sal_Int32 nType = pRow->GetActFieldDescr()->GetType();
789 if ( pRow->GetActFieldDescr() )
790 pRow->GetActFieldDescr()->SetType(GetView()->getController().getTypeInfoByType(nType));
791 // Adjust the field names
792 pRow->GetActFieldDescr()->SetName( GenerateName( pRow->GetActFieldDescr()->GetName() ) );
793 pRow->SetPos(nInsertRow);
794 m_pRowList->insert( m_pRowList->begin()+nInsertRow,pRow );
795 vInsertedUndoRedoRows.push_back(std::make_shared<OTableRow>(*pRow));
796 nInsertRow++;
800 // RowInserted calls CursorMoved.
801 // The UI data should not be stored here.
802 RowInserted( nRow,vInsertedUndoRedoRows.size() );
804 // Create the Undo-Action
805 GetUndoManager().AddUndoAction( std::make_unique<OTableEditorInsUndoAct>(this, nRow, std::move(vInsertedUndoRedoRows)) );
806 GetView()->getController().setModified( true );
807 InvalidateFeatures();
810 void OTableEditorCtrl::DeleteRows()
812 OSL_ENSURE(GetView()->getController().isDropAllowed(),"Call of DeleteRows not valid here. Please check isDropAllowed!");
813 // Create the Undo-Action
814 GetUndoManager().AddUndoAction( std::make_unique<OTableEditorDelUndoAct>(this) );
816 // Delete all marked rows
817 tools::Long nIndex = FirstSelectedRow();
818 nOldDataPos = nIndex;
820 while( nIndex != SFX_ENDOFSELECTION )
822 // Remove rows
823 m_pRowList->erase( m_pRowList->begin()+nIndex );
824 RowRemoved( nIndex );
826 // Insert the empty row at the end
827 m_pRowList->push_back( std::make_shared<OTableRow>());
828 RowInserted( GetRowCount()-1 );
830 nIndex = FirstSelectedRow();
833 // Force the current record to be displayed
834 m_nDataPos = GetCurRow();
835 InvalidateStatusCell( nOldDataPos );
836 InvalidateStatusCell( m_nDataPos );
837 SetDataPtr( m_nDataPos );
838 ActivateCell();
839 pDescrWin->DisplayData( pActRow->GetActFieldDescr() );
840 GetView()->getController().setModified( true );
841 InvalidateFeatures();
844 void OTableEditorCtrl::InsertNewRows( sal_Int32 nRow )
846 OSL_ENSURE(GetView()->getController().isAddAllowed(),"Call of InsertNewRows not valid here. Please check isAppendAllowed!");
847 // Create Undo-Action
848 sal_Int32 nInsertRows = GetSelectRowCount();
849 if( !nInsertRows )
850 nInsertRows = 1;
851 GetUndoManager().AddUndoAction( std::make_unique<OTableEditorInsNewUndoAct>(this, nRow, nInsertRows) );
852 // Insert the number of selected rows
853 for( tools::Long i=nRow; i<(nRow+nInsertRows); i++ )
854 m_pRowList->insert( m_pRowList->begin()+i ,std::make_shared<OTableRow>());
855 RowInserted( nRow, nInsertRows );
857 GetView()->getController().setModified( true );
858 InvalidateFeatures();
861 void OTableEditorCtrl::SetControlText( sal_Int32 nRow, sal_uInt16 nColId, const OUString& rText )
863 // Set the Browser Controls
864 if( nColId < FIELD_FIRST_VIRTUAL_COLUMN )
866 GoToRow( nRow );
867 GoToColumnId( nColId );
868 CellControllerRef xController = Controller();
869 if(xController.is())
870 xController->GetWindow().SetText( rText );
871 else
872 RowModified(nRow,nColId);
875 // Set the Tabpage controls
876 else
878 pDescrWin->SetControlText( nColId, rText );
882 void OTableEditorCtrl::SetCellData( sal_Int32 nRow, sal_uInt16 nColId, const TOTypeInfoSP& _pTypeInfo )
884 // Relocate the current pointer
885 if( nRow == -1 )
886 nRow = GetCurRow();
887 OFieldDescription* pFieldDescr = GetFieldDescr( nRow );
888 if( !pFieldDescr && nColId != FIELD_TYPE)
889 return;
891 // Set individual fields
892 switch( nColId )
894 case FIELD_TYPE:
895 SwitchType( _pTypeInfo );
896 break;
897 default:
898 OSL_FAIL("OTableEditorCtrl::SetCellData: invalid column!");
900 SetControlText(nRow,nColId,_pTypeInfo ? _pTypeInfo->aUIName : OUString());
903 void OTableEditorCtrl::SetCellData( sal_Int32 nRow, sal_uInt16 nColId, const css::uno::Any& _rNewData )
905 // Relocate the current pointer
906 if( nRow == -1 )
907 nRow = GetCurRow();
908 OFieldDescription* pFieldDescr = GetFieldDescr( nRow );
909 if( !pFieldDescr && nColId != FIELD_TYPE)
910 return;
912 OUString sValue;
913 // Set individual fields
914 switch( nColId )
916 case FIELD_NAME:
917 sValue = ::comphelper::getString(_rNewData);
918 pFieldDescr->SetName( sValue );
919 break;
921 case FIELD_TYPE:
922 OSL_FAIL("OTableEditorCtrl::SetCellData: invalid column!");
923 break;
925 case COLUMN_DESCRIPTION:
926 sValue = ::comphelper::getString(_rNewData);
927 pFieldDescr->SetDescription( sValue );
928 break;
930 case FIELD_PROPERTY_DEFAULT:
931 pFieldDescr->SetControlDefault( _rNewData );
932 sValue = GetView()->GetDescWin()->getGenPage()->getControlDefault(pFieldDescr);
933 break;
935 case FIELD_PROPERTY_REQUIRED:
937 sValue = ::comphelper::getString(_rNewData);
938 pFieldDescr->SetIsNullable( sValue.toInt32() );
940 break;
942 case FIELD_PROPERTY_TEXTLEN:
943 case FIELD_PROPERTY_LENGTH:
945 sValue = ::comphelper::getString(_rNewData);
946 pFieldDescr->SetPrecision( sValue.toInt32() );
948 break;
950 case FIELD_PROPERTY_NUMTYPE:
951 OSL_FAIL("OTableEditorCtrl::SetCellData: invalid column!");
952 break;
954 case FIELD_PROPERTY_AUTOINC:
956 sValue = ::comphelper::getString(_rNewData);
957 pFieldDescr->SetAutoIncrement(sValue == DBA_RES(STR_VALUE_YES));
959 break;
960 case FIELD_PROPERTY_SCALE:
962 sValue = ::comphelper::getString(_rNewData);
963 pFieldDescr->SetScale(sValue.toInt32());
965 break;
967 case FIELD_PROPERTY_BOOL_DEFAULT:
968 sValue = GetView()->GetDescWin()->BoolStringPersistent(::comphelper::getString(_rNewData));
969 pFieldDescr->SetControlDefault(Any(sValue));
970 break;
972 case FIELD_PROPERTY_FORMAT:
974 sValue = ::comphelper::getString(_rNewData);
975 pFieldDescr->SetFormatKey(sValue.toInt32());
977 break;
980 SetControlText(nRow,nColId,sValue);
983 Any OTableEditorCtrl::GetCellData( sal_Int32 nRow, sal_uInt16 nColId )
985 OFieldDescription* pFieldDescr = GetFieldDescr( nRow );
986 if( !pFieldDescr )
987 return Any();
989 // Relocate the current pointer
990 if( nRow==-1 )
991 nRow = GetCurRow();
992 SetDataPtr( nRow );
994 static const OUString strYes(DBA_RES(STR_VALUE_YES));
995 static const OUString strNo(DBA_RES(STR_VALUE_NO));
996 OUString sValue;
997 // Read out the fields
998 switch( nColId )
1000 case FIELD_NAME:
1001 sValue = pFieldDescr->GetName();
1002 break;
1004 case FIELD_TYPE:
1005 if ( pFieldDescr->getTypeInfo() )
1006 sValue = pFieldDescr->getTypeInfo()->aUIName;
1007 break;
1009 case COLUMN_DESCRIPTION:
1010 sValue = pFieldDescr->GetDescription();
1011 break;
1012 case HELP_TEXT:
1013 sValue = pFieldDescr->GetHelpText();
1014 break;
1016 case FIELD_PROPERTY_DEFAULT:
1017 return pFieldDescr->GetControlDefault();
1019 case FIELD_PROPERTY_REQUIRED:
1020 sValue = pFieldDescr->GetIsNullable() == ColumnValue::NULLABLE ? strYes : strNo;
1021 break;
1023 case FIELD_PROPERTY_TEXTLEN:
1024 case FIELD_PROPERTY_LENGTH:
1025 sValue = OUString::number(pFieldDescr->GetPrecision());
1026 break;
1028 case FIELD_PROPERTY_NUMTYPE:
1029 OSL_FAIL("OTableEditorCtrl::GetCellData: invalid column!");
1030 break;
1032 case FIELD_PROPERTY_AUTOINC:
1033 sValue = pFieldDescr->IsAutoIncrement() ? strYes : strNo;
1034 break;
1036 case FIELD_PROPERTY_SCALE:
1037 sValue = OUString::number(pFieldDescr->GetScale());
1038 break;
1040 case FIELD_PROPERTY_BOOL_DEFAULT:
1041 sValue = GetView()->GetDescWin()->BoolStringUI(::comphelper::getString(pFieldDescr->GetControlDefault()));
1042 break;
1044 case FIELD_PROPERTY_FORMAT:
1045 sValue = OUString::number(pFieldDescr->GetFormatKey());
1046 break;
1049 return Any(sValue);
1052 OUString OTableEditorCtrl::GetCellText( sal_Int32 nRow, sal_uInt16 nColId ) const
1054 OUString sCellText;
1055 const_cast< OTableEditorCtrl* >( this )->GetCellData( nRow, nColId ) >>= sCellText;
1056 return sCellText;
1059 sal_uInt32 OTableEditorCtrl::GetTotalCellWidth(sal_Int32 nRow, sal_uInt16 nColId)
1061 return GetTextWidth(GetCellText(nRow, nColId)) + 2 * GetTextWidth(u"0"_ustr);
1064 OFieldDescription* OTableEditorCtrl::GetFieldDescr( sal_Int32 nRow )
1066 std::vector< std::shared_ptr<OTableRow> >::size_type nListCount(
1067 m_pRowList->size());
1068 if( (nRow<0) || (sal::static_int_cast< unsigned long >(nRow)>=nListCount) )
1070 OSL_FAIL("(nRow<0) || (nRow>=nListCount)");
1071 return nullptr;
1073 std::shared_ptr<OTableRow> pRow = (*m_pRowList)[ nRow ];
1074 if( !pRow )
1075 return nullptr;
1076 return pRow->GetActFieldDescr();
1079 bool OTableEditorCtrl::IsCutAllowed()
1081 bool bIsCutAllowed = (GetView()->getController().isAddAllowed() && GetView()->getController().isDropAllowed()) ||
1082 GetView()->getController().isAlterAllowed();
1084 if (bIsCutAllowed)
1086 int nStartPos, nEndPos;
1087 switch(m_eChildFocus)
1089 case DESCRIPTION:
1091 weld::Entry& rEntry = pDescrCell->get_widget();
1092 bIsCutAllowed = rEntry.get_selection_bounds(nStartPos, nEndPos);
1093 break;
1095 case HELPTEXT:
1097 weld::Entry& rEntry = pHelpTextCell->get_widget();
1098 bIsCutAllowed = rEntry.get_selection_bounds(nStartPos, nEndPos);
1099 break;
1101 case NAME:
1103 weld::Entry& rEntry = pNameCell->get_widget();
1104 bIsCutAllowed = rEntry.get_selection_bounds(nStartPos, nEndPos);
1105 break;
1107 case ROW:
1108 bIsCutAllowed = IsCopyAllowed();
1109 break;
1110 default:
1111 bIsCutAllowed = false;
1112 break;
1116 return bIsCutAllowed;
1119 bool OTableEditorCtrl::IsCopyAllowed()
1121 bool bIsCopyAllowed = false;
1122 int nStartPos, nEndPos;
1123 if (m_eChildFocus == DESCRIPTION )
1125 weld::Entry& rEntry = pDescrCell->get_widget();
1126 bIsCopyAllowed = rEntry.get_selection_bounds(nStartPos, nEndPos);
1128 else if(HELPTEXT == m_eChildFocus )
1130 weld::Entry& rEntry = pHelpTextCell->get_widget();
1131 bIsCopyAllowed = rEntry.get_selection_bounds(nStartPos, nEndPos);
1133 else if(m_eChildFocus == NAME)
1135 weld::Entry& rEntry = pNameCell->get_widget();
1136 bIsCopyAllowed = rEntry.get_selection_bounds(nStartPos, nEndPos);
1138 else if(m_eChildFocus == ROW)
1140 Reference<XPropertySet> xTable = GetView()->getController().getTable();
1141 if( !GetSelectRowCount() || (xTable.is() && ::comphelper::getString(xTable->getPropertyValue(PROPERTY_TYPE)) == "VIEW"))
1142 return false;
1144 // If one of the selected rows is empty, Copy is not possible
1145 std::shared_ptr<OTableRow> pRow;
1146 tools::Long nIndex = FirstSelectedRow();
1147 while( nIndex != SFX_ENDOFSELECTION )
1149 pRow = (*m_pRowList)[nIndex];
1150 if( !pRow->GetActFieldDescr() )
1151 return false;
1153 nIndex = NextSelectedRow();
1156 bIsCopyAllowed = true;
1159 return bIsCopyAllowed;
1162 bool OTableEditorCtrl::IsPasteAllowed() const
1164 bool bAllowed = GetView()->getController().isAddAllowed();
1165 if ( bAllowed )
1167 TransferableDataHelper aTransferData(TransferableDataHelper::CreateFromSystemClipboard(GetParent()));
1168 bool bRowFormat = aTransferData.HasFormat(SotClipboardFormatId::SBA_TABED);
1169 if ( m_eChildFocus == ROW )
1170 bAllowed = bRowFormat;
1171 else
1172 bAllowed = !bRowFormat && aTransferData.HasFormat(SotClipboardFormatId::STRING);
1175 return bAllowed;
1178 void OTableEditorCtrl::cut()
1180 if(m_eChildFocus == NAME)
1182 if(GetView()->getController().isAlterAllowed())
1184 SaveData(-1,FIELD_NAME);
1185 pNameCell->get_widget().cut_clipboard();
1186 CellModified(-1,FIELD_NAME);
1189 else if(m_eChildFocus == DESCRIPTION)
1191 if(GetView()->getController().isAlterAllowed())
1193 SaveData(-1,COLUMN_DESCRIPTION);
1194 pDescrCell->get_widget().cut_clipboard();
1195 CellModified(-1,COLUMN_DESCRIPTION);
1198 else if(HELPTEXT == m_eChildFocus )
1200 if(GetView()->getController().isAlterAllowed())
1202 SaveData(-1,HELP_TEXT);
1203 pHelpTextCell->get_widget().cut_clipboard();
1204 CellModified(-1,HELP_TEXT);
1207 else if(m_eChildFocus == ROW)
1209 if (nCutEvent)
1210 Application::RemoveUserEvent(nCutEvent);
1211 nCutEvent = Application::PostUserEvent(LINK(this, OTableEditorCtrl, DelayedCut), nullptr, true);
1215 void OTableEditorCtrl::copy()
1217 if (GetSelectRowCount())
1218 OTableRowView::copy();
1219 else if(m_eChildFocus == NAME)
1221 weld::Entry& rEntry = pNameCell->get_widget();
1222 rEntry.copy_clipboard();
1224 else if(HELPTEXT == m_eChildFocus )
1226 weld::Entry& rEntry = pHelpTextCell->get_widget();
1227 rEntry.copy_clipboard();
1229 else if(m_eChildFocus == DESCRIPTION )
1231 weld::Entry& rEntry = pDescrCell->get_widget();
1232 rEntry.copy_clipboard();
1236 void OTableEditorCtrl::paste()
1238 TransferableDataHelper aTransferData(TransferableDataHelper::CreateFromSystemClipboard(GetParent()));
1239 if(aTransferData.HasFormat(SotClipboardFormatId::SBA_TABED))
1241 if( nPasteEvent )
1242 Application::RemoveUserEvent( nPasteEvent );
1243 nPasteEvent = Application::PostUserEvent( LINK(this, OTableEditorCtrl, DelayedPaste), nullptr, true );
1245 else if(m_eChildFocus == NAME)
1247 if(GetView()->getController().isAlterAllowed())
1249 pNameCell->get_widget().paste_clipboard();
1250 CellModified();
1253 else if(HELPTEXT == m_eChildFocus )
1255 if(GetView()->getController().isAlterAllowed())
1257 pHelpTextCell->get_widget().paste_clipboard();
1258 CellModified();
1261 else if(m_eChildFocus == DESCRIPTION)
1263 if(GetView()->getController().isAlterAllowed())
1265 pDescrCell->get_widget().paste_clipboard();
1266 CellModified();
1271 bool OTableEditorCtrl::IsDeleteAllowed()
1274 return GetSelectRowCount() != 0 && GetView()->getController().isDropAllowed();
1277 bool OTableEditorCtrl::IsInsertNewAllowed( sal_Int32 nRow )
1280 bool bInsertNewAllowed = GetView()->getController().isAddAllowed();
1281 // If fields can be added, Paste in the new fields
1282 if (bInsertNewAllowed && !GetView()->getController().isDropAllowed())
1284 SetDataPtr(nRow);
1285 if( GetActRow()->IsReadOnly() )
1286 return false;
1289 return bInsertNewAllowed;
1292 bool OTableEditorCtrl::IsPrimaryKeyAllowed()
1294 if( !GetSelectRowCount() )
1295 return false;
1297 OTableController& rController = GetView()->getController();
1298 if ( !rController.getSdbMetaData().supportsPrimaryKeys() )
1299 return false;
1301 Reference<XPropertySet> xTable = rController.getTable();
1302 // Key must not be changed
1303 // This applies only if the table is not new and not a css::sdbcx::View. Otherwise no DROP is executed
1305 if(xTable.is() && ::comphelper::getString(xTable->getPropertyValue(PROPERTY_TYPE)) == "VIEW")
1306 return false;
1307 // If there is an empty field, no primary key
1308 // The entry is only permitted if
1309 // - there are no empty entries in the selection
1310 // - No Memo or Image entries
1311 // - DROP is not permitted (see above) and the column is not Required (not null flag is not set).
1312 tools::Long nIndex = FirstSelectedRow();
1313 std::shared_ptr<OTableRow> pRow;
1314 while( nIndex != SFX_ENDOFSELECTION )
1316 pRow = (*m_pRowList)[nIndex];
1317 OFieldDescription* pFieldDescr = pRow->GetActFieldDescr();
1318 if(!pFieldDescr)
1319 return false;
1320 else
1322 // Memo and Image fields cannot be primary keys
1323 // or if the column cannot be dropped and the Required flag is not set
1324 // or if a css::sdbcx::View is available and the Required flag is not set
1325 const TOTypeInfoSP& pTypeInfo = pFieldDescr->getTypeInfo();
1326 if( pTypeInfo->nSearchType == ColumnSearch::NONE
1327 || (pFieldDescr->IsNullable() && pRow->IsReadOnly())
1329 return false;
1332 nIndex = NextSelectedRow();
1335 return true;
1338 void OTableEditorCtrl::Command(const CommandEvent& rEvt)
1340 switch (rEvt.GetCommand())
1342 case CommandEventId::ContextMenu:
1344 Point aMenuPos( rEvt.GetMousePosPixel() );
1345 if (!rEvt.IsMouseEvent())
1347 if ( 1 == GetSelectColumnCount() )
1349 sal_uInt16 nSelId = GetColumnId(
1350 sal::static_int_cast< sal_uInt16 >(
1351 FirstSelectedColumn() ) );
1352 ::tools::Rectangle aColRect( GetFieldRectPixel( 0, nSelId, false ) );
1354 aMenuPos = aColRect.TopCenter();
1356 else if ( GetSelectRowCount() > 0 )
1358 ::tools::Rectangle aColRect( GetFieldRectPixel( FirstSelectedRow(), HANDLE_ID ) );
1360 aMenuPos = aColRect.TopCenter();
1362 else
1364 OTableRowView::Command(rEvt);
1365 return;
1369 // Show the Context menu
1370 if( !IsReadOnly() )
1372 sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(aMenuPos.X()));
1373 sal_Int32 nRow = GetRowAtYPosPixel(aMenuPos.Y());
1375 if ( HANDLE_ID != nColId )
1377 if ( nRow < 0 && nColId != BROWSER_INVALIDID )
1378 { // hit the header
1379 if ( 3 != nColId )
1380 { // 3 would mean the last column, and this last column is auto-sized
1381 if ( !IsColumnSelected( nColId ) )
1382 SelectColumnId( nColId );
1384 ::tools::Rectangle aRect(aMenuPos, Size(1, 1));
1385 weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
1386 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, u"dbaccess/ui/querycolmenu.ui"_ustr));
1387 std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr));
1388 xContextMenu->remove(u"delete"_ustr);
1389 xContextMenu->remove(u"separator"_ustr);
1390 if (xContextMenu->popup_at_rect(pPopupParent, aRect) == "width")
1391 adjustBrowseBoxColumnWidth( this, nColId );
1395 else
1397 ::tools::Rectangle aRect(aMenuPos, Size(1, 1));
1398 weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
1399 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, u"dbaccess/ui/tabledesignrowmenu.ui"_ustr));
1400 std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr));
1402 if (!IsCutAllowed())
1403 xContextMenu->remove(u"cut"_ustr);
1404 if (!IsCopyAllowed())
1405 xContextMenu->remove(u"copy"_ustr);
1406 if (!IsPasteAllowed())
1407 xContextMenu->remove(u"paste"_ustr);
1408 if (!IsDeleteAllowed())
1409 xContextMenu->remove(u"delete"_ustr);
1410 // tdf#71224: WORKAROUND for the moment, we don't implement insert field at specific position
1411 // It's not SQL standard and each database has made its choice (some use "BEFORE", other "FIRST" and "AFTER")
1412 // and some, like Postgresql, don't allow this.
1413 // So for the moment, test if the table already exists (and so it's an edition), in this case only
1414 // we remove "Insert Fields" entry. Indeed, in case of new table, there's no pb.
1416 // The real fix is to implement the insert for each database + error message for those which don't support this
1417 //if (!IsInsertNewAllowed(nRow))
1418 if ( GetView()->getController().getTable().is() )
1419 xContextMenu->remove(u"insert"_ustr);
1421 if (IsPrimaryKeyAllowed())
1423 xContextMenu->set_active(u"primarykey"_ustr, IsRowSelected(GetCurRow()) && IsPrimaryKey());
1425 else
1427 xContextMenu->remove(u"primarykey"_ustr);
1430 if( SetDataPtr(m_nDataPos) )
1431 pDescrWin->SaveData( pActRow->GetActFieldDescr() );
1433 // All actions which change the number of rows must be run asynchronously
1434 // otherwise there may be problems between the Context menu and the Browser
1435 m_nDataPos = GetCurRow();
1436 OUString sIdent = xContextMenu->popup_at_rect(pPopupParent, aRect);
1437 if (sIdent == "cut")
1438 cut();
1439 else if (sIdent == "copy")
1440 copy();
1441 else if (sIdent == "paste")
1442 paste();
1443 else if (sIdent == "delete")
1445 if( nDeleteEvent )
1446 Application::RemoveUserEvent( nDeleteEvent );
1447 nDeleteEvent = Application::PostUserEvent( LINK(this, OTableEditorCtrl, DelayedDelete), nullptr, true );
1449 else if (sIdent == "insert")
1451 if( nInsNewRowsEvent )
1452 Application::RemoveUserEvent( nInsNewRowsEvent );
1453 nInsNewRowsEvent = Application::PostUserEvent( LINK(this, OTableEditorCtrl, DelayedInsNewRows), nullptr, true );
1455 else if (sIdent == "primarykey")
1457 SetPrimaryKey( !IsPrimaryKey() );
1462 break;
1463 default:
1464 OTableRowView::Command(rEvt);
1469 IMPL_LINK_NOARG( OTableEditorCtrl, DelayedCut, void*, void )
1471 nCutEvent = nullptr;
1472 OTableRowView::cut();
1475 IMPL_LINK_NOARG( OTableEditorCtrl, DelayedPaste, void*, void )
1477 nPasteEvent = nullptr;
1479 sal_Int32 nPastePosition = GetView()->getController().getFirstEmptyRowPosition();
1480 if ( !GetView()->getController().getTable().is() )
1481 nPastePosition = GetSelectRowCount() ? FirstSelectedRow() : GetCurRow();
1483 if (!IsInsertNewAllowed(nPastePosition))
1484 { // Insertion is not allowed, only appending, so test if there are full cells after the PastePosition
1486 auto aIter = std::find_if(m_pRowList->rbegin(), m_pRowList->rend(), [](const std::shared_ptr<OTableRow>& rxRow) {
1487 return rxRow && rxRow->GetActFieldDescr() && !rxRow->GetActFieldDescr()->GetName().isEmpty(); });
1488 auto nFreeFromPos = static_cast<sal_Int32>(std::distance(aIter, m_pRowList->rend())); // from here on there are only empty rows
1489 if (nPastePosition < nFreeFromPos) // if at least one PastePosition is full, go right to the end
1490 nPastePosition = nFreeFromPos;
1493 OTableRowView::Paste( nPastePosition );
1494 SetNoSelection();
1495 GoToRow( nPastePosition );
1498 IMPL_LINK_NOARG( OTableEditorCtrl, DelayedDelete, void*, void )
1500 nDeleteEvent = nullptr;
1501 DeleteRows();
1504 IMPL_LINK_NOARG( OTableEditorCtrl, DelayedInsNewRows, void*, void )
1506 nInsNewRowsEvent = nullptr;
1507 sal_Int32 nPastePosition = GetView()->getController().getFirstEmptyRowPosition();
1508 if ( !GetView()->getController().getTable().is() )
1509 nPastePosition = GetSelectRowCount() ? FirstSelectedRow() : m_nDataPos;
1511 InsertNewRows( nPastePosition );
1512 SetNoSelection();
1513 GoToRow( nPastePosition );
1516 void OTableEditorCtrl::AdjustFieldDescription(OFieldDescription* _pFieldDesc,
1517 MultiSelection& _rMultiSel,
1518 sal_Int32 _nPos,
1519 bool _bSet,
1520 bool _bPrimaryKey)
1522 _pFieldDesc->SetPrimaryKey( _bPrimaryKey );
1523 if(!_bSet && _pFieldDesc->getTypeInfo()->bNullable)
1525 _pFieldDesc->SetIsNullable(ColumnValue::NO_NULLS);
1526 _pFieldDesc->SetControlDefault(Any());
1528 if ( _pFieldDesc->IsAutoIncrement() && !_bPrimaryKey )
1530 OTableController& rController = GetView()->getController();
1531 if ( rController.isAutoIncrementPrimaryKey() )
1533 _pFieldDesc->SetAutoIncrement(false);
1536 // update field description
1537 pDescrWin->DisplayData(_pFieldDesc);
1539 _rMultiSel.Insert( _nPos );
1540 _rMultiSel.Select( _nPos );
1543 void OTableEditorCtrl::SetPrimaryKey( bool bSet )
1545 // Delete any existing Primary Keys
1546 MultiSelection aDeletedPrimKeys;
1547 aDeletedPrimKeys.SetTotalRange( Range(0,GetRowCount()) );
1549 sal_Int32 nRow = 0;
1550 for (auto const& row : *m_pRowList)
1552 OFieldDescription* pFieldDescr = row->GetActFieldDescr();
1553 if( pFieldDescr && row->IsPrimaryKey() && (!bSet || !IsRowSelected(nRow)) )
1555 AdjustFieldDescription(pFieldDescr,aDeletedPrimKeys,nRow,bSet,false);
1557 ++nRow;
1560 // Set the primary keys of the marked rows
1561 MultiSelection aInsertedPrimKeys;
1562 aInsertedPrimKeys.SetTotalRange( Range(0,GetRowCount()) );
1563 if( bSet )
1565 tools::Long nIndex = FirstSelectedRow();
1566 while( nIndex != SFX_ENDOFSELECTION )
1568 // Set the key
1569 std::shared_ptr<OTableRow> pRow = (*m_pRowList)[nIndex];
1570 OFieldDescription* pFieldDescr = pRow->GetActFieldDescr();
1571 if(pFieldDescr)
1572 AdjustFieldDescription(pFieldDescr,aInsertedPrimKeys,nIndex,false,true);
1574 nIndex = NextSelectedRow();
1578 GetUndoManager().AddUndoAction( std::make_unique<OPrimKeyUndoAct>(this, aDeletedPrimKeys, aInsertedPrimKeys) );
1580 // Invalidate the handle-columns
1581 InvalidateHandleColumn();
1583 // Set the TableDocSh's ModifyFlag
1584 GetView()->getController().setModified( true );
1585 InvalidateFeatures();
1588 bool OTableEditorCtrl::IsPrimaryKey()
1590 // Are all marked fields part of the Primary Key ?
1591 tools::Long nPrimaryKeys = 0;
1592 sal_Int32 nRow=0;
1593 for (auto const& row : *m_pRowList)
1595 if( IsRowSelected(nRow) && !row->IsPrimaryKey() )
1596 return false;
1597 if( row->IsPrimaryKey() )
1598 ++nPrimaryKeys;
1599 ++nRow;
1602 // Are there any unselected fields that are part of the Key ?
1603 return GetSelectRowCount() == nPrimaryKeys;
1606 void OTableEditorCtrl::SwitchType( const TOTypeInfoSP& _pType )
1608 // if there is no assigned field name
1609 sal_Int32 nRow(GetCurRow());
1610 OFieldDescription* pActFieldDescr = GetFieldDescr( nRow );
1611 if( pActFieldDescr )
1612 // Store the old description
1613 pDescrWin->SaveData( pActFieldDescr );
1615 if ( nRow < 0 || o3tl::make_unsigned(nRow) > m_pRowList->size() )
1616 return;
1617 // Show the new description
1618 std::shared_ptr<OTableRow> pRow = (*m_pRowList)[nRow];
1619 pRow->SetFieldType( _pType, true );
1620 if ( _pType )
1622 weld::ComboBox& rTypeList = pTypeCell->get_widget();
1623 const sal_Int32 nCurrentlySelected = rTypeList.get_active();
1625 if ( ( nCurrentlySelected == -1 )
1626 || ( GetView()->getController().getTypeInfo( nCurrentlySelected ) != _pType )
1629 sal_Int32 nEntryPos = 0;
1630 const OTypeInfoMap& rTypeInfo = GetView()->getController().getTypeInfo();
1631 for (auto const& elem : rTypeInfo)
1633 if(elem.second == _pType)
1634 break;
1635 ++nEntryPos;
1637 if (nEntryPos < rTypeList.get_count())
1638 rTypeList.set_active(nEntryPos);
1642 pActFieldDescr = pRow->GetActFieldDescr();
1643 if (pActFieldDescr != nullptr && !pActFieldDescr->GetFormatKey())
1645 sal_Int32 nFormatKey = ::dbtools::getDefaultNumberFormat( pActFieldDescr->GetType(),
1646 pActFieldDescr->GetScale(),
1647 pActFieldDescr->IsCurrency(),
1648 Reference< XNumberFormatTypes>(GetView()->getController().getNumberFormatter()->getNumberFormatsSupplier()->getNumberFormats(),UNO_QUERY),
1649 GetView()->getLocale());
1651 pActFieldDescr->SetFormatKey(nFormatKey);
1654 pDescrWin->DisplayData( pActFieldDescr );
1657 OTableDesignView* OTableEditorCtrl::GetView() const
1659 return m_pView;
1662 void OTableEditorCtrl::DeactivateCell(bool bUpdate)
1664 OTableRowView::DeactivateCell(bUpdate);
1665 // now we have to deactivate the field description
1666 sal_Int32 nRow(GetCurRow());
1667 if (pDescrWin)
1668 pDescrWin->SetReadOnly(bReadOnly || !SetDataPtr(nRow) || GetActRow()->IsReadOnly());
1671 bool OTableEditorCtrl::PreNotify( NotifyEvent& rNEvt )
1673 if (rNEvt.GetType() == NotifyEventType::GETFOCUS)
1675 if( pHelpTextCell && pHelpTextCell->HasChildPathFocus() )
1676 m_eChildFocus = HELPTEXT;
1677 else if( pDescrCell && pDescrCell->HasChildPathFocus() )
1678 m_eChildFocus = DESCRIPTION;
1679 else if(pNameCell && pNameCell->HasChildPathFocus() )
1680 m_eChildFocus = NAME;
1681 else
1682 m_eChildFocus = ROW;
1685 return OTableRowView::PreNotify(rNEvt);
1688 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */