How to override undo / redo in QPlainTextEdit - c++

I am sub-classing QPlainText edit and I would like to be able to intercept undo / redo commands so that I can implement custom functionality.
I realise that I can disable the undo / redo capability with setUndoRedoEnabled and I can detect Ctrl+Z and Ctrl+Y key presses. However, this doesn't seem like the best cross platform way of doing it.
Any advice?

You simply need to reimplement the slots :
class MyTestEdit : public QPlainTextEdit {
Q_OBJECT
public slots:
void redo() { ... }
void undo() { ... }
};
Signal and slots are exactly like other c++ methods. If you reimplement them in a subclass, they will be called instead of the the parent's.

I think you can use "QUndoStack" for this.
In you subclass's constructor (constructor is better), call a method that creates Undo and Redo actions to handle for your class.
Prototype:
//Call this function to register undo and redo actions.
Void methodCrteaesUndoandRedoActions()
{
QUndoStack unStack = new QUndoStack (this);
QAction *undoAct = undoStack->createUndoAction(this);
QAction *RedoAct = undoStack->createRedoAction(this);
}
//Implement below functions in your class to handle the business.
void undo()
{
}
void redo()
{
}

Related

Overriding JUCE ButtonStateChange / Listener Issues

I'm struggling to figure out how to properly override a button's ButtonStateChange in the JUCE library. I'm wanting to change the what happens when a button is held down. I'm fairly new to overriding, but I've been able to successfully override other elements in the JUCE library. Though I am having an issue with this topic.
1) I know you create a new class, maybe MyCustomButton, then
2) Inherit the class you are looking to modify, Button::Listener (not sure if I should do private or public inheritance)
3) Copy and paste the code of the function you want to alter, applying the override keyword to the prototype,
but after this, I'm lost. I'm not sure how to let this new class affect a button that already exists. I know i need to add a listener to an existing button in the constructor and remove the listener in the destructor of the GUI component, but still, I don't know how to apply this new ButtonChangeState listener to an existing button.
Any help would be greatly appreciated.
You can create a new class which inherits from one of Juce's button classes (e.g juce::TextButton) and override buttonStateChanged()
class MyCustomButton : public juce::TextButton
{
public:
MyCustomButton();
protected:
void buttonStateChanged() override
{
// do what you want here
}
};
To apply to your already existing button, just change its type to MyCustomButton.
Alternatively, you can make the class where you use the button inherit from juce::Button::Listener and override buttonStateChanged(Button*).
Then all you need is to attach the listener to your button:
class MyWindow : public Component, private juce::Button::Listener
{
public:
MyWindow()
{
m_button.addListener(this);
}
~MyWindow()
{
m_button.removeListener(this);
}
private:
juce::TextButton m_button;
void buttonStateChanged(Button* button) override
{
if (button == &m_button)
{
// do what you want
}
}
};

itemDoubleClicked signal is not emitted when mouseDoubleClickEvent is implemented

I'm implementing a class that inherits the QTreeWidget,
I'm trying to do something only when the user left-clicks on an item.
Since itemDoubleClicked only gives you the item and not the mouse event,
and mouseDoubleClickEvent only gives you the mouse event with no item,
so I thought I would add a member in the class and record whether left or right button was pressed in mouseDoubleClickEvent,
then check that info when entering the slot connected to signal itemDoubleClicked.
That is, if the signal is emitted after the event handler. I was planning on experimenting if this was true, but then I ran into this issue.
Ok, back to the class, it looks something like this:
class myTreeWidget : public QTreeWidget{
Q_OBJECT
private:
Qt::MouseButton m_button;
public:
myTreeWidget(QWidget* parent):QTreeWidget(parent){
m_button = Qt::NoButton;
connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)),
this, SLOT(slot_doubleClick(QTreeWidgetItem*,int)));
}
void mouseDoubleClickEvent(QMouseEvent* event){
m_button = event->button();
}
public slots:
void slot_doubleClick(QTreeWidgetItem* item, int column);
signals:
void itemDoubleClicked(QTreeWidgetItem* item, int column);
}
Yep, something like this.
Then I used gdb to check which was called first,
mouseDoubleClickEvent or slot_doubleClick,
and it turns out that slot_doubleClick was not called at all.
I commented out mouseDoubleClickEvent and tried again,
and slot_doubleClick was called.
So um... what I'm asking here is...
is this a limitation in Qt?
Can I only choose one between signals&slots and event handlers?
Or am I just doing it wrong?
Moreover, if this is a limitation,
can you recommend another solution to what I'm trying to do?
(only respond to left double-clicks)
Sorry for the long post and thanks!
If you override some event handler and want also default behavior, you should call base handler implementation. For example try this:
void mouseDoubleClickEvent(QMouseEvent* event){
m_button = event->button();
QTreeWidget::mouseDoubleClickEvent(event);
}

Propagate events to both QGraphicsView and QGraphicsScene

