How to get Scrollbar notifications from a TMemo control? - vcl

I have a VCL TMemo control and need to be notified every time the text is scrolled. There is no OnScroll event and the scroll messages doesn't seem to propagate up to the parent form.
Any idea of how to get the notification?
As a last resort I can place an external TScrollBar and update the TMemo in the OnScroll event, but then I have to keep them in sync when I move the cursor or scroll the mouse wheel in TMemo...

You can use a interposer class to handle the WM_VSCROLL and WM_HSCROLL messages and the EN_VSCROLL and EN_HSCROLL notification codes (exposed through the WM_COMMAND message).
Try this sample
type
TMemo = class(Vcl.StdCtrls.TMemo)
private
procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
procedure WMVScroll(var Msg: TWMHScroll); message WM_VSCROLL;
procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
end;
TForm16 = class(TForm)
Memo1: TMemo;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form16: TForm16;
implementation
{$R *.dfm}
{ TMemo }
procedure TMemo.CNCommand(var Message: TWMCommand);
begin
case Message.NotifyCode of
EN_VSCROLL : OutputDebugString('EN_VSCROLL');
EN_HSCROLL : OutputDebugString('EN_HSCROLL');
end;
inherited ;
end;
procedure TMemo.WMHScroll(var Msg: TWMHScroll);
begin
OutputDebugString('WM_HSCROLL') ;
inherited;
end;
procedure TMemo.WMVScroll(var Msg: TWMHScroll);
begin
OutputDebugString('WM_HSCROLL') ;
inherited;
end;

You can subclass the Memo's WindowProc property at runtime to catch all of messages sent to the Memo, eg:
private:
TWndMethod PrevMemoWndProc;
void __fastcall MemoWndProc(TMessage &Message);
__fastcall TMyForm::TMyForm(TComponent *Owner)
: TForm(Owner)
{
PrevMemoWndProc = Memo1->WindowProc;
Memo1->WindowProc = MemoWndProc;
}
void __fastcall TMyForm::MemoWndProc(TMessage &Message)
{
switch (Message.Msg)
{
case CN_COMMAND:
{
switch (reinterpret_cast<TWMCommand&>(Message).NotifyCode)
{
case EN_VSCROLL:
{
//...
break;
}
case EN_HSCROLL:
{
//...
break;
}
}
break;
}
case WM_HSCROLL:
{
//...
break;
}
case WM_VSCROLL:
{
//...
break;
}
}
PrevMemoWndProc(Message);
}

Related

Revit external command with progress bar and 'Cancel' button

