/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
IMPL_LINK_NOARG( SbaXGridPeer, OnDispatchEvent, void*, void )
{
VclPtr< SbaGridControl > pGrid = GetAs< SbaGridControl >(); if ( !pGrid ) // if this fails, we were disposing before arriving here return;
if ( !Application::IsMainThread() )
{ // still not in the main thread (see SbaXGridPeer::dispatch). post an event, again // without moving the special even to the back of the queue
pGrid->PostUserEvent( LINK( this, SbaXGridPeer, OnDispatchEvent ) );
} else
{
DispatchArgs aArgs = m_aDispatchArgs.front();
m_aDispatchArgs.pop();
if ( !Application::IsMainThread() )
{ // we're not in the main thread. This is bad, as we want to raise windows here, // and VCL does not like windows to be opened in non-main threads (at least on Win32). // Okay, do this async. No problem with this, as XDispatch::dispatch is defined to be // a one-way method.
// save the args
DispatchArgs aDispatchArgs;
aDispatchArgs.aURL = aURL;
aDispatchArgs.aArgs = aArgs;
m_aDispatchArgs.push( aDispatchArgs );
// post an event // we use the Window::PostUserEvent here, instead of the application::PostUserEvent // this saves us from keeping track of these events - as soon as the window dies, // the events are deleted automatically. For the application way, we would need to // do this ourself. // As we use our grid as window, and the grid dies before we die, this should be no problem.
pGrid->PostUserEvent( LINK( this, SbaXGridPeer, OnDispatchEvent ) ); return;
}
// notify any status listeners that the dialog is now active (well, about to be active)
MapDispatchToBool::const_iterator aThisURLState = m_aDispatchStates.emplace( eURLType, true ).first;
NotifyStatusChanged( aURL, nullptr );
// execute the dialog switch ( eURLType )
{ case dtBrowserAttribs:
pGrid->SetBrowserAttrs(); break;
case dtRowHeight:
pGrid->SetRowHeight(); break;
case dtColumnAttribs:
{
OSL_ENSURE(nColId != -1, "SbaXGridPeer::dispatch : invalid parameter !"); if (nColId != -1) break;
pGrid->SetColAttrs(nColId);
} break;
case dtColumnWidth:
{
OSL_ENSURE(nColId != -1, "SbaXGridPeer::dispatch : invalid parameter !"); if (nColId != -1) break;
pGrid->SetColWidth(nColId);
} break;
case dtUnknown: break;
}
// notify any status listeners that the dialog vanished
m_aDispatchStates.erase( aThisURLState );
NotifyStatusChanged( aURL, nullptr );
}
void SbaGridHeader::StartDrag( sal_Int8 _nAction, const Point& _rPosPixel )
{
SolarMutexGuard aGuard; // in the new DnD API, the solar mutex is not locked when StartDrag is called
ImplStartColumnDrag( _nAction, _rPosPixel );
}
void SbaGridHeader::MouseButtonDown( const MouseEvent& _rMEvt )
{ if (_rMEvt.IsLeft()) if (_rMEvt.GetClicks() != 2)
{ // the base class will start a column move here, which we don't want to allow // (at the moment. If we store relative positions with the columns, we can allow column moves...)
}
FmGridHeader::MouseButtonDown(_rMEvt);
}
void SbaGridHeader::ImplStartColumnDrag(sal_Int8 _nAction, const Point& _rMousePos)
{
sal_uInt16 nId = GetItemId(_rMousePos); bool bResizingCol = false; if (HEADERBAR_ITEM_NOTFOUND != nId)
{
tools::Rectangle aColRect = GetItemRect(nId);
aColRect.AdjustLeft(nId ? 3 : 0 ); // the handle col (nId == 0) does not have a left margin for resizing
aColRect.AdjustRight( -3 );
bResizingCol = !aColRect.Contains(_rMousePos);
} if (bResizingCol) return;
// force the base class to end its drag mode
EndTracking(TrackingEventFlags::Cancel | TrackingEventFlags::End);
// because we have 3d-buttons the select handler is called from MouseButtonUp, but StartDrag // occurs earlier (while the mouse button is down) // so for optical reasons we select the column before really starting the drag operation.
notifyColumnSelect(nId);
static_cast<SbaGridControl*>(GetParent())->StartDrag(_nAction,
Point(
_rMousePos.X() + GetPosPixel().X(), // we aren't left-justified with our parent, in contrast to the data window
_rMousePos.Y() - GetSizePixel().Height()
)
);
}
if ( xField.is() )
{ switch( ::comphelper::getINT32(xField->getPropertyValue(PROPERTY_TYPE)) )
{ case DataType::BINARY: case DataType::VARBINARY: case DataType::LONGVARBINARY: case DataType::SQLNULL: case DataType::OBJECT: case DataType::BLOB: case DataType::CLOB: case DataType::REF: break; default:
rMenu.insert(nPos++, u"colattrset"_ustr, DBA_RES(RID_STR_COLUMN_FORMAT),
nullptr, nullptr, nullptr, TRISTATE_INDET);
rMenu.insert_separator(nPos++, u"separator1"_ustr);
}
}
// get the (UNO) column model
Reference< XIndexAccess > xCols = GetPeer()->getColumns();
Reference< XPropertySet > xAffectedCol; if (xCols.is() && (nModelPos != sal_uInt16(-1)))
xAffectedCol.set(xCols->getByIndex(nModelPos), css::uno::UNO_QUERY);
// get the field the column is bound to
Reference< XPropertySet > xField = getField(nModelPos);
::dbaui::callColumnFormatDialog(xAffectedCol,xField,pFormatter,GetFrameWeld());
}
try
{ // the db is the implemented by the parent of the grid control's model ...
Reference< XChild > xColumns(GetPeer()->getColumns(), UNO_QUERY); if (xColumns.is())
{
Reference< XRowSet > xDataSource(xColumns->getParent(), UNO_QUERY);
::dbtools::ensureRowSetConnection( xDataSource, getContext(), nullptr );
Reference< XChild > xConn(::dbtools::getConnection(xDataSource),UNO_QUERY); if (xConn.is())
{ // ... and the RO-flag simply is implemented by a property
Reference< XPropertySet > xDbProps(xConn->getParent(), UNO_QUERY); if (xDbProps.is())
{
Reference< XPropertySetInfo > xInfo = xDbProps->getPropertySetInfo(); if (xInfo->hasPropertyByName(PROPERTY_ISREADONLY))
bDBIsReadOnly = ::comphelper::getBOOL(xDbProps->getPropertyValue(PROPERTY_ISREADONLY));
}
}
}
} catch (const Exception&)
{
TOOLS_WARN_EXCEPTION("dbaccess", "SbaGridControl::IsReadOnlyDB Exception occurred");
}
return bDBIsReadOnly;
}
void SbaGridControl::MouseButtonDown( const BrowserMouseEvent& rMEvt)
{
sal_Int32 nRow = GetRowAtYPosPixel(rMEvt.GetPosPixel().Y());
sal_uInt16 nColPos = GetColumnAtXPosPixel(rMEvt.GetPosPixel().X());
sal_uInt16 nViewPos = (nColPos == BROWSER_INVALIDID) ? sal_uInt16(-1) : sal_uInt16(nColPos - 1); // 'the handle column' and 'no valid column' will both result in a view position of -1 !
void SbaGridControl::StartDrag( sal_Int8 _nAction, const Point& _rPosPixel )
{
SolarMutexGuard aGuard; // in the new DnD API, the solar mutex is not locked when StartDrag is called
bool bHandled = false;
do
{ // determine if dragging is allowed // (Yes, this is controller (not view) functionality. But collecting and evaluating all the // information necessary via UNO would be quite difficult (if not impossible) so // my laziness says 'do it here'...)
sal_Int32 nRow = GetRowAtYPosPixel(_rPosPixel.Y());
sal_uInt16 nColPos = GetColumnAtXPosPixel(_rPosPixel.X());
sal_uInt16 nViewPos = (nColPos == BROWSER_INVALIDID) ? sal_uInt16(-1) : sal_uInt16(nColPos-1); // 'the handle column' and 'no valid column' will both result in a view position of -1 !
bool bCurrentRowVirtual = IsCurrentAppending() && IsModified(); // the current row doesn't really exist: the user's appending a new one and already has entered some data, // so the row contains data which has no counter part within the data source
sal_Int32 nCorrectRowCount = GetRowCount(); if (GetOptions() & DbGridControlOptions::Insert)
--nCorrectRowCount; // there is an empty row for inserting records if (bCurrentRowVirtual)
--nCorrectRowCount;
if ((nColPos == BROWSER_INVALIDID) || (nRow >= nCorrectRowCount)) break;
bool bHitHandle = (nColPos == 0);
// check which kind of dragging has to be initiated if ( bHitHandle // the handle column // AND
&& ( GetSelectRowCount() // at least one row is selected // OR
|| ( (nRow >= 0) // a row below the header
&& !bCurrentRowVirtual // we aren't appending a new record
&& (nRow != GetCurrentPos()) // a row which is not the current one
) // OR
|| ( (0 == GetSelectRowCount()) // no rows selected
&& (-1 == nRow) // hit the header
)
)
)
{ // => start dragging the row if (GetDataWindow().IsMouseCaptured())
GetDataWindow().ReleaseMouse();
if (0 == GetSelectRowCount()) // no rows selected, but here in this branch // -> the user started dragging the upper left corner, which symbolizes the whole table
SelectAll();
if ( _bTrueIfClipboardFalseIfDrag )
pTransfer->CopyToClipboard( this ); else
pTransfer->StartDrag(this, DND_ACTION_COPY | DND_ACTION_LINK);
} catch(Exception&)
{
}
}
void SbaGridControl::DoFieldDrag(sal_uInt16 nColumnPos, sal_Int16 nRowPos)
{ // the only thing to do here is dragging the pure cell text // the old implementation copied a SBA_FIELDDATAEXCHANGE_FORMAT, too, (which was rather expensive to obtain), // but we have no client for this DnD format anymore (the mail part of SO 5.2 was the only client)
sal_Int32 nCorrectRowCount = GetRowCount(); if (GetOptions() & DbGridControlOptions::Insert)
--nCorrectRowCount; // there is an empty row for inserting records if (IsCurrentAppending())
--nCorrectRowCount; // the current data record doesn't really exist, we are appending a new one
if ( (nCol == BROWSER_INVALIDID) || (nRow >= nCorrectRowCount) || (nCol == 0) ) // no valid cell under the mouse cursor break;
tools::Rectangle aRect = GetCellRect(nRow, nCol, false); if (!aRect.Contains(rEvt.maPosPixel)) // not dropped within a cell (a cell isn't as wide as the column - the are small spaces) break;
if ((IsModified() || (GetCurrentRow().is() && GetCurrentRow()->IsModified())) && (GetCurrentPos() != nRow)) // there is a current and modified row or cell and he text is to be dropped into another one break;
CellControllerRef xCurrentController = Controller(); if (xCurrentController.is() && xCurrentController->IsValueChangedFromSaved() && ((nRow != GetCurRow()) || (nCol != GetCurColumnId()))) // the current controller is modified and the user wants to drop in another cell -> no chance // (when leaving the modified cell an error may occur - this is deadly while dragging) break;
Reference< XPropertySet > xField = getField(GetModelColumnPos(nCol)); if (!xField.is()) // the column is not valid bound (for instance a binary field) break;
try
{ // assume that text can be dropped into a field if the column has a css::awt::XTextComponent interface
Reference< XIndexAccess > xColumnControls(GetPeer()); if (xColumnControls.is())
{
Reference< css::awt::XTextComponent > xColControl(
xColumnControls->getByIndex(GetViewColumnPos(nCol)),
css::uno::UNO_QUERY); if (xColControl.is())
{
m_bActivatingForDrop = true;
GoToRowColumnId(nRow, nCol);
m_bActivatingForDrop = false;
sal_Int8 SbaGridControl::ExecuteDrop( const BrowserExecuteDropEvent& rEvt )
{ // we need some properties of our data source
Reference< XPropertySet > xDataSource = getDataSource(); if (!xDataSource.is()) return DND_ACTION_NONE;
// we need a valid connection if (!::dbtools::getConnection(Reference< XRowSet > (xDataSource,UNO_QUERY)).is()) return DND_ACTION_NONE;
sal_Int32 nCorrectRowCount = GetRowCount(); if (GetOptions() & DbGridControlOptions::Insert)
--nCorrectRowCount; // there is an empty row for inserting records if (IsCurrentAppending())
--nCorrectRowCount; // the current data record doesn't really exist, we are appending a new one
OSL_ENSURE((nCol != BROWSER_INVALIDID) && (nRow < nCorrectRowCount), "SbaGridControl::Drop : dropped on an invalid position !"); // AcceptDrop should have caught this
// from now we work with ids instead of positions
nCol = GetColumnId(nCol);
GoToRowColumnId(nRow, nCol); if (!IsEditing())
ActivateCell();
// get the dropped string
TransferableDataHelper aDropped( rEvt.maDropEvent.Transferable );
OUString sDropped; if ( !aDropped.GetString( SotClipboardFormatId::STRING, sDropped ) ) return DND_ACTION_NONE;
IEditImplementation* pEditImplementation = pController->GetEditImplementation();
pEditImplementation->SetText(sDropped); // SetText itself doesn't call a Modify as it isn't a user interaction
pController->Modify();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.