I have a Qt app with a QGraphicsView and QGraphicsScene. I subclassed them both and want to handle mouseevents in both. I can do this for both classes separately, using
virtual void mousePressEvent(QMouseEvent* event);
But when I handle the events in the GraphicsView, I don't receive them anymore in the GraphicsScene. How do I fix this, i.e. pass the event from the grapchicsview to the graphicsscene?
Just forward mousePressEvent to your GraphicsView's parent, this will then call scene's mousePressEvent :
void MyGraphicsView::mousePressEvent(QMouseEvent * e)
{
// forward to scene (via default view behaviour)
QGraphicsView::mousePressEvent(e) ;
// ...
}
BTW, QGraphicsScene::mousePressEvent has a different kind (class) of event : QGraphicsSceneMouseEvent
Call the base implementation at the end of your overriden function, eg:
void MyView::mousePressEvent(QMouseEvent* event)
{
// do something
QGraphicsView::mousePressEvent(event);
}

reimplement the shouldInterruptJavaScript() in Qt (C++)

According to the help of Qt for QWebPage [Slot ShoudInteruptJavaScript], located here:
This function is called when a JavaScript program is running for a long period of time.
If the user wanted to stop the JavaScript the implementation should return true; otherwise false.
The default implementation executes the query using QMessageBox::information with QMessageBox::Yes and QMessageBox::No buttons.
Warning: Because of binary compatibility constraints, this function is not virtual. If you want to provide your own implementation in a QWebPage subclass, reimplement the shouldInterruptJavaScript() slot in your subclass instead. QtWebKit will dynamically detect the slot and call it.
I don't want qt show a message when javascript runnig for long period of time.
So, how can i reimplement ShoudInteruptJavaScript? and where should i create it?
Please show me a sample
Thanks
All the info you need is in the documentation.
Create a new custom class that inherits from QWebPage, make sure it's a Q_OBJECT to receive signals.
class MyFunkyPage : public QWebPage {
Q_OBJECT
public slots:
bool shouldInterruptJavaScript() {
QApplication::processEvents(QEventLoop::AllEvents, 42);
// Ignore the error
return false;
}
};
Set the page of your QWebView to a custom subclass of QWebPage.
setPage(new MyFunkyPage());
Then when your page gets this signal it won't stop the script from executing, and it won't show a dialog.
The MyFunkyPage solution potentially leaks memory and causes crashes because the object passed to setPage has no parent and setPage does not take ownership. Instead,
class QWebPageWithoutJsWarning : public QWebPage {
Q_OBJECT
public:
QWebPageWithoutJsWarning(QObject* parent = 0) : QWebPage(parent) {}
public slots:
bool shouldInterruptJavaScript() {
return false;
}
};
Set the page of your QWebView to the custom subclass of QWebPage, parented on the WebView,
void suppressJSWarning(QWebView& webView) {
webView.setPage(new QWebPageWithoutJsWarning(&webView));
}
#anson-mackeracher almost had it right.
Qt needs it to be a private slot, not a public one. Here's what works for my class:
class MyFunkyPage : public QWebPage {
Q_OBJECT
private slots:
bool shouldInterruptJavaScript() {
// Ignore the error (return true to kill the runaway JavaScript)
return false;
}
};
Set the page of your QWebView to a custom subclass of QWebPage.
setPage(new MyFunkyPage());
I just tested this with Qt 4.8.4 and it works like a charm. I didn't need the processEvents call.

Using QMDIArea with Qt 4.4.

I'm using the QMdiArea in Qt 4.4.
If a new project is created, I add a number of sub windows to a QMdiArea. I'd like to disallow the user to close a sub window during runtime. The sub windows should only be closed if the whole application is closed or if a new project is created.
How can I do this?
You need to define your own subWindow. create a subclass of QMdiSubWindow and override the closeEvent(QCloseEvent *closeEvent). you can control it by argument. for example:
void ChildWindow::closeEvent(QCloseEvent *closeEvent)
{
if(/*condition C*/)
closeEvent->accept();
else
closeEvent->ignore(); // you can do something else, like
// writing a string in status bar ...
}
then subclass the QMdiArea and override QMdiArea::closeAllSubWindows () like this:
class MainWindowArea : public QMdiArea
{
Q_OBJECT
public:
explicit MainWindowArea(QWidget *parent = 0);
signals:
void closeAllSubWindows();
public slots:
};
// Implementation:
MainWindowArea::closeAllSubWindows()
{
// set close condition (new project is creating, C = true)
foreach(QMdiSubWindow* sub,this->subWindowList())
{
(qobject_cast<ChildWindow*>(sub))->close();
}
}
you may also need to override close slot of your mdi area.
You'd do this the same as for a top-level window: process and ignore the QCloseEvent it sent. QMdiArea::closeActiveSubWindow/QMdiArea::closeAllSubWindows just call QWidget::close, which sends a closeEvent and confirms that it was accepted before proceeding.
You can process this event by subclassing QMdiSubWindow and reimplementing QWidget::closeEvent, or by using an event filter to intercept it..