513 lines
17 KiB
C++
513 lines
17 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <sal/config.h>
|
|
|
|
#include <cassert>
|
|
|
|
#include <com/sun/star/beans/XMultiPropertySet.hpp>
|
|
#include <com/sun/star/beans/XPropertiesChangeListener.hpp>
|
|
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
|
|
#include <officecfg/Office/Common.hxx>
|
|
#include <editeng/eeitem.hxx>
|
|
#include <editeng/colritem.hxx>
|
|
#include <editeng/fhgtitem.hxx>
|
|
#include <editeng/fontitem.hxx>
|
|
#include <editeng/wghtitem.hxx>
|
|
#include <sqledit.hxx>
|
|
#include <cppuhelper/implbase.hxx>
|
|
#include <i18nlangtag/languagetag.hxx>
|
|
#include <svl/itempool.hxx>
|
|
#include <svl/itemset.hxx>
|
|
#include <vcl/commandevent.hxx>
|
|
#include <vcl/event.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <vcl/specialchars.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
|
|
using namespace dbaui;
|
|
|
|
class SQLEditView::ChangesListener:
|
|
public cppu::WeakImplHelper< css::beans::XPropertiesChangeListener >
|
|
{
|
|
public:
|
|
explicit ChangesListener(SQLEditView& editor): editor_(editor) {}
|
|
|
|
private:
|
|
virtual ~ChangesListener() override {}
|
|
|
|
virtual void SAL_CALL disposing(css::lang::EventObject const &) override
|
|
{
|
|
std::unique_lock g(editor_.m_mutex);
|
|
editor_.m_notifier.clear();
|
|
}
|
|
|
|
virtual void SAL_CALL propertiesChange(
|
|
css::uno::Sequence< css::beans::PropertyChangeEvent > const &) override
|
|
{
|
|
SolarMutexGuard g;
|
|
editor_.ImplSetFont();
|
|
}
|
|
|
|
SQLEditView& editor_;
|
|
};
|
|
|
|
SQLEditView::SQLEditView(std::unique_ptr<weld::ScrolledWindow> xScrolledWindow)
|
|
: m_xScrolledWindow(std::move(xScrolledWindow))
|
|
, m_aUpdateDataTimer("dbaccess SQLEditView m_aUpdateDataTimer")
|
|
, m_aHighlighter(HighlighterLanguage::SQL)
|
|
, m_bInUpdate(false)
|
|
, m_bDisableInternalUndo(false)
|
|
{
|
|
m_xScrolledWindow->connect_vadjustment_changed(LINK(this, SQLEditView, ScrollHdl));
|
|
}
|
|
|
|
void SQLEditView::DisableInternalUndo()
|
|
{
|
|
GetEditEngine()->EnableUndo(false);
|
|
m_bDisableInternalUndo = true;
|
|
}
|
|
|
|
void SQLEditView::SetItemPoolFont(SfxItemPool* pItemPool)
|
|
{
|
|
OUString sFontName(officecfg::Office::Common::Font::SourceViewFont::FontName::get().value_or(OUString()));
|
|
if (sFontName.isEmpty())
|
|
{
|
|
vcl::Font aTmpFont(OutputDevice::GetDefaultFont(DefaultFontType::FIXED, Application::GetSettings().GetUILanguageTag().getLanguageType(), GetDefaultFontFlags::OnlyOne));
|
|
sFontName = aTmpFont.GetFamilyName();
|
|
}
|
|
|
|
Size aFontSize(0, officecfg::Office::Common::Font::SourceViewFont::FontHeight::get());
|
|
vcl::Font aAppFont(sFontName, aFontSize);
|
|
|
|
pItemPool->SetUserDefaultItem(SvxFontItem(aAppFont.GetFamilyType(), aAppFont.GetFamilyName(),
|
|
u""_ustr, PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW,
|
|
EE_CHAR_FONTINFO));
|
|
pItemPool->SetUserDefaultItem(SvxFontItem(aAppFont.GetFamilyType(), aAppFont.GetFamilyName(),
|
|
u""_ustr, PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW,
|
|
EE_CHAR_FONTINFO_CJK));
|
|
pItemPool->SetUserDefaultItem(SvxFontItem(aAppFont.GetFamilyType(), aAppFont.GetFamilyName(),
|
|
u""_ustr, PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW,
|
|
EE_CHAR_FONTINFO_CTL));
|
|
|
|
pItemPool->SetUserDefaultItem(
|
|
SvxFontHeightItem(aAppFont.GetFontHeight() * 20, 100, EE_CHAR_FONTHEIGHT));
|
|
pItemPool->SetUserDefaultItem(
|
|
SvxFontHeightItem(aAppFont.GetFontHeight() * 20, 100, EE_CHAR_FONTHEIGHT_CJK));
|
|
pItemPool->SetUserDefaultItem(
|
|
SvxFontHeightItem(aAppFont.GetFontHeight() * 20, 100, EE_CHAR_FONTHEIGHT_CTL));
|
|
}
|
|
|
|
void SQLEditView::makeEditEngine()
|
|
{
|
|
assert(!m_pItemPool);
|
|
m_pItemPool = EditEngine::CreatePool();
|
|
SetItemPoolFont(m_pItemPool.get());
|
|
m_xEditEngine.reset(new EditEngine(m_pItemPool.get()));
|
|
}
|
|
|
|
void SQLEditView::SetDrawingArea(weld::DrawingArea* pDrawingArea)
|
|
{
|
|
WeldEditView::SetDrawingArea(pDrawingArea);
|
|
|
|
EditEngine& rEditEngine = *GetEditEngine();
|
|
|
|
rEditEngine.SetDefaultHorizontalTextDirection(EEHorizontalTextDirection::L2R);
|
|
rEditEngine.SetModifyHdl(LINK(this, SQLEditView, ModifyHdl));
|
|
rEditEngine.SetStatusEventHdl(LINK(this, SQLEditView, EditStatusHdl));
|
|
|
|
m_aUpdateDataTimer.SetTimeout(150);
|
|
m_aUpdateDataTimer.SetInvokeHandler(LINK(this, SQLEditView, ImplUpdateDataHdl));
|
|
|
|
ImplSetFont();
|
|
|
|
// Listen for change of Font and Color Settings:
|
|
// Using "this" in ctor is a little fishy, but should work here at least as
|
|
// long as there are no derivations:
|
|
m_listener = new ChangesListener(*this);
|
|
css::uno::Reference< css::beans::XMultiPropertySet > n(
|
|
officecfg::Office::Common::Font::SourceViewFont::get(),
|
|
css::uno::UNO_QUERY_THROW);
|
|
{
|
|
std::unique_lock g(m_mutex);
|
|
m_notifier = n;
|
|
}
|
|
css::uno::Sequence< OUString > s { u"FontHeight"_ustr, u"FontName"_ustr };
|
|
n->addPropertiesChangeListener(s, m_listener);
|
|
m_ColorConfig.AddListener(this);
|
|
}
|
|
|
|
SQLEditView::~SQLEditView()
|
|
{
|
|
css::uno::Reference< css::beans::XMultiPropertySet > n;
|
|
{
|
|
std::unique_lock g(m_mutex);
|
|
n = m_notifier;
|
|
}
|
|
if (n.is()) {
|
|
n->removePropertiesChangeListener(m_listener);
|
|
}
|
|
m_ColorConfig.RemoveListener(this);
|
|
}
|
|
|
|
void SQLEditView::SetTextAndUpdate(const OUString& rNewText)
|
|
{
|
|
SetText(rNewText);
|
|
UpdateData();
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SQLEditView, ModifyHdl, LinkParamNone*, void)
|
|
{
|
|
if (m_bInUpdate)
|
|
return;
|
|
m_aUpdateDataTimer.Start();
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SQLEditView, ImplUpdateDataHdl, Timer*, void)
|
|
{
|
|
UpdateData();
|
|
}
|
|
|
|
Color SQLEditView::GetColorValue(TokenType aToken)
|
|
{
|
|
return GetSyntaxHighlightColor(m_aColorConfig, m_aHighlighter.GetLanguage(), aToken);
|
|
}
|
|
|
|
void SQLEditView::UpdateData()
|
|
{
|
|
m_bInUpdate = true;
|
|
EditEngine& rEditEngine = *GetEditEngine();
|
|
|
|
bool bModified = rEditEngine.IsModified();
|
|
bool bUndoEnabled = rEditEngine.IsUndoEnabled();
|
|
rEditEngine.EnableUndo(false);
|
|
|
|
// syntax highlighting
|
|
for (sal_Int32 nLine=0; nLine < rEditEngine.GetParagraphCount(); ++nLine)
|
|
{
|
|
OUString aLine( rEditEngine.GetText( nLine ) );
|
|
|
|
ESelection aAllLine(nLine, 0, nLine, EE_TEXTPOS_MAX);
|
|
rEditEngine.RemoveAttribs(aAllLine, false, EE_CHAR_COLOR);
|
|
rEditEngine.RemoveAttribs(aAllLine, false, EE_CHAR_WEIGHT);
|
|
rEditEngine.RemoveAttribs(aAllLine, false, EE_CHAR_WEIGHT_CJK);
|
|
rEditEngine.RemoveAttribs(aAllLine, false, EE_CHAR_WEIGHT_CTL);
|
|
|
|
std::vector<HighlightPortion> aPortions;
|
|
m_aHighlighter.getHighlightPortions( aLine, aPortions );
|
|
for (auto const& portion : aPortions)
|
|
{
|
|
SfxItemSet aSet(rEditEngine.GetEmptyItemSet());
|
|
aSet.Put(SvxColorItem(GetColorValue(portion.tokenType), EE_CHAR_COLOR));
|
|
rEditEngine.QuickSetAttribs(aSet, ESelection(nLine, portion.nBegin, nLine, portion.nEnd));
|
|
}
|
|
}
|
|
|
|
rEditEngine.ClearModifyFlag();
|
|
|
|
m_bInUpdate = false;
|
|
|
|
rEditEngine.EnableUndo(bUndoEnabled);
|
|
|
|
if (bModified)
|
|
m_aModifyLink.Call(nullptr);
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
void SQLEditView::DoBracketHilight(sal_uInt16 nKey)
|
|
{
|
|
ESelection aCurrentPos = m_xEditView->GetSelection();
|
|
sal_Int32 nStartPos = aCurrentPos.start.nIndex;
|
|
const sal_uInt32 nStartPara = aCurrentPos.start.nPara;
|
|
sal_uInt16 nCount = 0;
|
|
int nChar = -1;
|
|
|
|
switch (nKey)
|
|
{
|
|
case '\'': // no break
|
|
case '"':
|
|
{
|
|
nChar = nKey;
|
|
break;
|
|
}
|
|
case '}' :
|
|
{
|
|
nChar = '{';
|
|
break;
|
|
}
|
|
case ')':
|
|
{
|
|
nChar = '(';
|
|
break;
|
|
}
|
|
case ']':
|
|
{
|
|
nChar = '[';
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nChar == -1)
|
|
return;
|
|
|
|
bool bUndoEnabled = m_xEditEngine->IsUndoEnabled();
|
|
m_xEditEngine->EnableUndo(false);
|
|
|
|
sal_uInt32 nPara = nStartPara;
|
|
do
|
|
{
|
|
if (nPara == nStartPara && nStartPos == 0)
|
|
continue;
|
|
|
|
OUString aLine( m_xEditEngine->GetText( nPara ) );
|
|
|
|
if (aLine.isEmpty())
|
|
continue;
|
|
|
|
for (sal_Int32 i = (nPara==nStartPara) ? nStartPos-1 : aLine.getLength()-1; i>0; --i)
|
|
{
|
|
if (aLine[i] == nChar)
|
|
{
|
|
if (!nCount)
|
|
{
|
|
SfxItemSet aSet(m_xEditEngine->GetEmptyItemSet());
|
|
aSet.Put(SvxColorItem(Color(0,0,0), EE_CHAR_COLOR));
|
|
aSet.Put(SvxWeightItem(WEIGHT_ULTRABOLD, EE_CHAR_WEIGHT));
|
|
aSet.Put(SvxWeightItem(WEIGHT_ULTRABOLD, EE_CHAR_WEIGHT_CJK));
|
|
aSet.Put(SvxWeightItem(WEIGHT_ULTRABOLD, EE_CHAR_WEIGHT_CTL));
|
|
|
|
m_xEditEngine->QuickSetAttribs(aSet, ESelection(nPara, i, nPara, i + 1));
|
|
m_xEditEngine->QuickSetAttribs(aSet, ESelection(nStartPara, nStartPos));
|
|
return;
|
|
}
|
|
else
|
|
--nCount;
|
|
}
|
|
if (aLine[i] == nKey)
|
|
++nCount;
|
|
}
|
|
} while (nPara--);
|
|
|
|
m_xEditEngine->EnableUndo(bUndoEnabled);
|
|
}
|
|
|
|
Color SQLEditView::GetSyntaxHighlightColor(const svtools::ColorConfig& rColorConfig, HighlighterLanguage eLanguage, TokenType aToken)
|
|
{
|
|
Color aColor;
|
|
switch (eLanguage)
|
|
{
|
|
case HighlighterLanguage::SQL:
|
|
{
|
|
switch (aToken)
|
|
{
|
|
case TokenType::Identifier: aColor = rColorConfig.GetColorValue(svtools::SQLIDENTIFIER).nColor; break;
|
|
case TokenType::Number: aColor = rColorConfig.GetColorValue(svtools::SQLNUMBER).nColor; break;
|
|
case TokenType::String: aColor = rColorConfig.GetColorValue(svtools::SQLSTRING).nColor; break;
|
|
case TokenType::Operator: aColor = rColorConfig.GetColorValue(svtools::SQLOPERATOR).nColor; break;
|
|
case TokenType::Keywords: aColor = rColorConfig.GetColorValue(svtools::SQLKEYWORD).nColor; break;
|
|
case TokenType::Parameter: aColor = rColorConfig.GetColorValue(svtools::SQLPARAMETER).nColor; break;
|
|
case TokenType::Comment: aColor = rColorConfig.GetColorValue(svtools::SQLCOMMENT).nColor; break;
|
|
default: aColor = Color(0,0,0);
|
|
}
|
|
break;
|
|
}
|
|
case HighlighterLanguage::Basic:
|
|
{
|
|
switch (aToken)
|
|
{
|
|
case TokenType::Identifier: aColor = Color(255,0,0); break;
|
|
case TokenType::Comment: aColor = Color(0,0,45); break;
|
|
case TokenType::Number: aColor = Color(204,102,204); break;
|
|
case TokenType::String: aColor = Color(0,255,45); break;
|
|
case TokenType::Operator: aColor = Color(0,0,100); break;
|
|
case TokenType::Keywords: aColor = Color(0,0,255); break;
|
|
case TokenType::Error : aColor = Color(0,255,255); break;
|
|
default: aColor = Color(0,0,0);
|
|
}
|
|
break;
|
|
}
|
|
default: aColor = Color(0,0,0);
|
|
|
|
}
|
|
return aColor;
|
|
}
|
|
|
|
bool SQLEditView::KeyInput(const KeyEvent& rKEvt)
|
|
{
|
|
DoBracketHilight(rKEvt.GetCharCode());
|
|
|
|
if (m_bDisableInternalUndo)
|
|
{
|
|
KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
|
|
if (eFunc == KeyFuncType::UNDO || eFunc == KeyFuncType::REDO)
|
|
return false;
|
|
}
|
|
|
|
return WeldEditView::KeyInput(rKEvt);
|
|
}
|
|
|
|
bool SQLEditView::Command(const CommandEvent& rCEvt)
|
|
{
|
|
if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
|
|
{
|
|
::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1));
|
|
weld::Widget* pPopupParent = GetDrawingArea();
|
|
std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, u"vcl/ui/editmenu.ui"_ustr));
|
|
std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr));
|
|
|
|
bool bEnableCut = true;
|
|
bool bEnableCopy = true;
|
|
bool bEnableDelete = true;
|
|
bool bEnablePaste = true;
|
|
bool bEnableSpecialChar = true;
|
|
|
|
EditView* pEditView = GetEditView();
|
|
|
|
if (!pEditView->HasSelection())
|
|
{
|
|
bEnableCut = false;
|
|
bEnableCopy = false;
|
|
bEnableDelete = false;
|
|
}
|
|
|
|
if (pEditView->IsReadOnly())
|
|
{
|
|
bEnableCut = false;
|
|
bEnablePaste = false;
|
|
bEnableDelete = false;
|
|
bEnableSpecialChar = false;
|
|
}
|
|
|
|
xContextMenu->set_sensitive(u"cut"_ustr, bEnableCut);
|
|
xContextMenu->set_sensitive(u"copy"_ustr, bEnableCopy);
|
|
xContextMenu->set_sensitive(u"delete"_ustr, bEnableDelete);
|
|
xContextMenu->set_sensitive(u"paste"_ustr, bEnablePaste);
|
|
xContextMenu->set_sensitive(u"specialchar"_ustr, bEnableSpecialChar);
|
|
xContextMenu->set_visible(u"undo"_ustr, false);
|
|
xContextMenu->set_visible(u"specialchar"_ustr, vcl::GetGetSpecialCharsFunction() != nullptr);
|
|
|
|
OUString sCommand = xContextMenu->popup_at_rect(pPopupParent, aRect);
|
|
|
|
if (sCommand == "cut")
|
|
pEditView->Cut();
|
|
else if (sCommand == "copy")
|
|
pEditView->Copy();
|
|
else if (sCommand == "paste")
|
|
pEditView->Paste();
|
|
else if (sCommand == "delete")
|
|
pEditView->DeleteSelected();
|
|
else if (sCommand == "selectall")
|
|
{
|
|
if (m_xEditEngine->GetParagraphCount())
|
|
{
|
|
pEditView->SetSelection(ESelection::All());
|
|
}
|
|
}
|
|
else if (sCommand == "specialchar")
|
|
{
|
|
OUString aChars = vcl::GetGetSpecialCharsFunction()(pPopupParent, m_xEditEngine->GetStandardFont(0));
|
|
if (!aChars.isEmpty())
|
|
{
|
|
pEditView->InsertText(aChars);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return WeldEditView::Command(rCEvt);
|
|
}
|
|
|
|
void SQLEditView::EditViewScrollStateChange()
|
|
{
|
|
// editengine height has changed or editview scroll pos has changed
|
|
SetScrollBarRange();
|
|
}
|
|
|
|
void SQLEditView::SetScrollBarRange()
|
|
{
|
|
EditEngine *pEditEngine = GetEditEngine();
|
|
if (!pEditEngine)
|
|
return;
|
|
if (!m_xScrolledWindow)
|
|
return;
|
|
EditView* pEditView = GetEditView();
|
|
if (!pEditView)
|
|
return;
|
|
|
|
int nVUpper = pEditEngine->GetTextHeight();
|
|
int nVCurrentDocPos = pEditView->GetVisArea().Top();
|
|
const Size aOut(pEditView->GetOutputArea().GetSize());
|
|
int nVStepIncrement = aOut.Height() * 2 / 10;
|
|
int nVPageIncrement = aOut.Height() * 8 / 10;
|
|
int nVPageSize = aOut.Height();
|
|
|
|
/* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has
|
|
effectively...
|
|
|
|
lower = gtk_adjustment_get_lower
|
|
upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size
|
|
|
|
and requires that upper > lower or the deceleration animation never ends
|
|
*/
|
|
nVPageSize = std::min(nVPageSize, nVUpper);
|
|
|
|
m_xScrolledWindow->vadjustment_configure(nVCurrentDocPos, 0, nVUpper,
|
|
nVStepIncrement, nVPageIncrement, nVPageSize);
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SQLEditView, ScrollHdl, weld::ScrolledWindow&, void)
|
|
{
|
|
DoScroll();
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SQLEditView, EditStatusHdl, EditStatus&, void)
|
|
{
|
|
Resize();
|
|
}
|
|
|
|
void SQLEditView::DoScroll()
|
|
{
|
|
if (m_xEditView)
|
|
{
|
|
auto currentDocPos = m_xEditView->GetVisArea().Top();
|
|
auto nDiff = currentDocPos - m_xScrolledWindow->vadjustment_get_value();
|
|
// we expect SetScrollBarRange callback to be triggered by Scroll
|
|
// to set where we ended up
|
|
m_xEditView->Scroll(0, nDiff);
|
|
}
|
|
}
|
|
|
|
void SQLEditView::ConfigurationChanged(utl::ConfigurationBroadcaster*, ConfigurationHints)
|
|
{
|
|
UpdateData();
|
|
}
|
|
|
|
void SQLEditView::ImplSetFont()
|
|
{
|
|
// see SmEditWindow::DataChanged for a similar case
|
|
SetItemPoolFont(m_pItemPool.get()); // change default font
|
|
// re-create with the new font
|
|
EditEngine& rEditEngine = *GetEditEngine();
|
|
OUString aTxt(rEditEngine.GetText());
|
|
rEditEngine.Clear();
|
|
SetTextAndUpdate(aTxt);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|