I am developing a new external command for Revit. it needs a progress bar + a button to cancel its execution in any moment.
In order to get it, I haver implemented a external event.
Implementing an external event handler with de code to be executed by the command.
public class GestorDeEventoExterno : IExternalEventHandler
{
public bool CancellationRequested { get; set; }
private VentanaDeProgreso progressAndcancelWindow;
private EventWaitHandle eventWait;
public void Execute(UIApplication aplicacionDeLaIU)
{
using (this.eventWait = new AutoResetEvent(false))
{
// New thread for the progress bar.
Thread progressBarThread = new Thread(new ThreadStart(() =>
{
// Populating the progress bar window.
this.progressAndcancelWindow = new VentanaDeProgreso(this);
progressAndcancelWindow.Show();
// Chenge the state of the wait event.
this.eventWait.Set();
Dispatcher.Run();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.IsBackground = true;
progressBarThread.Start();
this.eventWait.WaitOne();
}
// Get the current revit document.
Document documentoActivo = aplicacionDeLaIU.ActiveUIDocument.Document;
// Code to simulate the revit command operation.
for (int i = 0;
i <= 100;
i++)
{
// Code to be executed if a cancellation has been requested.
if (this.CancellationRequested)
{
TaskDialog.Show("Test", "Cancel");
this.progressAndcancelWindow.Dispatcher.Invoke(new Action(this.progressAndcancelWindow.Close));
return;
}
this.progressAndcancelWindow.ActualizarProgreso($"loop number: {i}", i, 100);
Thread.Sleep(100);
}
this.progressAndcancelWindow.Dispatcher.Invoke(new Action(this.progressAndcancelWindow.Close));
TaskDialog.Show("Test", "END");
}
public string GetName()
{
return "test";
}
}
Implementing an external command to register the external event and populate the main window
public class Class1 : IExternalCommand
{
public Result Execute(
ExternalCommandData externalCommandData,
ref string message,
ElementSet elements)
{
// Registering the external event.
GestorDeEventoExterno externalEventHandler = new GestorDeEventoExterno();
ExternalEvent externalEvent = ExternalEvent.Create(externalEventHandler);
// Populating the main window.
VentanaPrincipal mainWindow = new VentanaPrincipal(
externalEvent);
mainWindow.Show();
return Result.Succeeded;
}
}
Finally, the code behind of the progress bar window
public partial class VentanaDeProgreso : Window
{
private GestorDeEventoExterno externalEventHandler;
public void ActualizarProgreso(
string texto,
int valorActual,
int valortotal = 100)
{
this.Dispatcher.Invoke(
new Action<string, int, int>(
delegate (string txt, int vActual, int vTotal)
{
this.IndicadorDeProgreso.Value = valorActual;
this.IndicadorDeProgreso.Maximum = vTotal;
this.Texto.Text = txt;
}),
System.Windows.Threading.DispatcherPriority.Background,
texto,
valorActual,
valortotal);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// AsignaciĆ³n de valor verdadero a la propiedad de cancelaciĆ³n solicitada del evento externo.
this.externalEventHandler.CancellationRequested = true;
}
public VentanaDeProgreso(GestorDeEventoExterno externalEventHandler)
{
InitializeComponent();
this.externalEventHandler= externalEventHandler;
}
}
AS you can see, the progress window has the external event handler as a property and the cancel button click event sets the property 'CancellationRequested'.
My question is: How can I improve it?
You do not need an external event to cancel your command.
You only need an external event to cancel submit a request to execute Revit API functionality from some context in which it is not available.
Your cancellation requires no Revit API functionality, just your own stuff, hence no external event.
Therefore, you can restructure the whole solution much more simply. Kiss!

Changed event for JavaScript Binding in Qml?

I have a property which is defined by a JavaScript expression (currentContainer):
Item {
id: theContainer
property alias currentIndex: theListView.currentIndex
signal onCurrentIndexChanged()
property MyCustomCppContainer currentContainer: {
if(theListView.currentIndex >= 0)
theModel.getCustomContainer(theListView.currentIndex)
else
null
}
signal onCurrentContainerChanged() // nobody calls this signal (yet)
MyCustomCppModel {
id: theModel
}
ListView {
id: theListView
anchors.fill: parent
model: theModel
currentIndex: -1
onCurrentIndexChanged: theContainer.onCurrentIndexChanged()
/* Other properties stripped for example */
}
}
Sadly I always get the last selected container, not the currently selected one:
ContainerItem {
onCurrentIndexChanged: {
//On first change, currentContainer is null
//though the first one was selected
//After selecting the second entry
//I get the result I expected last time
console.log(currentContainer.name);
}
}
I think a solution would be to have another signal for currentContainer: onCurrentContainerChanged().
But who calls this special signal?
I could solve this using a C++ helper class:
class PropertyChangedHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant theProperty WRITE setTheProperty NOTIFY thePropertyChanged)
public:
PropertyChangedHelper(QObject* parent = nullptr) : QObject(parent) {}
virtual ~PropertyChangedHelper() {}
void setTheProperty(QVariant) {
Q_EMIT thePropertyChanged();
}
Q_SIGNALS:
void thePropertyChanged();
private:
Q_DISABLE_COPY(PropertyChangedHelper)
};
Usage is quite simple:
PropertyChangedHelper {
theProperty: containerItem.currentContainer
onThePropertyChanged: {
console.log(containerItem.currentContainer.name);
}
}
I am not sure if this violates any Qml/QtQuick philosophies, but it works.

Listening for BlueTooth state Changes with React-Native - Android

I am working with a team of React-Native JS developers who asked me to give them a native module for listening for Bluetooth state changes.
I created a ReactMethod that takes a Callback:
#ReactMethod
public void registerForBlueToothStateChanges(Callback listener){
mainActivity = (MainActivity)getCurrentActivity();
mainActivity.registerBTStateChangeListener(listener);
}
This method essentially is designed to set a Callback that will be used by my broadcast Receiver and at the same time send the current Bluetooth state back to the call back.. That all works fine, but in my Actual BroadCast Receiver i get an error: "The callback registerForBlueToothStateChanges() exists in module TripManager, but only one callback maybe registered to a function in a native Module"
Main Activity Relative Code:
#Subscribe
public void onBluetoothStateChanged(BluetoothStateChange state){
//check against current state to avoid duplicate offs
Log.v(TAG, "STATE CHANGED WOOT");
if(!state.equals(lastBluetoothState)){
lastBluetoothState = state;
Log.v(TAG, "BT STATE CHANGE***: "+state +" "+state.equals(BluetoothStateChange.ON));
if(blueToothStateListener != null) {
int i = 0;
if (state.equals(BluetoothStateChange.ON)) {
i = 1;
}
sendBTStateChange(i);
}
}
lastBluetoothState = state;
}
public void registerBTStateChangeListener(Callback listener){
blueToothStateListener = listener;
int i = 0;
if(lastBluetoothState.equals(BluetoothStateChange.ON)){
i = 1;
}
sendBTStateChange(i);
}
private void sendBTStateChange(int code){
blueToothStateListener.invoke(code);
}
Any pointers how i can get around this?

Catch mouse event on tree widget item in QTreeWidget

In tree widget I have following signal connected:
connect(mTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*, int)),
SLOT(onItemClicked(QTreeWidgetItem*, int)));
where onItemClicked() slot is following:
void WidgetBox::onItemClicked(QTreeWidgetItem *item, int )
{
int index = getPageIndex(item);
setCurrentIndex(index);
}
int WidgetBox::getPageIndex(QTreeWidgetItem *item)
{
if (!item) return -1;
QTreeWidgetItem *parent = item->parent();
if(parent) // Parent is top level item
{
return mTreeWidget->indexOfTopLevelItem(parent);
}
else // Current item is top level
{
return item->treeWidget()->indexOfTopLevelItem(item);
}
}
void WidgetBox::setCurrentIndex(int index)
{
if (index != currentIndex() && checkIndex(index))
{
mTreeWidget->setCurrentItem(mTreeWidget->topLevelItem(index));
emit currentIndexChanged(index);
}
}
However I can't catch itemClicked() signal and onItemClicked() never executed because top level items has push button widget (set with setItemWidget() method) which intercepts mouse event and child items contain container widgets which may have any widget combinations in them.
Is there a good method here to invoke this itemClicked() signal for both top level and child items of tree widget?
installEventFilter() for all widgets found in an item by something like following:QList<QWidget *> widgets = parentWidget.findChildren<QWidget *>();?
Or establish mouse event propagation somehow?
QCoreApplication::postEvent()?
How to organize such process better so all widgets process mouse event as they need to and TreeWidget issue SIGNAL(itemClicked()) as well?
Full sources to reproduce: https://github.com/akontsevich/WidgetBox
So solution is simple and following - just re-send void itemClicked(QTreeWidgetItem *item, int column); signal in PageEventFilter:
PageEventFilter::PageEventFilter(QObject *parent, QTreeWidgetItem *item)
: QObject(parent)
, mItem(item)
{
connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)),
mItem->treeWidget(), SIGNAL(itemClicked(QTreeWidgetItem*,int)));
}
bool PageEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress)
{
// Resend signal to QTreeWidget
emit itemClicked(mItem, 0);
return false; // Send event to the object (do not filter it)
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
P.S. Will leave previous answer as well if somebody needs code or idea.
Created event filter like this:
class PageEventFilter : public QObject
{
Q_OBJECT
public:
PageEventFilter(QObject *parent, QTreeWidgetItem *item);
protected:
bool eventFilter(QObject *obj, QEvent *event);
private:
QTreeWidgetItem *mItem;
};
bool PageEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress)
{
// Emitate mouse event for parent QTreeWidget
QMouseEvent *oldEvent = (QMouseEvent *)event;
QRect itemRect = mItem->treeWidget()->visualItemRect(mItem);
QPointF mousePos(itemRect.x() + 1, itemRect.y() + 1);
QMouseEvent *newEvent = new QMouseEvent(oldEvent->type(),
mousePos,
oldEvent->button(),
oldEvent->buttons(),
oldEvent->modifiers());
QCoreApplication::postEvent(mItem->treeWidget(), newEvent);
return false; // Sent event to the object (do not filter it)
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
Install it to a button in tree widget item. It creates and sends (imitates) mouse event to tree widget, however tree widget still does not send itemClicked(QTreeWidgetItem*, int) signal. What is the problem could be? Wrong mouse pos? Tried to send event to viewport() - no luck as well. Any way to solve this?

Pause application in QML when app is in background Symbian

I want to know of any pure QML way to find out whether the application is in the background or not and then accordingly stop or play music. In meego the alternate way to do is through the PlatformWindow Element but it does not exist in Symbian QML. Help needed please
Finally I got it working :) and i did it though Qt way... here are the steps
1) Create a class MyEventFilter
class myEventFilter : public QObject
{
bool eventFilter(QObject *obj, QEvent *event) {
switch(event->type()) {
case QEvent::WindowActivate:
emit qmlvisiblechange(true);
qDebug() << "Window activated";
bis_foreground=true;
return true;
case QEvent::WindowDeactivate:
emit qmlvisiblechange(false);
qDebug() << "Window deactivated";
bis_foreground=false;
return true;
default:
return false;
}
}
void dosomething();
private:
int something;
public:
bool bis_foreground;
Q_OBJECT
public slots:
Q_INVOKABLE QString checkvisibility() {
if (bis_foreground==true) return "true";
else return "false";
}
signals:
void qmlvisiblechange(bool is_foreground);
};
2) Then in main.cpp include this file include the class and add setContext propery like this
context->setContextProperty("myqmlobject", &ef);
3) in qml file call it like this:
Item {
id: name
Connections
{
target:myqmlobject
onQmlvisiblechange:
{
if(is_foreground)
{
//dont do anything...
}
else
{
playSound.stop()
}
}
}
}
Enjoy :)
Why do you need a pure QML way?
You can detect if an application has been sent to the background by installing an event filter.
Check: http://www.developer.nokia.com/Community/Wiki/Detecting_when_a_Qt_application_has_been_switched_to_the_background_and_when_resumed
For a "pure" QML way, there is the Symbian QML element:
http://doc.qt.nokia.com/qt-components-symbian/qml-symbian.html
It has a foreground property that indicates whether the app is in the foreground or in the background. You can try connecting to onForegroundChanged.
From the documentation, the Symbian element is not "creatable". It exists as a context property named symbian. So a sample usage would be:
import QtQuick 1.1
import com.nokia.symbian 1.1
PageStackWindow {
id: window
initialPage: MainPage {tools: toolBarLayout}
showStatusBar: true
showToolBar: true
function appForegroundChanged() {
console.log("Foreground: " + symbian.foreground)
}
function appCurrentTimeChanged() {
console.log("Current time: " + symbian.currentTime)
}
Component.onCompleted: {
symbian.currentTimeChanged.connect(appCurrentTimeChanged)
symbian.foregroundChanged.connect(appForegroundChanged)
}
ToolBarLayout {
id: toolBarLayout
ToolButton {
flat: true
iconSource: "toolbar-back"
onClicked: window.pageStack.depth <= 1 ? Qt.quit() : window.pageStack.pop()
}
}
}