Trolltech Home | Qt4-preview-feedback Home | Recent Threads | All Threads | Author | Date
All threads index page 1

Qt4-preview-feedback Archive, January 2008
Various bugs and suggestions to Qt4.4-tp1

Pages: Prev | 1 | 2 | Next

Message 1 in thread

Hello,

I have gathered some bugs and suggestions as in the subject and mailed them
to qt-bugs, but they said that support for unsupported features is not
supported ;) And they redirected me here.

So here comes the list - sorry to send it in a single bulk, but it's quite
clear, so there shouldn't be any problems with spotting things one is
interested in.

Again - everything is based on TP1, I haven't check later snapshots, but by
the nature of those bugs/ideas I assume they might not have been
implemented yet.

Bugs:

In general: terrible Polish translation - lots of little and big things 
concerning grammar, style and font encoding

QCompleter 
==========

- QCompleter::CompletionMode not defined using Q_ENUMS

- QCompleter should use contentsRect() instead of rect() when determining 
where to show the popup. To see what I mean use the class on a lineedit with 
a significantly reduced contentsRect - i.e. setContentsMargins(0, 0, 30,
0) - the completer should adjust to the frame and not to the whole widget
width

Designer
=======

user interface mode settings - GUI design rules imply that if you 
choose between options a combobox should not be used. Instead use radio 
buttons or a check box.

Designer needs a working Internet connection (or lack of networking at all)
to 
be able to start with the WebKit plugin (a simple iptables -I OUTPUT -j DROP 
will probably replicate the bug). If it doesn't receive what it waits for,
it hangs indefinitely. It should either timeout or not depend on QtNetwork 
instead.

I have a custom multipage container widget plugin for a widget that is
derived from QScrollArea. Because of the base class it is handled
incorrectly by Designer - the container infrastructure is not present
(despite the fact that the container extension and a property sheet
extension are present) - it behaves like a plain QScrollArea. If I wrap the
widget (in code, not in Designer) into a QWidget (so that it is derived
from QWidget and not QScrollArea) Designer handles it correctly.

Providing an "addpagemethod" entry in domXml() for a container widget makes
it usable by UIC but at the same time renders it unusable from Designer -
when you preview such a widget or load a form containing it, it gets
wrecked. I can provide code for my widgets to verify that if you want (but
it's a part of a larger widget set, so it'd be easier if you had yours -
you can probably verify it with the multiple page widget example bundled
with Qt).

Suggestions:

In general:
=========

Try to reduce the size of debugging symbols for Qt libraries. The whole set
of symbols (tested under Linux/x86 with external debugging data) requires
almost 600MB of memory to load, that's definitely too much and slows down
the debugging process very much. Maybe an option to have a reduced set of
symbols or a decision of having (or not) the symbols at all per library
would be possible? For example so that the compilation process could
discard symbols from less frequently used/interesting libraries and only
leave those for i.e. QtCore and QtGui. I know I can delete appropriate
files myself but it still bloats all the libs during compilation (requiring
~3GB or disk space to compile full suite) and requires an unnecessary user
intervention (making things even more problematic when access to the
mentioned files is restricted).

Make it possible to build qmake using multiple build threads (aka
make -j<N>). It is possible to compile the libraries themselves using
multiple compilation threads, but not qmake (at least without breaking the
configuration process and calling make -j<N> on qmake manually). This would
reduce compilation time significantly and this is especially important when
building optimized qmake.

QLineEdit
========

make the widget (and probably all others that are similar) emit a signal
when its read only state changes or signal the change through changeEvent

Designer
=======

make it possible to drag & drop QActions on _all_ widgets and call 
addAction() behind the scenes. Of course an ability to remove actions from
the context menu would be needed as well


There is more where that came from ;)

With kind regards,
  W.


-- 
 [ signature omitted ] 

Message 2 in thread

Witold Wysota wrote:
>Try to reduce the size of debugging symbols for Qt libraries. The whole
> set of symbols (tested under Linux/x86 with external debugging data)
> requires almost 600MB of memory to load, that's definitely too much and
> slows down the debugging process very much. Maybe an option to have a
> reduced set of symbols or a decision of having (or not) the symbols at
> all per library would be possible? For example so that the compilation
> process could discard symbols from less frequently used/interesting
> libraries and only leave those for i.e. QtCore and QtGui. I know I can
> delete appropriate files myself but it still bloats all the libs during
> compilation (requiring ~3GB or disk space to compile full suite) and
> requires an unnecessary user intervention (making things even more
> problematic when access to the mentioned files is restricted).

QtGui is the big issue. So we should discard symbols from that one, right?

-rw-r--r-- 1 tmacieir tmacieir  81M 2008-01-17 22:16 
libQtGui.so.4.4.0.debug

(Note: QtWebKit is more than twice as big)

I am not sure that we'll be incorporating that idea, though...

>Make it possible to build qmake using multiple build threads (aka
>make -j<N>). It is possible to compile the libraries themselves using
>multiple compilation threads, but not qmake (at least without breaking
> the configuration process and calling make -j<N> on qmake manually).
> This would reduce compilation time significantly and this is especially
> important when building optimized qmake.

Sure it's possible:
MAKEFLAGS=-jN ./configure .....

I always do that. My MAKEFLAGS is set by the session itself in order to 
apply to all builds.

But the response from qt-bugs was bizarre. All bug reports and suggestions 
are supposed to be accepted there.

-- 
 [ signature omitted ] 

Attachment: signature.asc
Description: This is a digitally signed message part.


Message 3 in thread

Thiago Macieira wrote:

> 
> QtGui is the big issue. So we should discard symbols from that one, right?
> 
> -rw-r--r-- 1 tmacieir tmacieir  81M 2008-01-17 22:16
> libQtGui.so.4.4.0.debug
> 
> (Note: QtWebKit is more than twice as big)
> 
> I am not sure that we'll be incorporating that idea, though...
> 

For me the bigget two are GUI and WebKit - 118 and 214 MB respectively. If
you add all the smaller libraries to that plus an additional burden of
application (like Designer) symbols and non-Qt libs, that's a huge number
of symbols. I have to wait almost a minute for gdb to become responsive
when starting debugging. And it's really hard to actually compile the whole
suite on a system with limited quota. The whole situation probably gets
even worse on 64b architectures. Debugging a memory demanding application
may become pain in the... neck even on more high-end systems.

> Sure it's possible:
> MAKEFLAGS=-jN ./configure .....

Ok, but it's easy to make that an option of configure, isn't it? If we have
Qt concurrent then concurency should be supported on other "levels" as
well.


> But the response from qt-bugs was bizarre. All bug reports and suggestions
> are supposed to be accepted there.

I think it's because I was referring to functionality which was added in
4.4, so they might not have been aware of all of that or they didn't want
to dig into code that is subject to swift changes anyway. And I don't blaim
them although it is a bad motivation for reporting bugs. I waited 10 days
to post them here again.

Regards,
  W.

-- 
 [ signature omitted ] 

Message 4 in thread

Witold Wysota wrote:
> QLineEdit
> ========
> make the widget (and probably all others that are similar) emit a signal
> when its read only state changes or signal the change through changeEvent

Hi, Witold. What's the particular use case for this? I know there's been
requests for general property change sniffing before, is this what you're
requesting?

> There is more where that came from ;)

Bring it on!

-- 
 [ signature omitted ] 

Message 5 in thread

Andreas Aardal Hanssen wrote:
 
> Hi, Witold. 

Hi Andreas,

> What's the particular use case for this? I know there's been 
> requests for general property change sniffing before, is this what you're
> requesting?

Long story short - I subclassed QLineEdit and added a button to it that is
to reset text to its original state (or another usecase - clear it like in
KDE4 or sth) and it should be shown/hidden or enabled/disabled depending on
the readOnly property (because there is no point in showing the button if
you can't use it). Currently I use a workaround of checking the readOnly
property in paintEvent() as the widget gets redrawn after changing its
state and I update the button respectively there, but it's a silly thing to
use paint event for that.

void QwwClearLineEdit::paintEvent(QPaintEvent *ev)
{
    // hack to make sure the button is disabled 
    // when the widget is made read only
    if(!vis && isReadOnly()){
        // widget became read only
        button()->setVisible(false);
        vis = true;
    } else if(vis && !isReadOnly()){
        // widget stopped being read only
        button()->setVisible(!text().isEmpty());
        vis = false;
    }
    QwwButtonLineEdit::paintEvent(ev);
}

Having a general method of detecting changes to properties would indeed
solve the problem, but it'd be an overhead - properties change very often.
Adding a readOnlyChanged() signal to widgets would be simple and wouldn't
break the ABI. Using changeEvent() as I mentioned in my report is an option
as well (so that the API is not cluttered with lots of signals a limited
number of people would use).

> 
>> There is more where that came from ;)
> 
> Bring it on!

I will when I have a bit of time to gather my conclusions.

Cheers,


-- 
 [ signature omitted ] 

Message 6 in thread

Witold Wysota wrote:
> Long story short - I subclassed QLineEdit and added a button to it that is
> to reset text to its original state (or another usecase - clear it like in
> KDE4 or sth) and it should be shown/hidden or enabled/disabled depending
> ...
> Having a general method of detecting changes to properties would indeed
> solve the problem, but it'd be an overhead - properties change very often.
> Adding a readOnlyChanged() signal to widgets would be simple and wouldn't
> break the ABI. Using changeEvent() as I mentioned in my report is an
> option as well (so that the API is not cluttered with lots of signals a
> limited number of people would use).

Hm. But if you're the one adding the button, you're also changing the r/o
property yourself, do you still really need notification? :-)

-- 
 [ signature omitted ] 

Message 7 in thread

Andreas Aardal Hanssen wrote:
> 
> Hm. But if you're the one adding the button, you're also changing the r/o
> property yourself,

Not really. The button is part of the lineedit's subclass, the lineedit's
readOnly property might be changed in Designer (or in code) and there is no
direct access to the button. The class looks more or less like this:

class X : public QLineEdit {
public:
 X(...) : QLineEdit(...){
    b = new QToolButton(this);
    setContentsMargins(0, 0, b->sizeHint().width()+1, 0);
 }
protected:
  void resizeEvent(...){
    b->move(contentsRect().right()+1, 0);
    ...
  }
private:
  QToolButton *b;
};

Anyway this is only a use case. A general rule is that if you use QLineEdit
itself, you don't need the notification, because you are the one who
changed the property in the first place, but if you subclass and want to
react to changes within the subclassed widget (code modularity and stuff),
you are not the one changing the property thus such a notification might be
useful. In this case changeEvent() would probably be a better choice
because it embeds the notification in the widget without notifying the
environment.


> do you still really need notification? :-) 

Yep :) Unless of course you consider the paintEvent workaround a good
design :)

A side note - the funny thing is that having:

Q_PROPERTY(type name READ name WRITE setName NOTIFY nameChanged)

could allow moc to generate code that would emit nameChanged() or even
nameChanged(...) by itself, without having to modify anything in the class
code. Moc would just have to track if the change really occured.


You know what? Now that I think of it, there is a general problem with Qt
widgets (hi, Widget Team Leader! ;)). Their API is fine if you just want to
use those ready made widgets, but it's often hard to subclass them to add
new functionality. Either the required functionality is hidden inside
P-IMPL or the API just lacks a protected or virtual protected method to
access what is needed. And unfortunately protecting the ABI reduces the
chance of having such a functionality introduced at a later stage of
development.

Cheers,
  W.

-- 
 [ signature omitted ] 

Message 8 in thread

On Friday 18 January 2008 01:23, Witold Wysota wrote:

> Bugs:
>
> In general: terrible Polish translation - lots of little and big things
> concerning grammar, style and font encoding
>

The Polish translation was not updated yet. I'm currently working on it. 
However if you see any bug in grammar or style feel free to post your 
correction/suggestion.

> Designer
> =======
>
> user interface mode settings - GUI design rules imply that if you
> choose between options a combobox should not be used. Instead use radio
> buttons or a check box.
>

Both a combobox and a set of radiobuttons provide a functionality of choosing 
between options. However the combobox is more suitable when there are more 
than 4-5 options. Changing it to a checkbox is not a good idea because we 
have here 2 options, not 1 which can be switched on or off. So your 
suggestion about changing it to set of radio buttons is noted.

> Designer needs a working Internet connection (or lack of networking at all)
> to
> be able to start with the WebKit plugin (a simple iptables -I OUTPUT -j
> DROP will probably replicate the bug). If it doesn't receive what it waits
> for, it hangs indefinitely. It should either timeout or not depend on
> QtNetwork instead.

We'll check that.

> I have a custom multipage container widget plugin for a widget that is
> derived from QScrollArea. Because of the base class it is handled
> incorrectly by Designer - the container infrastructure is not present
> (despite the fact that the container extension and a property sheet
> extension are present) - it behaves like a plain QScrollArea. If I wrap the
> widget (in code, not in Designer) into a QWidget (so that it is derived
> from QWidget and not QScrollArea) Designer handles it correctly.
>

Could you post an example? It would help us understand your issue.

> Providing an "addpagemethod" entry in domXml() for a container widget makes
> it usable by UIC but at the same time renders it unusable from Designer -
> when you preview such a widget or load a form containing it, it gets
> wrecked. I can provide code for my widgets to verify that if you want (but
> it's a part of a larger widget set, so it'd be easier if you had yours -
> you can probably verify it with the multiple page widget example bundled
> with Qt).
>

An example here would also help.

> Designer
> =======
>
> make it possible to drag & drop QActions on _all_ widgets and call
> addAction() behind the scenes. Of course an ability to remove actions from
> the context menu would be needed as well
>

Noted.

Thanks for your feedback!

Regards

Jarek

To unsubscribe - send "unsubscribe" in the subject to qt4-preview-feedback-request@xxxxxxxxxxxxx


Message 9 in thread

Jarek Kobus wrote:
 
> The Polish translation was not updated yet. I'm currently working on it.
> However if you see any bug in grammar or style feel free to post your
> correction/suggestion.

I'll try to assemble a list.

> Changing it to a checkbox is not a good idea
> because we have here 2 options, not 1 which can be switched on or off.

Agreed.

 
>> Designer needs a working Internet connection (or lack of networking at
>> all) to
>> be able to start with the WebKit plugin (a simple iptables -I OUTPUT -j
>> DROP will probably replicate the bug). If it doesn't receive what it
>> waits for, it hangs indefinitely. It should either timeout or not depend
>> on QtNetwork instead.
> 
> We'll check that.

I think I am a bit mistaken here because I removed the WebKit plugin and it
still hangs when the network is down (although reported as up). When there
is no active network interface, it seems to work fine.
 
>> I have a custom multipage container widget plugin for a widget that is
>> derived from QScrollArea. Because of the base class it is handled
>> incorrectly by Designer - the container infrastructure is not present
>> (despite the fact that the container extension and a property sheet
>> extension are present) - it behaves like a plain QScrollArea. If I wrap
>> the widget (in code, not in Designer) into a QWidget (so that it is
>> derived from QWidget and not QScrollArea) Designer handles it correctly.
>>
> 
> Could you post an example? It would help us understand your issue.

I will, but I have to craft a container example. My containers rely on my
code, so it's hard to post them.

>> Providing an "addpagemethod" entry in domXml() for a container widget
>> makes it usable by UIC but at the same time renders it unusable from
>> Designer - when you preview such a widget or load a form containing it,
>> it gets wrecked. I can provide code for my widgets to verify that if you
>> want (but it's a part of a larger widget set, so it'd be easier if you
>> had yours - you can probably verify it with the multiple page widget
>> example bundled with Qt).
>>
> 
> An example here would also help.

Try the multipage container example that comes with Qt. It should be broken
as well. Compile the plugin, run Designer, add some widgets to the form,
save the form, restart Designer and reload the form or just preview the
form.

Attached you will find two images showing what I mean - "form" is the form
during design and "preview" is the same form when previewing it. The widget
is the "outlook navigation bar" clone, so it's easy to imagine how it
should look like.

And the domXml():

QString QwwNavigationBarIface::domXml() const {
    return QString("<ui>\
                   <widget class=\"QwwNavigationBar\"
name=\"navigationBar\">\
                   <widget class=\"QWidget\" name=\"page1\" />\
                   <widget class=\"QWidget\" name=\"page2\" />\
                   </widget>\
                   <customwidgets>\
                        <customwidget>\
                            <class>QwwNavigationBar</class>\
                            <extends>QWidget</extends>\
                            <addpagemethod>addWidget</addpagemethod>\
                        </customwidget>\
                    </customwidgets>\
                   </ui>");
}

When you remove the "customwidgets" section, the preview works fine, but UIC
generates wrong code.

Regards,
  W.

-- 
 [ signature omitted ] 

Attachment: form.png
Description: PNG image

Attachment: preview.png
Description: PNG image


Message 10 in thread

On Friday 18 January 2008 11:43, Witold Wysota wrote:
> Jarek Kobus wrote:
> > The Polish translation was not updated yet. I'm currently working on it.
> > However if you see any bug in grammar or style feel free to post your
> > correction/suggestion.
>
> I'll try to assemble a list.
>

Are you able to verify your list against the translation taken from 4.4 
snapshot?

> >> Providing an "addpagemethod" entry in domXml() for a container widget
> >> makes it usable by UIC but at the same time renders it unusable from
> >> Designer - when you preview such a widget or load a form containing it,
> >> it gets wrecked. I can provide code for my widgets to verify that if you
> >> want (but it's a part of a larger widget set, so it'd be easier if you
> >> had yours - you can probably verify it with the multiple page widget
> >> example bundled with Qt).
> >
> > An example here would also help.
>
> Try the multipage container example that comes with Qt. It should be broken
> as well. Compile the plugin, run Designer, add some widgets to the form,
> save the form, restart Designer and reload the form or just preview the
> form.
>
> Attached you will find two images showing what I mean - "form" is the form
> during design and "preview" is the same form when previewing it. The widget
> is the "outlook navigation bar" clone, so it's easy to imagine how it
> should look like.
>
> And the domXml():
>
> QString QwwNavigationBarIface::domXml() const {
>     return QString("<ui>\
>                    <widget class=\"QwwNavigationBar\"
> name=\"navigationBar\">\
>                    <widget class=\"QWidget\" name=\"page1\" />\
>                    <widget class=\"QWidget\" name=\"page2\" />\
>                    </widget>\
>                    <customwidgets>\
>                         <customwidget>\
>                             <class>QwwNavigationBar</class>\
>                             <extends>QWidget</extends>\
>                             <addpagemethod>addWidget</addpagemethod>\
>                         </customwidget>\
>                     </customwidgets>\
>                    </ui>");
> }
>
> When you remove the "customwidgets" section, the preview works fine, but
> UIC generates wrong code.
>

Could you check if making your addWidget() method a public slot helps?

Regards

Jarek

To unsubscribe - send "unsubscribe" in the subject to qt4-preview-feedback-request@xxxxxxxxxxxxx


Message 11 in thread

Jarek Kobus wrote:

> On Friday 18 January 2008 11:43, Witold Wysota wrote:
>> Jarek Kobus wrote:
>> > The Polish translation was not updated yet. I'm currently working on
>> > it. However if you see any bug in grammar or style feel free to post
>> > your correction/suggestion.
>>
>> I'll try to assemble a list.
>>
> 
> Are you able to verify your list against the translation taken from 4.4
> snapshot?

Yes, of course. It should be as simple as taking the .ts file and feeding it
to the TP1 version. I'll send you a mail when I have the list. Discussing
it in Polish will be much simpler as the grammar is involved.

> 
> Could you check if making your addWidget() method a public slot helps?
> 

Looks like it does (thanks, you saved my day). At least I can't break it
after a minute of playing with it. I didn't see it mentioned anywhere in
the docs though. Actually there was nothing about the domXml's contents
regarding the "addpagemethod" tag, only an example of the method itself.

By the way are there any other things one can put inside domXml() to make
different things happen? :)

Regards,
  W.

-- 
 [ signature omitted ] 

Message 12 in thread

On Friday 18 January 2008 14:55, Witold Wysota wrote:
> Jarek Kobus wrote:
> > On Friday 18 January 2008 11:43, Witold Wysota wrote:
> >> Jarek Kobus wrote:
> >> > The Polish translation was not updated yet. I'm currently working on
> >> > it. However if you see any bug in grammar or style feel free to post
> >> > your correction/suggestion.
> >>
> >> I'll try to assemble a list.
> >
> > Are you able to verify your list against the translation taken from 4.4
> > snapshot?
>
> Yes, of course. It should be as simple as taking the .ts file and feeding
> it to the TP1 version. I'll send you a mail when I have the list.
> Discussing it in Polish will be much simpler as the grammar is involved.
>

Cool!

> > Could you check if making your addWidget() method a public slot helps?
>
> Looks like it does (thanks, you saved my day). At least I can't break it
> after a minute of playing with it. I didn't see it mentioned anywhere in
> the docs though. Actually there was nothing about the domXml's contents
> regarding the "addpagemethod" tag, only an example of the method itself.
>

It's not mentioned because it should work :). We will try to fix it. Making it 
a slot it just a temporary workaround.

> By the way are there any other things one can put inside domXml() to make
> different things happen? :)

I'm not conscious of them at least :)

Regards

Jarek

To unsubscribe - send "unsubscribe" in the subject to qt4-preview-feedback-request@xxxxxxxxxxxxx


Message 13 in thread

Jarek Kobus wrote:

>> I have a custom multipage container widget plugin for a widget that is
>> derived from QScrollArea. Because of the base class it is handled
>> incorrectly by Designer - the container infrastructure is not present
>> (despite the fact that the container extension and a property sheet
>> extension are present) - it behaves like a plain QScrollArea. If I wrap
>> the widget (in code, not in Designer) into a QWidget (so that it is
>> derived from QWidget and not QScrollArea) Designer handles it correctly.
>>
> 
> Could you post an example? It would help us understand your issue.
> 

Full version (although not compilable unless you remove code relevant to
other classes) attached. Short version:

void QwwTaskPanelIface::initialize(QDesignerFormEditorInterface * core) {
    if(isInitialized())
        return;
    wwWidgetInterface::initialize(core);
    formEditor = core;
    QExtensionManager *manager = formEditor->extensionManager();
    QExtensionFactory *factory = new QwwWidgetsExtensionFactory(manager);

    Q_ASSERT(manager != 0);
    manager->registerExtensions(factory,
Q_TYPEID(QDesignerContainerExtension));
    manager->registerExtensions(factory,
Q_TYPEID(QDesignerPropertySheetExtension));
    QTimer::singleShot(1000, this, SLOT(execute()));
}

QwwTaskPanelIface is the custom widget plugin of the widget,
wwWidgetInterface is its base class derived from the proper interface, but
it's irrelevant.

Here I register container and property sheet extensions. Contents of those
extensions themselves are irrelevant, so I won't post them here.

The extension factory:

#define WW_REGISTER_CONTAINEREXTENSION(cl, object, parent) \
    if(cl *widget = qobject_cast<cl*>(object)) \
        return new cl ## ContainerExtension(widget, parent);

QObject * QwwWidgetsExtensionFactory::createExtension(QObject * object,
const QString & iid, QObject * parent) const {
...
if (iid == Q_TYPEID(QDesignerContainerExtension)) {
#ifndef WW_NO_TASKPANEL
        WW_REGISTER_CONTAINEREXTENSION(QwwTaskPanel, object, parent);
#endif
...
} else if (iid == Q_TYPEID(QDesignerPropertySheetExtension)) {
        if (cheatmutex.tryLock()) {
#ifndef WW_NO_TASKPANEL
            if (QwwTaskPanel *widget = qobject_cast<QwwTaskPanel*>(object) )
{
                QObject *otherExtension = m_manager->extension(object, iid);
                cheatmutex.unlock();
                return new QwwTaskPanelSheetExtension(widget,
otherExtension, parent);
            }
#endif
...


As for the widget itself:

class Q_WW_EXPORT QwwTaskPanel : public QScrollArea {
...
};

The effect is that extensions for the class are not doing anything in
Designer. The widget has a classic QScrollArea interface. Now if you change
its base class to QWidget and wrap the scroll area in it, magically
everything starts to work (but you lose the scroll area properties) -
additional properties and the container context menu appear.

I suspect the reason to be that extensions are queried and an extension
associated with QScrollArea is found and accepted first before mine is
queried so mine is not even triggered for the widget. I think it's enough
to make sure that extensions embedded inside Designer query for other
extensions for the same widget and merge their functionality. If you take a
look at my code, that's what my property sheet extension does.

Just to answer a probable question - everything worked fine in 4.3.3, so the
changes made to Designer 4.4 are responsible for the change of the
behaviour.

Regards,
  W.

-- 
 [ signature omitted ] 
//
// C++ Implementation: %{MODULE}
//
// Description:
//
//
// Author: %{AUTHOR} <%{EMAIL}>, (C) %{YEAR}
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef WW_NO_TASKPANEL
#include "qwwtaskpanel.h"
#include "qwwtaskpanel_p.h"


/*
 *
 *  This class needs a complete rewrite
 *
 *  It needs an own layout that will handle animating its items,
 *  just like QMainWindowLayout does the same for QMainWindow
 *
 */

TaskHeader::TaskHeader(QWidget *w, QWidget *parent) : QFrame(parent) {
    m_widget = w;
    setFrameShape(QFrame::StyledPanel);
    setFrameRect(rect().adjusted(0, 8, 0, 0));
    setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
    QHBoxLayout *l = new QHBoxLayout(this);
    l->setMargin(1);
    m_spacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
    l->addItem(m_spacer);
    m_text = new QLabel;
    QFont f = m_text->font();
    f.setBold(true);
    m_text->setFont(f);
    m_button = new QToolButton;
    m_button->setObjectName("__qt__passive_button");
    m_button->setAutoRaise(true);
    m_button->setCheckable(true);
    m_button->setArrowType(Qt::DownArrow);
    l->addWidget(m_text);
    l->addWidget(m_button);
}

void TaskHeader::setToggleIcon(const QIcon &i) {
    m_button->setIcon(i);
    if (i.isNull()) {
        m_button->setArrowType(m_button->isChecked() ? Qt::UpArrow : Qt::DownArrow);
    } else {
        m_button->setArrowType(Qt::NoArrow);
    }
}

void TaskHeader::setTaskName(const QString &n) {
    m_text->setText(n);
    layout()->invalidate();
    update();
}

void TaskHeader::setIcon(const QIcon &i) {
    m_icon = i;
    if (i.isNull()) {
        m_spacer->changeSize(0,0, QSizePolicy::Fixed, QSizePolicy::Fixed);
    } else {
        m_spacer->changeSize(50,16, QSizePolicy::Fixed, QSizePolicy::Fixed);
    }
    m_spacer->invalidate();
    layout()->invalidate();
    update();
}

void TaskHeader::paintEvent(QPaintEvent *ev) {
    QFrame::paintEvent(ev);
    QPainter p(this);
    m_icon.paint(&p, QRect(2, 1, 32, 32), Qt::AlignCenter, isEnabled() ? QIcon::Normal : QIcon::Disabled);
}

QToolButton* TaskHeader::toggleButton() const {
    return m_button;
}




Task::Task(QWidget *body, QWidget *parent) : QWidget(parent) {
    m_body = body;
    m_animBody = 0;
    m_animator.setDuration(1200);
    m_animator.setUpdateInterval(20);
    m_animator.setCurveShape(QTimeLine::EaseInOutCurve);
    QVBoxLayout *l = new QVBoxLayout(this);
    l->setSpacing(0);
    l->setMargin(0);
    m_header = new TaskHeader(body);

    l->addWidget(m_header);
    l->addWidget(m_body);

    m_body->setVisible(false);
    m_body->installEventFilter(this);
    connect(m_header->toggleButton(), SIGNAL(toggled(bool)), this, SLOT(setOpen(bool)));
    connect(&m_animator, SIGNAL(frameChanged(int)), SLOT(animate(int)));
    connect(&m_animator, SIGNAL(finished()), SLOT(animFinished()));
}

void Task::setOpen(bool o) {
    QToolButton *b = m_header->toggleButton();
    if (b->isChecked() == o) {
        b->setChecked(o);
        if (b->arrowType()!=Qt::NoArrow) {
            if (o) {
                b->setArrowType(Qt::UpArrow);
            } else {
                b->setArrowType(Qt::DownArrow);
            }
        }
        QwwTaskPanel *tp = parent() ? qobject_cast<QwwTaskPanel*>(parent()->parent()->parent()) : 0;
        if(tp && tp->isAnimated()){
        if (m_animator.state()!=QTimeLine::NotRunning) {
            m_animator.setDirection(m_animator.direction()==QTimeLine::Forward ? QTimeLine::Backward : QTimeLine::Forward);
        } else {
            m_animBody = new QWidget;
            m_animBody->installEventFilter(this);
#ifndef Q_WS_WIN
            m_animBody->setEnabled(false);
#endif
            m_animBody->setAttribute(Qt::WA_NoSystemBackground, true);
            body()->ensurePolished();
            QSize s = QLayout::closestAcceptableSize(body(), body()->sizeHint()).expandedTo(QSize(width(), 0));
            body()->resize(s);

            body()->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
            body()->setAttribute(Qt::WA_WState_Hidden, false);
            m_animpix = QPixmap::grabWidget(body());
            body()->setAttribute(Qt::WA_WState_Hidden, true);
            if (o) {
                m_animator.setDirection(QTimeLine::Forward);
                m_animator.setFrameRange(0, s.height());
            } else {
                m_animator.setDirection(QTimeLine::Backward);
                m_animator.setFrameRange(0, m_body->height());
            }
            m_body->hide();
            QVBoxLayout *l = (QVBoxLayout*)layout();
            l->addWidget(m_animBody);
            m_animBody->show();
            m_animator.start();
        }
        } else {
            if(o)
                m_body->show();
            else
                m_body->hide();
        }
    }
}

bool Task::eventFilter(QObject *o, QEvent *e) {
    if (o==m_animBody && e->type()==QEvent::Paint) {
        QPainter p(m_animBody);
        p.drawPixmap(m_animBody->rect(), m_animpix, m_animpix.rect().adjusted(0, m_animpix.height()-m_animBody->height(), 0, 0));
        return true;
    }
//     if (o==m_body && e->type()==QEvent::WindowTitleChange) {
//         setName(m_body->windowTitle());
//     }
//     if (o==m_body && e->type()==QEvent::WindowIconChange) {
//         setIcon(m_body->windowIcon());
//     }
    return false;
}

void Task::animate(int frame ) {
    m_animBody->setFixedSize(m_animBody->width(), frame);
    m_animBody->updateGeometry();
//     m_animBody->update();

}

void Task::animFinished() {
    if (m_animator.currentFrame()!=0) {
        m_body->show();
    }
    m_animBody->lower();
    m_animBody->hide();
    m_animBody->deleteLater();
    m_animBody = 0;
    updateGeometry();
}


/**
 *  @class QwwTaskPanel
 *  @brief Task panel simmilar to the one from WindowsXP.(unstable)
 *
 *
 *
 */

QwwTaskPanel::QwwTaskPanel(QWidget *parent) : QWidget(parent)
        /*: QScrollArea(parent)*/ {
    QScrollArea *sa = new QScrollArea(this);
    QVBoxLayout *la = new QVBoxLayout(this);
    la->setMargin(0);
    la->addWidget(sa);
    m_animated = false;
    m_panel = new QWidget(sa->viewport());
    m_panel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::MinimumExpanding);
    m_panel->setObjectName("ww_taskpanel_panel");
    m_current = -1;
    QVBoxLayout *l = new QVBoxLayout(m_panel);
    l->addStretch();
    sa->setWidget(m_panel);
    sa->setWidgetResizable(true);
    setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
}


QwwTaskPanel::~QwwTaskPanel() {}

void QwwTaskPanel::addTask(QWidget * task, const QString & label) {
    insertTask(taskCount(), task, label);
}

void QwwTaskPanel::addTask(QWidget * task, const QIcon & icon, const QString & label) {
    insertTask(taskCount(), task, icon, label);
}

void QwwTaskPanel::insertTask(int index, QWidget * task, const QString & label) {
    insertTask(index, task, QIcon(), label);
}

void QwwTaskPanel::insertTask(int index, QWidget * task, const QIcon & icon, const QString & label) {
    if (!task) return;
    Task *tsk = new Task(task);
    tsk->setToggleIcon(m_toggleIcon);
    if (label.isNull())
        tsk->setName(task->windowTitle());
    else {
        tsk->setName(label);
        task->setWindowTitle(label);
    }
    if (icon.isNull()) {
        tsk->setIcon(task->windowIcon());
    } else {
        tsk->setIcon(icon);
        task->setWindowIcon(icon);
    }
    static_cast<QBoxLayout*>(m_panel->layout())->insertWidget(index, tsk);
    m_tasks.insert(index, tsk);

    if (m_tasks.count()==1) {
        setCurrentIndex(0);
    }
     tsk->show();
}

int QwwTaskPanel::taskCount() const {
    return m_tasks.count();
}

void QwwTaskPanel::removeTask(int index) {
    if (index < 0 || index>=m_tasks.count()) return;
    Task *tsk = static_cast<Task*>(m_tasks.at(index));
    m_tasks.removeAt(index);
    if (m_tasks.count()<=index) {
        setCurrentIndex(m_tasks.count()-1);
    }
    QWidget *body = tsk->body();
    body->setParent(this);
    delete tsk;
}

QWidget * QwwTaskPanel::task(int index) const {
    if (index < 0 || index>=m_tasks.count()) return 0;
    Task *tsk = static_cast<Task*>(m_tasks.at(index));
    return tsk ? tsk->body() : 0;
}

int QwwTaskPanel::indexOf(QWidget * task) const {
    for (int i=0;i<m_tasks.count();i++) {
        Task *tsk = static_cast<Task*>(m_tasks.at(i));
        if (task == tsk) return i;
    }
    return -1;
}

void QwwTaskPanel::setToggleIcon(const QIcon & icon) {
    m_toggleIcon = icon;
    foreach(QWidget *tsk, m_tasks) {
        static_cast<Task*>(tsk)->setToggleIcon(icon);
    }
}

void QwwTaskPanel::setCurrentIndex(int index) {
    if (index<0 || index>=m_tasks.count() || index==m_current)
        return;
    if (m_current!=-1) {
        Task *tsk = static_cast<Task*>(m_tasks.at(m_current));
        tsk->setOpen(false);
    }
    m_current = index;
    Task *tsk = static_cast<Task*>(m_tasks.at(index));
    tsk->setOpen(true);
    emit currentIndexChanged(index);
}

QWidget * QwwTaskPanel::currentTask() const {
    return task(currentIndex());
}

void QwwTaskPanel::setTaskIcon(int index, const QIcon & icon) {
    if (index < 0 || index>=m_tasks.count()) return;
    Task *tsk = qobject_cast<Task*>(m_tasks.at(index));
    if (!tsk) return;
    tsk->setIcon(icon);

}

void QwwTaskPanel::setTaskTitle(int index, const QString & title) {
    if (index < 0 || index>=m_tasks.count()) return;
    Task *tsk = qobject_cast<Task*>(m_tasks.at(index));
    if (!tsk) return;
    tsk->setName(title);
}

void QwwTaskPanel::setTaskName(int index, const QString & name) {
    if (index < 0 || index>=m_tasks.count()) return;
    Task *tsk = qobject_cast<Task*>(m_tasks.at(index));
    if (!tsk) return;
    tsk->body()->setObjectName(name);
}
#endif

//
//
// C++ Interface: qwwtaskpanel
//
// Description:
//
//
// Author: Witold Wysota <wysota@xxxxxxxxxxxxx>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef QWWTASKPANEL_H
#define QWWTASKPANEL_H
#ifndef WW_NO_TASKPANEL
#include <QScrollArea>
#include <QList>
#include <QIcon>
#include <wwglobal.h>

class Q_WW_EXPORT QwwTaskPanel : public QWidget/*QScrollArea*/ {
    Q_OBJECT
    Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
    Q_PROPERTY(QIcon toggleIcon READ toggleIcon WRITE setToggleIcon)
    Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated)
public:
    QwwTaskPanel(QWidget *parent = 0);
    ~QwwTaskPanel();
    void addTask(QWidget *task, const QString &label = QString());
    void addTask(QWidget *task, const QIcon &icon, const QString &label = QString());

    void insertTask(int index, QWidget *task, const QString &label = QString());
    void insertTask(int index, QWidget *task, const QIcon &icon, const QString &label = QString());
    void removeTask(int index);
    int taskCount() const;
    QWidget *task(int index) const;
    QWidget *currentTask() const;
    int indexOf(QWidget *task) const;
    const QIcon &toggleIcon() const { return m_toggleIcon; }
    void setToggleIcon(const QIcon &icon);
    void setTaskIcon(int index, const QIcon &icon);
    void setTaskTitle(int index, const QString &title);
    void setTaskName(int index, const QString &name);
    int currentIndex() const { return m_current; }
    bool isAnimated() const { return m_animated; }
    //void setCurrentTask(QWidget *task);
public slots:
    void setCurrentIndex(int index);
    void setAnimated(bool a){ m_animated = a; }
signals:
    void currentIndexChanged(int);
private:
    QList<QWidget*> m_tasks;
    QWidget *m_panel;
    QIcon m_toggleIcon;
    int m_current;
    bool m_animated;
};


#endif
#endif

//
//
// C++ Implementation: %{MODULE}
//
// Description:
//
//
// Author: %{AUTHOR} <%{EMAIL}>, (C) %{YEAR}
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef WW_NO_TASKPANEL

#include "qwwtaskpaneliface.h"
#include "qwwtaskpanel.h"
#include <QtDesigner/QExtensionManager>
#include <QtDesigner/QDesignerFormEditorInterface>
#include <QtDesigner/QDesignerFormWindowInterface>
#include <QtDesigner/QDesignerContainerExtension>
#include <QtDesigner/QDesignerPropertySheetExtension>

#include "qwwtaskpanel/qwwtaskpanel_p.h"
#include <QChildEvent>
#include <QMainWindow>
#include <QMenuBar>

#include "wwwidgets.h"
// #include "propertymatcher.h"

QwwTaskPanelIface::QwwTaskPanelIface(QObject *parent)
        : wwWidgetInterface(parent) {
}


QwwTaskPanelIface::~QwwTaskPanelIface() {}


QWidget * QwwTaskPanelIface::createWidget(QWidget * parent) {
    QwwTaskPanel *panel = new QwwTaskPanel(parent);
    connect(panel, SIGNAL(currentIndexChanged(int)),
            this, SLOT(currentIndexChanged(int)));
    QWidget *w = qFindChild<QWidget*>(panel, "ww_taskpanel_panel");
    if (w) {
        w->installEventFilter(this);
    }
    return panel;
}

QIcon QwwTaskPanelIface::icon() const {
    return QPixmap(":/trolltech/formeditor/images/widgets/widgetstack.png");
}

void QwwTaskPanelIface::initialize(QDesignerFormEditorInterface * core) {
    if(isInitialized())
        return;
    wwWidgetInterface::initialize(core);
    formEditor = core;
    QExtensionManager *manager = formEditor->extensionManager();
    QExtensionFactory *factory = new QwwWidgetsExtensionFactory(manager);

    Q_ASSERT(manager != 0);
    manager->registerExtensions(factory, Q_TYPEID(QDesignerContainerExtension));
    manager->registerExtensions(factory, Q_TYPEID(QDesignerPropertySheetExtension));
    QTimer::singleShot(1000, this, SLOT(execute()));
}

QwwTaskPanelContainerExtension::QwwTaskPanelContainerExtension(QwwTaskPanel * widget, QObject *parent ) : QObject(parent){
    myWidget = widget;
}

QWidget * QwwTaskPanelContainerExtension::widget(int index) const {
    return myWidget->task(index);
}

void QwwTaskPanelContainerExtension::setCurrentIndex(int index) {
    myWidget->setCurrentIndex(index);
}

void QwwTaskPanelContainerExtension::remove(int index) {
    myWidget->removeTask(index);
}

void QwwTaskPanelContainerExtension::insertWidget(int index, QWidget * widget) {
    myWidget->insertTask(index, widget);
}

int QwwTaskPanelContainerExtension::currentIndex() const {
    return myWidget->currentIndex();
}

int QwwTaskPanelContainerExtension::count() const {
    return myWidget->taskCount();
}

void QwwTaskPanelContainerExtension::addWidget(QWidget * widget) {
    myWidget->addTask(widget);
}

QString QwwTaskPanelIface::domXml() const {
    return QString("<ui>\
                   <widget class=\"QwwTaskPanel\" name=\"taskPanel\">\
                    <widget class=\"QWidget\" name=\"task\" >\
                        <property name=\"windowTitle\">\
                            <string>Unnamed</string>\
                        </property>\
                    </widget>\
                   </widget></ui>");
//                    <customwidgets>\
//                    <customwidget>\
//                    <class>QwwTaskPanel</class>\
//                             <extends>QWidget</extends>\
//                             <addpagemethod>addTask</addpagemethod>\
//                    </customwidget>\
//                    </customwidgets>\
//                    </ui>");
}

void QwwTaskPanelIface::currentIndexChanged(int )
{
    QwwTaskPanel *widget = qobject_cast<QwwTaskPanel*>(sender());
    if (widget) {
        QDesignerFormWindowInterface *form;
        form = QDesignerFormWindowInterface::findFormWindow(widget);
        if (form)
            form->emitSelectionChanged();
    }
}

bool QwwTaskPanelIface::eventFilter(QObject * , QEvent * e)
{
//     if(e->type() == QEvent::ChildAdded){
//         QChildEvent *chev = static_cast<QChildEvent*>(e);
//         Task *tsk = qobject_cast<Task*>(chev->child());
//         if(tsk){
//             tsk->body()->setMinimumHeight(200);
//         }
//     }
    return false;
}

void QwwTaskPanelIface::execute()
{
    if(formEditor->topLevel()){
        QMainWindow *mw = dynamic_cast<QMainWindow*>(formEditor->topLevel());
        if(!mw) return;
        wwWidgetsMenu *menu = new wwWidgetsMenu("wwWidgets");
        mw->menuBar()->addMenu(menu);
    }

}



bool QwwTaskPanelSheetExtension::isChanged(int index) const
{
    int myindex = other() ? other()->count() : 0;
    if(index>=myindex) return false;
    if(other()){
        return other()->isChanged(index);
    }
    return false;
}


// int QwwTaskPanelSheetExtension::indexOf(const QString & name) const
// {
//     if(name=="currentTaskTitle") return myindex+2;
//     if(name=="currentTaskName") return myindex;
//     if(name=="currentTaskIcon") return myindex+1;
//     if(m_other){
//         return m_other->indexOf(name);
//     }
//     return -1;
// }

bool QwwTaskPanelSheetExtension::hasReset(int index) const
{
    int myindex = other() ? other()->count() : 0;
    if(index==myindex+1) return true;
    if(index==myindex+2) return false;
    if(index==myindex) return true;
    if(other()){
        return other()->hasReset(index);
    }
    return false;
}

// QString QwwTaskPanelSheetExtension::propertyName(int index) const
// {
//     if(index==myindex+2) return "currentTaskTitle";
//     if(index==myindex+1) return "currentTaskIcon";
//     if(index==myindex) return "currentTaskName";
//     if(m_other){
//         return m_other->propertyName(index);
//     }
//     return QString::null;
// }

// QString QwwTaskPanelSheetExtension::propertyGroup(int index) const
// {
//     if(index>=myindex && index<myindex+tp_propcnt) return "QwwTaskPanel";
//     if(m_other){
//         return m_other->propertyGroup(index);
//     }
//     return "";
// }

QVariant QwwTaskPanelSheetExtension::property(int index) const
{
    int myindex = other() ? other()->count() : 0;
    if(!m_panel)
	return QVariant();
    if(!m_panel->currentTask() && index >=myindex)
        return QVariant();
    if(index==myindex+2) return m_panel->currentTask()->windowTitle();
    if(index==myindex+1) return m_panel->currentTask()->windowIcon();
    if(index==myindex+0) return m_panel->currentTask()->objectName();
    if(other()){
        return other()->property(index);
    }
    return QVariant();
}

void QwwTaskPanelSheetExtension::setAttribute(int index, bool attribute)
{
    int myindex = other() ? other()->count() : 0;
    if(index==myindex) return;
    if(index==myindex+1) return;
    if(index==myindex+2) return;
    if(other()){
        return other()->setAttribute(index, attribute);
    }
}

bool QwwTaskPanelSheetExtension::reset(int index)
{
   int myindex = other() ? other()->count() : 0;
   if(index==myindex+1){
        QWidget *task = m_panel->currentTask();
        m_panel->setTaskIcon(m_panel->currentIndex(), QIcon());
        setPropertyChanged(task, "windowIcon");
        return true;

    }
    if(index==myindex){
        QWidget *task = m_panel->currentTask();
        QDesignerFormWindowInterface *form = QDesignerFormWindowInterface::findFormWindow(m_panel);
    if (form && task) {
        QDesignerFormEditorInterface *editor = form->core();
        QExtensionManager *manager = editor->extensionManager();
        QDesignerPropertySheetExtension *sheet;
        sheet = qt_extension<QDesignerPropertySheetExtension*>(manager, task);
        int propertyIndex = sheet->indexOf("objectName");
        if(sheet->hasReset(propertyIndex))
        return sheet->reset(propertyIndex);
        else return false;
    }
    }
    if(index==myindex+2) return false;
    if(other()){
        return other()->reset(index);
    }
    return false;
}

void QwwTaskPanelSheetExtension::setProperty(int index, const QVariant & value)
{
    int myindex = other() ? other()->count() : 0;
    if(index==myindex){
        QWidget *task = m_panel->currentTask();
        m_panel->setTaskName(m_panel->currentIndex(), value.toString());
        setPropertyChanged(task, "objectName");
        return;
    }
    if(index==myindex+1){
        QWidget *task = m_panel->currentTask();
        m_panel->setTaskIcon(m_panel->currentIndex(), qvariant_cast<QIcon>(value));
//         task->setWindowTitle
        setPropertyChanged(task, "windowIcon");
        return;
    }
    if(index==myindex+2){
        QWidget *task = m_panel->currentTask();
        m_panel->setTaskTitle(m_panel->currentIndex(), value.toString());
        setPropertyChanged(task, "windowTitle");
        return;
    }
    if(other()){
        return other()->setProperty(index, value);
    }
}

void QwwTaskPanelSheetExtension::setChanged(int index, bool changed)
{
    int myindex = other() ? other()->count() : 0;
    if(index==myindex) return;
    if(index==myindex+1) return;
    if(index==myindex+2) return;
    if(other()){
        return other()->setChanged(index, changed);
    }
}

void QwwTaskPanelSheetExtension::setPropertyGroup(int index, const QString & group)
{
    int myindex = other() ? other()->count() : 0;
    if(index==myindex) return;
    if(index==myindex+2) return;
    if(index==myindex+1) return;
    if(other()){
        return other()->setPropertyGroup(index, group);
    }
}

void QwwTaskPanelSheetExtension::setVisible(int index, bool visible)
{
    int myindex = other() ? other()->count() : 0;
    if(index==myindex) return;
    if(index==myindex+1) return;
    if(index==myindex+2) return;
    if(other()){
        return other()->setVisible(index, visible);
    }
}

// class TaskPanelPropertyMatcher : public PropertyMatcher {
// public:
//     TaskPanelPropertyMatcher(QwwTaskPanel *panel){
//         m_panel = panel;
//     }
//     void setProperty(QObject *o, const QString &name, const QVariant &value){
//
//         QWidget *task = m_panel->currentTask();
//         int index = m_panel->currentIndex();
//         QDesignerFormWindowInterface *form = QDesignerFormWindowInterface::findFormWindow(m_panel);
//         if (form && task) {
//             QDesignerFormEditorInterface *editor = form->core();
//             QExtensionManager *manager = editor->extensionManager();
//             QDesignerPropertySheetExtension *sheet;
//             sheet = qt_extension<QDesignerPropertySheetExtension*>(manager, task);
//             int propertyIndex = sheet->indexOf(QLatin1String("windowIcon"));
//             sheet->setChanged(propertyIndex, true);
//         }
//
//     }
// };


QwwTaskPanelSheetExtension::QwwTaskPanelSheetExtension(QWidget *w, QObject *other, QObject *parent) : wwSheetExtension(QStringList() << "currentTaskName" << "currentTaskIcon" << "currentTaskTitle", qobject_cast<QDesignerPropertySheetExtension*>(other), parent)
{
    m_panel = qobject_cast<QwwTaskPanel*>(w);
    //     m_matcher = new TaskPanelPropertyMatcher(m_panel);
}

QwwTaskPanelSheetExtension::~ QwwTaskPanelSheetExtension()
{
    //     delete m_matcher;
}

void QwwTaskPanelSheetExtension::setPropertyChanged(QWidget * task, const QString & propName, const QVariant &value)
{
    QDesignerFormWindowInterface *form = QDesignerFormWindowInterface::findFormWindow(m_panel);
    if (form && task) {
        QDesignerFormEditorInterface *editor = form->core();
        QExtensionManager *manager = editor->extensionManager();
        QDesignerPropertySheetExtension *sheet;
        sheet = qt_extension<QDesignerPropertySheetExtension*>(manager, task);
        int propertyIndex = sheet->indexOf(propName);
        if(value.isValid())
            sheet->setProperty(propertyIndex, value);
        sheet->setChanged(propertyIndex, true);
    }

}

#endif


//
//
// C++ Interface: qwwtaskpaneliface
//
// Description:
//
//
// Author: Witold Wysota <wysota@xxxxxxxxxxxxx>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef QWWTASKPANELIFACE_H
#define QWWTASKPANELIFACE_H
#ifndef WW_NO_TASKPANEL
#include "wwinterfaces.h"
#include <QTimer>
#include <QVariant>

#include "qwwtaskpanel.h"


class QwwTaskPanelIface : public wwWidgetInterface {
    Q_OBJECT
    Q_INTERFACES(QDesignerCustomWidgetInterface);
public:
    QwwTaskPanelIface(QObject *parent = 0);
    ~QwwTaskPanelIface();
    bool isContainer() const {
        return true;
    }
    QIcon icon() const;
    QString domXml() const;
    QWidget *createWidget(QWidget *parent);
    void initialize(QDesignerFormEditorInterface *core);
    QString group() const { return "[ww] Containers"; }
private:
    bool eventFilter(QObject *obj, QEvent *e);
    QDesignerFormEditorInterface *formEditor;
private slots:
    void currentIndexChanged(int);
    void execute();
};


class QwwTaskPanelContainerExtension: public QObject,
            public QDesignerContainerExtension {
    Q_OBJECT
    Q_INTERFACES(QDesignerContainerExtension);

public:
    QwwTaskPanelContainerExtension(QwwTaskPanel *widget, QObject *parent);

    void addWidget(QWidget *widget);
    int count() const;
    int currentIndex() const;
    void insertWidget(int index, QWidget *widget);
    void remove(int index);
    void setCurrentIndex(int index);
    QWidget *widget(int index) const;

private:
    QwwTaskPanel *myWidget;
};

//class PropertyMatcher;

class QwwTaskPanelSheetExtension : public wwSheetExtension
 {
     Q_OBJECT
     Q_INTERFACES(QDesignerPropertySheetExtension);

 public:
    QwwTaskPanelSheetExtension(QWidget *w, QObject *other, QObject *parent=0);
    ~QwwTaskPanelSheetExtension();
    bool hasReset ( int index ) const;
    bool isChanged ( int index ) const;
    QVariant property ( int index ) const;
    bool reset ( int index );
    void setAttribute ( int index, bool attribute );
    void setChanged ( int index, bool changed );
    void setProperty ( int index, const QVariant & value );
    void setPropertyGroup ( int index, const QString & group );
    void setVisible ( int index, bool visible );
private:
  QwwTaskPanel *m_panel;
  void setPropertyChanged(QWidget *task, const QString &propName, const QVariant &value=QVariant());
 };

#endif
#endif

//
//
// C++ Interface: qwwtaskpanel_p
//
// Description:
//
//
// Author: Witold Wysota <wysota@xxxxxxxxxxxxx>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include <QResizeEvent>
#include <QLayout>
#include <QPainter>
#include <QLabel>
#include <QIcon>
#include <QToolButton>
#include <QTimeLine>
#include <wwglobal.h>


class TaskHeader : public QFrame {
    Q_OBJECT
public:
    TaskHeader(QWidget *w, QWidget *parent = 0);
    void setToggleIcon(const QIcon &i);
    void setTaskName(const QString &n);
    void setIcon(const QIcon &i);
    inline QToolButton *toggleButton() const;
protected:
    void paintEvent(QPaintEvent *ev);
    QWidget *m_widget;
    QLabel *m_text;
    QIcon m_icon;
    QSpacerItem *m_spacer;
    QToolButton *m_button;
};

class Q_WW_EXPORT Task : public QWidget {
    Q_OBJECT
public:
    Task(QWidget *body, QWidget *parent = 0);
    void setName(const QString &n) {
        m_header->setTaskName(n);
        m_body->setWindowTitle(n);
    }
    void setIcon(const QIcon &i) {
        m_header->setIcon(i);
        m_body->setWindowIcon(i);
    }
    void setToggleIcon(const QIcon &i) {
        m_header->setToggleIcon(i);
    }
    QWidget *body() const {
        return m_body;
    }
signals:
    void opened();
    void closed();
public slots:
    void setOpen(bool o);

private slots:
    void animate(int);
    void animFinished();
protected:
    bool eventFilter(QObject *o, QEvent *e);
    TaskHeader *m_header;
    QWidget *m_body;
    QWidget *m_animBody;
    QTimeLine m_animator;
    QPixmap m_animpix;
};




//
//
// C++ Implementation: %{MODULE}
//
// Description:
//
//
// Author: Witold Wysota <wysota@xxxxxxxxxxxxx>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "wwinterfaces.h"

wwWidgetInterface::wwWidgetInterface(QObject *parent) : QObject(parent) {
    m_initialized = false;
}

wwWidgetInterface::~ wwWidgetInterface() {}

bool wwWidgetInterface::isContainer() const {
    return false;
}

QString wwWidgetInterface::group() const {
    return "wwWidgets";
}

QIcon wwWidgetInterface::icon() const {
    return QIcon();
}

bool wwWidgetInterface::isInitialized() const {
    return m_initialized;
}

void wwWidgetInterface::initialize(QDesignerFormEditorInterface * core) {
    m_initialized = true;
}

QString wwWidgetInterface::whatsThis() const {
    return QString("%1 - part of wwWidgets widget collection").arg(name());
}

QString wwWidgetInterface::toolTip() const {
    return name();
}

QString wwWidgetInterface::name() const {
    QString cl = metaObject()->className();
    cl.chop(5);
    return cl;
}

QString wwWidgetInterface::includeFile() const {
    return name().toLower()+".h";
}

wwSheetExtension::wwSheetExtension(const QStringList &l, QDesignerPropertySheetExtension *oth, QObject * parent) : QObject(parent), QDesignerPropertySheetExtension() {
    m_other = oth;
    m_names = l;
}

int wwSheetExtension::count() const {
    int c = m_names.count();
    return other() ? other()->count() + c : c;
}

QDesignerPropertySheetExtension * wwSheetExtension::other() const {
    return m_other;
}

const QStringList & wwSheetExtension::names() const {
    return m_names;
}

QString wwSheetExtension::propertyGroup(int index) const {
    int myindex = other() ? other()->count() : 0;
    if (index>=myindex && index<myindex+names().count()) return name();
    return other() ? other()->propertyGroup(index) : "";
}

QString wwSheetExtension::name() const {
    QString n = metaObject()->className();
    n.chop(14);
    return n;
}

QString wwSheetExtension::propertyName(int index) const {
    int myindex = other() ? other()->count() : 0;
    if (index>=myindex) return names().at(index-myindex);
    return other() ? other()->propertyName(index) : QString::null;
}

int wwSheetExtension::indexOf(const QString & name) const {
    int myindex = other() ? other()->count() : 0;
    int i = names().indexOf(name);
    if (i>=0) return myindex+i;
    return other() ? other()->indexOf(name) : -1;
}

bool wwSheetExtension::isVisible(int index) const {
    int myindex = other() ? other()->count() : 0;
    if (index>=myindex) return true;
    return other() ? other()->isVisible(index) : false;
}

bool wwSheetExtension::isAttribute(int index) const {
    int myindex = other() ? other()->count() : 0;
    if (index>=myindex) return true;
    return other() ? other()->isAttribute(index) : false;
}

//
//
// C++ Interface: wwinterfaces
//
// Description:
//
//
// Author: Witold Wysota <wysota@xxxxxxxxxxxxx>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef __WWINTERFACES_H
#define __WWINTERFACES_H

#include <QDesignerCustomWidgetInterface>

#include <QtDesigner/QDesignerPropertySheetExtension>
#include <QtDesigner/QDesignerContainerExtension>

/**
 *  @internal
 */
class wwWidgetInterface : public QObject, public QDesignerCustomWidgetInterface {
public:
    wwWidgetInterface(QObject *parent=0);
    ~wwWidgetInterface();
    bool isContainer() const;
    bool isInitialized() const;
    QIcon icon() const;
    QString name() const;
    QString group() const;
    QString toolTip() const;
    QString whatsThis() const;
    QString includeFile() const;
    void initialize(QDesignerFormEditorInterface *core);
private:
    bool m_initialized;
};

/**
 *  @internal
 */
class wwSheetExtension : public QObject, public QDesignerPropertySheetExtension {
public:
    wwSheetExtension(const QStringList &l, QDesignerPropertySheetExtension *oth, QObject *parent=0);
    int count() const;
    QString propertyGroup(int index) const;
    QString propertyName ( int index ) const;
    int indexOf ( const QString & name ) const;
    bool isVisible ( int index ) const;
    bool isAttribute ( int index ) const;
protected:
    QDesignerPropertySheetExtension * other() const;
    const QStringList &names() const;
    QString name() const;
private:
    QDesignerPropertySheetExtension *m_other;
    QStringList m_names;
};

#endif

//
//
// C++ Implementation: wwWidgets Designer plugin
//
// Description:
//
//
// Author: Witold Wysota <wysota@xxxxxxxxxxxxx>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include <QtDesigner/QtDesigner>
#include <QtCore/qplugin.h>

#include "wwwidgets.h"
#include "qwwlineeditiface.h"
#include "qwwtipwidgetiface.h"
#include "qwwlistwidgetiface.h"
#include "qwwlongspinboxiface.h"
#include "qwwtwocolorindicatoriface.h"
#include "qwwcolorcomboboxiface.h"
#include "qwwnavigationbariface.h"
#include "qwwnumpadiface.h"
#include "qwwcolorbuttoniface.h"
#include "qwwcolorcomboboxiface.h"
#include "qwwtaskpaneliface.h"
#include "qwwhuesatpickeriface.h"
#include "qwwhuesatradialpickeriface.h"
#include "qwwconfigwidgetiface.h"
#include "qwwtextspinboxiface.h"
#include "qwwrichtextbuttoniface.h"
#include "qwwfilechooseriface.h"
#include "qwwloginboxiface.h"
#include "qwwbreadcrumbiface.h"
#include "qwwbuttonlineeditiface.h"
#include "qwwclearlineeditiface.h"
#include "qwwresetlineeditiface.h"
#include "qwwrichtexteditiface.h"

#include <QMutex>
QMutex cheatmutex;

#define WW_EXPOSE(x) widgets << new x(this)

wwWidgets::wwWidgets(QObject *parent)
        : QObject(parent) {
    WW_EXPOSE(QwwLineEditIface);
    WW_EXPOSE(QwwTipWidgetIface);
    WW_EXPOSE(QwwListWidgetIface);
#ifndef WW_NO_SPINBOX
    WW_EXPOSE(QwwLongSpinBoxIface);
#endif
    WW_EXPOSE(QwwTextSpinBoxIface);
#ifndef WW_NO_TWOCOLORINDICATOR
    WW_EXPOSE(QwwTwoColorIndicatorIface);
#endif
#ifndef WW_NO_COLORCOMBOBOX
    WW_EXPOSE(QwwColorComboBoxIface);
#endif
#ifndef WW_NO_NAVIGATIONBAR
    WW_EXPOSE(QwwNavigationBarIface);
#endif
    WW_EXPOSE(QwwNumPadIface);
    WW_EXPOSE(QwwColorButtonIface);
#ifndef WW_NO_TASKPANEL
    WW_EXPOSE(QwwTaskPanelIface);
#endif
    WW_EXPOSE(QwwHueSatPickerIface);
    WW_EXPOSE(QwwHueSatRadialPickerIface);
    WW_EXPOSE(QwwConfigWidgetIface);
    WW_EXPOSE(QwwRichTextButtonIface);
#ifndef WW_NO_FILECHOOSER
    WW_EXPOSE(QwwFileChooserIface);
#endif
#ifndef WW_NO_LOGINBOX
    WW_EXPOSE(QwwLoginBoxIface);
#endif
#ifndef WW_NO_BREADCRUMB
    WW_EXPOSE(QwwBreadCrumbIface);
#endif
#ifndef WW_NO_BUTTONLINEEDIT
    WW_EXPOSE(QwwButtonLineEditIface);
    WW_EXPOSE(QwwClearLineEditIface);
    WW_EXPOSE(QwwResetLineEditIface);
#endif
    WW_EXPOSE(QwwRichTextEditIface);
}

QList< QDesignerCustomWidgetInterface * > wwWidgets::customWidgets( ) const {
    return widgets;
}

Q_EXPORT_PLUGIN2(wwwidgets, wwWidgets)


void wwWidgetsMenu::about() {
    QDialog dlg;
    Ui::About ui;
    ui.setupUi(&dlg);
    dlg.setFixedSize(dlg.sizeHint());
    dlg.exec();
}

wwWidgetsMenu::wwWidgetsMenu(const QString & title, QWidget * parent) : QMenu(title, parent) {
    addAction("About", this, SLOT(about()));
}


QwwWidgetsExtensionFactory::QwwWidgetsExtensionFactory(QExtensionManager *manager ) {
    m_manager = manager;
}

#define WW_REGISTER_CONTAINEREXTENSION(cl, object, parent) \
    if(cl *widget = qobject_cast<cl*>(object)) \
        return new cl ## ContainerExtension(widget, parent);

QObject * QwwWidgetsExtensionFactory::createExtension(QObject * object, const QString & iid, QObject * parent) const {

    if (iid == Q_TYPEID(QDesignerContainerExtension)) {
#ifndef WW_NO_TASKPANEL
        WW_REGISTER_CONTAINEREXTENSION(QwwTaskPanel, object, parent);
#endif
#ifndef WW_NO_NAVIGATIONBAR
        WW_REGISTER_CONTAINEREXTENSION(QwwNavigationBar, object, parent);
#endif
#ifndef WW_NO_CONFIGWIDGET
        WW_REGISTER_CONTAINEREXTENSION(QwwConfigWidget, object, parent);
#endif
        return 0;
    } else if (iid == Q_TYPEID(QDesignerPropertySheetExtension)) {
        if (cheatmutex.tryLock()) {
#ifndef WW_NO_TASKPANEL
            if (QwwTaskPanel *widget = qobject_cast<QwwTaskPanel*>(object) ) {
                QObject *otherExtension = m_manager->extension(object, iid);
                cheatmutex.unlock();
                return new QwwTaskPanelSheetExtension(widget, otherExtension, parent);
            }
#endif
#ifndef WW_NO_NAVIGATIONBAR
            if (QwwNavigationBar *widget = qobject_cast<QwwNavigationBar*>(object) ){
                QObject *otherExtension = m_manager->extension(object, iid);
                cheatmutex.unlock();
                return new QwwNavigationBarSheetExtension(widget, otherExtension, parent);
            }
#endif
#ifndef WW_NO_CONFIGWIDGET
            if (QwwConfigWidget *widget = qobject_cast<QwwConfigWidget*>(object) ){
                QObject *otherExtension = m_manager->extension(object, iid);
                cheatmutex.unlock();
                return new QwwConfigWidgetSheetExtension(widget, otherExtension, parent);
            }
#endif
            cheatmutex.unlock();
        }
    } else if(iid == Q_TYPEID(QDesignerTaskMenuExtension)){
            if(QwwColorButton *widget = qobject_cast<QwwColorButton*>(object)){
                return new QwwColorButtonTaskMenuExtension(widget, parent);
            }
#ifndef WW_NO_COLORCOMBOBOX
            if(QwwColorComboBox *widget = qobject_cast<QwwColorComboBox*>(object)){
                return new QwwColorComboBoxTaskMenuExtension(widget, parent);
            }
#endif
#ifndef WW_NO_TWOCOLORINDICATOR
            if(QwwTwoColorIndicator *widget = qobject_cast<QwwTwoColorIndicator*>(object)){
                return new QwwTwoColorIndicatorTaskMenuExtension(widget, parent);
            }
#endif
    }
    return 0;
}


//
//
// C++ Interface: wwwidgets
//
// Description:
//
//
// Author: Witold Wysota <wysota@xxxxxxxxxxxxx>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef WWWIDGETS_H
#define WWWIDGETS_H

#include <QObject>
#include <QDesignerCustomWidgetCollectionInterface>
#include <QtDesigner/QExtensionFactory>
#include "ui_about.h"
#include <QDialog>
#include <QMenu>


/**
    @author
*/
class wwWidgets : public QObject, public QDesignerCustomWidgetCollectionInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetCollectionInterface);

public:
    wwWidgets(QObject *parent = 0);
    virtual QList<QDesignerCustomWidgetInterface*> customWidgets() const;
private:
    QList<QDesignerCustomWidgetInterface*> widgets;
};

class wwWidgetsMenu : public QMenu {
    Q_OBJECT
public:
    wwWidgetsMenu(const QString &title, QWidget *parent=0);
public slots:
    void about();
};

class QwwWidgetsExtensionFactory: public QExtensionFactory {
    Q_OBJECT
public:
    QwwWidgetsExtensionFactory(QExtensionManager *parent = 0);
protected:
    QObject *createExtension(QObject *object, const QString &iid, QObject *parent) const;

    QExtensionManager *m_manager;
};


#endif


Message 14 in thread

On Friday 18 January 2008 15:31, Witold Wysota wrote:
> Jarek Kobus wrote:
> >> I have a custom multipage container widget plugin for a widget that is
> >> derived from QScrollArea. Because of the base class it is handled
> >> incorrectly by Designer - the container infrastructure is not present
> >> (despite the fact that the container extension and a property sheet
> >> extension are present) - it behaves like a plain QScrollArea. If I wrap
> >> the widget (in code, not in Designer) into a QWidget (so that it is
> >> derived from QWidget and not QScrollArea) Designer handles it correctly.
> >
> > Could you post an example? It would help us understand your issue.
>
> Full version (although not compilable unless you remove code relevant to
> other classes) attached. Short version:
>

Hi Witold,

We have fixed designer (the current snapshot should contain relevant changes) 
and your plugin should now work. We have modified a bit your plugin code (see 
attachement), e.g. wwSheetExtension is derived from 
QDesignerDynamicPropertySheetExtension as well (this removes warnings about 
wrong index) and changed property()/setProperty() methods' implementations 
(now the call is delegated to task's property sheet, so the "currentTaskIcon" 
property should work).

Regarding "addpagemethod" it shouldn't be necessary anymore to make it a 
public slot.

Thanks for letting us know about your issues. Hope this works now 4U2.

Regards

Jarek

Attachment:

Attachment: wysota_plugin.tar.gz
Description: application/tgz


Message 15 in thread

Jarek Kobus wrote:
 
> Hi Witold,

Hi Jarek,
 
> We have fixed designer (the current snapshot should contain relevant
> changes) and your plugin should now work. We have modified a bit your
> plugin code (see attachement), e.g. wwSheetExtension is derived from
> QDesignerDynamicPropertySheetExtension as well (this removes warnings
> about wrong index) and changed property()/setProperty() methods'
> implementations (now the call is delegated to task's property sheet, so
> the "currentTaskIcon" property should work).

Great, thanks for the modifications. This is funny... I just wanted to
report my "currentTaskIcon" property doesn't work because it's a QIcon and
I come here and see you said you fixed it :)

> Regarding "addpagemethod" it shouldn't be necessary anymore to make it a
> public slot.

Thx, I'll compile the snapshot and check it all out tomorrow.


> Thanks for letting us know about your issues. Hope this works now 4U2.
> 

I hope so as well. So when that's done, here are two more things regarding
Designer...

1. A suggestion to make it possible to reload plugins on the fly (without
having to restart Designer). I know it's tricky, but it would be really
helpful when designing custom widgets. Right now there are two choices -
restart Designer after every change or don't use Designer at all.

2. A suggestion or maybe a question... Assuming I have a custom widget that
contains items (doesn't matter if they are related to item views). I know
the UI format allows storing such items in the file, but (correct me if I'm
wrong) this is not possible for custom widgets. Let's take a use case
again... I have a button that displays a definable list of colours.
Currently I have to keep them in a property of type QStringList where each
colour is represented by a string. Now... this is silly, my opinion is that
everything that can be done in Designer for standard widgets, should be
achievable for custom ones as well.

Ok, let's have one more... number 3, I guess - a bug/suggestion this time
(still against 4.4-TP1). Assuming I have a custom multipage container
widget and I store the current index of the widget as a property there is a
problem with inserting pages. The way it works now is that UIC generates
code that first sets all properties and then adds pages. This makes
my "currentIndex" property useless, because at the time when it's set,
pages are not added yet, thus the value of the property that is being set
is invalid (and ignored) and I always end up with currentIndex being set to
the default value. Here is the relevant code generated by UIC with my
comments:

void setupUi(QWidget *Form)
    {
// cut
    navigationBar = new QwwNavigationBar(Form); // my container
    navigationBar->setObjectName(QString::fromUtf8("navigationBar"));
    navigationBar->setCurrentIndex(1); // <=== here the index is set
    page1 = new QWidget(navigationBar); // <== and here pages are being
created
    page1->setObjectName(QString::fromUtf8("page1"));
// cut
    navigationBar->addWidget(page1); // <== first page added
    page2 = new QWidget(navigationBar); // <== second created
// cut
    navigationBar->addWidget(page2); // <== second added
// cut
    retranslateUi(Form);
    QMetaObject::connectSlotsByName(Form);
    } // setupUi

See what I mean?

Either the order should be changed or settable or there should be a way to
mark a "current page index" property (maybe the same way
the "addPageMethod" tag is used) so that it's set only after the pages are
added.

Just for completeness - as always, UIC cheats for standard widgets, here is
a snippet of a similar code for a tab widget:


// ...
    tabWidget->addTab(tab_2, QString()); // <== page added here
    gridLayout->addWidget(tabWidget, 0, 0, 1, 1);


    retranslateUi(Form);

    tabWidget->setCurrentIndex(1);   // <<<<<<< CHEATING HERE!!!


    QMetaObject::connectSlotsByName(Form);
    } // setupUi


Am I the only one creating custom pluggable container widgets? Phew....

Cheers :)
  W.

-- 
 [ signature omitted ] 

Pages: Prev | 1 | 2 | Next