kexi

kexidbcombobox.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "kexidbcombobox.h"
21#include "kexidblineedit.h"
22#include "../kexiformscrollview.h"
23
24#include <kcombobox.h>
25#include <kdebug.h>
26#include <tdeapplication.h>
27
28#include <tqmetaobject.h>
29#include <tqpainter.h>
30#include <tqstyle.h>
31#include <tqdrawutil.h>
32#include <tqptrdict.h>
33#include <tqcursor.h>
34
35#include <kexidb/queryschema.h>
36#include <widget/tableview/kexicomboboxpopup.h>
37#include <widget/tableview/kexicelleditorfactory.h>
38#include <kexiutils/utils.h>
39
41class KexiDBComboBox::Private
42{
43 public:
44 Private()
45 : popup(0)
46 , visibleColumnInfo(0)
47 , subWidgetsWithDisabledEvents(0)
48 , isEditable(false)
49 , buttonPressed(false)
50 , mouseOver(false)
51 , dataEnteredByHand(true)
52 {
53 }
54 ~Private()
55 {
56 delete subWidgetsWithDisabledEvents;
57 subWidgetsWithDisabledEvents = 0;
58 }
59
60 KexiComboBoxPopup *popup;
61 KComboBox *paintedCombo;
62 TQSize sizeHint;
64 KexiDB::QueryColumnInfo* visibleColumnInfo;
65 TQPtrDict<char> *subWidgetsWithDisabledEvents;
66 bool isEditable : 1;
67 bool buttonPressed : 1;
68 bool mouseOver : 1;
69 bool dataEnteredByHand : 1;
70 bool designMode : 1;
71};
72
73//-------------------------------------
74
75KexiDBComboBox::KexiDBComboBox(TQWidget *parent, const char *name, bool designMode)
76 : KexiDBAutoField(parent, name, designMode, NoLabel)
78 , d(new Private())
79{
80 setMouseTracking(true);
81 setFocusPolicy(TQWidget::WheelFocus);
82 installEventFilter(this);
83 d->designMode = designMode;
84 d->paintedCombo = new KComboBox(this);
85 d->paintedCombo->hide();
86 d->paintedCombo->move(0,0);
87}
88
89KexiDBComboBox::~KexiDBComboBox()
90{
91 delete d;
92}
93
94KexiComboBoxPopup *KexiDBComboBox::popup() const
95{
96 return d->popup;
97}
98
99void KexiDBComboBox::setPopup(KexiComboBoxPopup *popup)
100{
101 d->popup = popup;
102}
103
104void KexiDBComboBox::setEditable(bool set)
105{
106 if (d->isEditable == set)
107 return;
108 d->isEditable = set;
109 d->paintedCombo->setEditable(set);
110 if (set)
111 createEditor();
112 else {
113 delete m_subwidget;
114 m_subwidget = 0;
115 }
116 update();
117}
118
119bool KexiDBComboBox::isEditable() const
120{
121 return d->isEditable;
122}
123
124void KexiDBComboBox::paintEvent( TQPaintEvent * )
125{
126 TQPainter p( this );
127 TQColorGroup cg( palette().active() );
128// if ( hasFocus() )
129// cg.setColor(TQColorGroup::Base, cg.highlight());
130// else
131 cg.setColor(TQColorGroup::Base, paletteBackgroundColor()); //update base color using (reimplemented) bg color
132 p.setPen(cg.text());
133
134 TQStyle::SFlags flags = TQStyle::Style_Default;
135 if (isEnabled())
136 flags |= TQStyle::Style_Enabled;
137 if (hasFocus())
138 flags |= TQStyle::Style_HasFocus;
139 if (d->mouseOver)
140 flags |= TQStyle::Style_MouseOver;
141
142 if ( width() < 5 || height() < 5 ) {
143 qDrawShadePanel( &p, rect(), cg, false, 2, &cg.brush( TQColorGroup::Button ) );
144 return;
145 }
146
148//bool reverse = TQApplication::reverseLayout();
149 style().drawComplexControl( TQStyle::CC_ComboBox, &p, d->paintedCombo /*this*/, rect(), cg,
150 flags, (uint)TQStyle::SC_All,
151 (d->buttonPressed ? TQStyle::SC_ComboBoxArrow : TQStyle::SC_None )
152 );
153
154 if (d->isEditable) {
155 //if editable, editor paints itself, nothing to do
156 }
157 else { //not editable: we need to paint the current item
158 TQRect editorGeometry( this->editorGeometry() );
159 if ( hasFocus() ) {
160 if (0==qstrcmp(style().name(), "windows")) //a hack
161 p.fillRect( editorGeometry, cg.brush( TQColorGroup::Highlight ) );
162 TQRect r( TQStyle::visualRect( style().subRect( TQStyle::SR_ComboBoxFocusRect, d->paintedCombo ), this ) );
163 r = TQRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2); //enlare by 1 pixel each side to avoid covering by the subwidget
164 style().drawPrimitive( TQStyle::PE_FocusRect, &p,
165 r, cg, flags | TQStyle::Style_FocusAtBorder, TQStyleOption(cg.highlight()));
166 }
167 //todo
168 }
169}
170
172{
173 TQRect r( TQStyle::visualRect(
174 style().querySubControlMetrics(TQStyle::CC_ComboBox, d->paintedCombo,
175 TQStyle::SC_ComboBoxEditField), d->paintedCombo ) );
176
177 //if ((height()-r.bottom())<6)
178 // r.setBottom(height()-6);
179 return r;
180}
181
183{
185 if (m_subwidget) {
186 m_subwidget->setGeometry( editorGeometry() );
187 if (!d->isEditable) {
188 m_subwidget->setCursor(TQCursor(TQt::ArrowCursor)); // widgets like listedit have IbeamCursor, we don't want that
189 TQPalette subwidgetPalette( m_subwidget->palette() );
190 subwidgetPalette.setColor(TQPalette::Active, TQColorGroup::Base,
191 subwidgetPalette.color(TQPalette::Active, TQColorGroup::Button));
192 m_subwidget->setPalette( subwidgetPalette );
193 if (d->subWidgetsWithDisabledEvents)
194 d->subWidgetsWithDisabledEvents->clear();
195 else
196 d->subWidgetsWithDisabledEvents = new TQPtrDict<char>();
197 d->subWidgetsWithDisabledEvents->insert(m_subwidget, (char*)1);
198 m_subwidget->installEventFilter(this);
199 TQObjectList *l = m_subwidget->queryList( "TQWidget" );
200 for ( TQObjectListIt it( *l ); it.current(); ++it ) {
201 d->subWidgetsWithDisabledEvents->insert(it.current(), (char*)1);
202 it.current()->installEventFilter(this);
203 }
204 delete l;
205 }
206 }
207 updateGeometry();
208}
209
210void KexiDBComboBox::setLabelPosition(LabelPosition position)
211{
212 if(m_subwidget) {
213 if (-1 != m_subwidget->metaObject()->findProperty("frameShape", true))
214 m_subwidget->setProperty("frameShape", TQVariant((int)TQFrame::NoFrame));
215 m_subwidget->setGeometry( editorGeometry() );
216 }
217// KexiSubwidgetInterface *subwidgetInterface = dynamic_cast<KexiSubwidgetInterface*>((TQWidget*)m_subwidget);
218 // update size policy
219// if (subwidgetInterface && subwidgetInterface->subwidgetStretchRequired(this)) {
220 TQSizePolicy sizePolicy( this->sizePolicy() );
221 if(position == Left)
222 sizePolicy.setHorData( TQSizePolicy::Minimum );
223 else
224 sizePolicy.setVerData( TQSizePolicy::Minimum );
225 //m_subwidget->setSizePolicy(sizePolicy);
226 setSizePolicy(sizePolicy);
227 //}
228// }
229}
230
231TQRect KexiDBComboBox::buttonGeometry() const
232{
233 TQRect arrowRect(
234 style().querySubControlMetrics( TQStyle::CC_ComboBox, d->paintedCombo, TQStyle::SC_ComboBoxArrow) );
235 arrowRect = TQStyle::visualRect(arrowRect, d->paintedCombo);
236 arrowRect.setHeight( TQMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); // a fix for Motif style
237 return arrowRect;
238}
239
240bool KexiDBComboBox::handleMousePressEvent(TQMouseEvent *e)
241{
242 if ( e->button() != TQt::LeftButton || d->designMode )
243 return true;
244/*todo if ( m_discardNextMousePress ) {
245 d->discardNextMousePress = false;
246 return;
247 }*/
248
249 if ( /*count() &&*/ ( !isEditable() || buttonGeometry().contains( e->pos() ) ) ) {
250 d->buttonPressed = false;
251
252/* if ( d->usingListBox() ) {
253 listBox()->blockSignals( true );
254 tqApp->sendEvent( listBox(), e ); // trigger the listbox's autoscroll
255 listBox()->setCurrentItem(d->current);
256 listBox()->blockSignals( false );
257 popup();
258 if ( arrowRect.contains( e->pos() ) ) {
259 d->arrowPressed = true;
260 d->arrowDown = true;
261 repaint( false );
262 }
263 } else {*/
264 showPopup();
265 return true;
266 }
267 return false;
268}
269
270bool KexiDBComboBox::handleKeyPressEvent(TQKeyEvent *ke)
271{
272 const int k = ke->key();
273 const bool dropDown = (ke->state() == TQt::NoButton && ((k==TQt::Key_F2 && !d->isEditable) || k==TQt::Key_F4))
274 || (ke->state() == TQt::AltButton && k==TQt::Key_Down);
275 const bool escPressed = ke->state() == TQt::NoButton && k==TQt::Key_Escape;
276 const bool popupVisible = popup() && popup()->isVisible();
277 if ((dropDown || escPressed) && popupVisible) {
278 popup()->hide();
279 return true;
280 }
281 else if (dropDown && !popupVisible) {
282 d->buttonPressed = false;
283 showPopup();
284 return true;
285 }
286 else if (popupVisible) {
287 const bool enterPressed = k==TQt::Key_Enter || k==TQt::Key_Return;
288 if (enterPressed/* && m_internalEditorValueChanged*/) {
289 acceptPopupSelection();
290 return true;
291 }
292 return handleKeyPressForPopup( ke );
293 }
294
295 return false;
296}
297
298bool KexiDBComboBox::keyPressed(TQKeyEvent *ke)
299{
301 return true;
302
303 const int k = ke->key();
304 const bool popupVisible = popup() && popup()->isVisible();
305 const bool escPressed = ke->state() == TQt::NoButton && k==TQt::Key_Escape;
306 if (escPressed && popupVisible) {
307 popup()->hide();
308 return true;
309 }
310 if (ke->state() == TQt::NoButton && (k==TQt::Key_PageDown || k==TQt::Key_PageUp) && popupVisible)
311 return true;
312 return false;
313}
314
315void KexiDBComboBox::mousePressEvent( TQMouseEvent *e )
316{
317 if (handleMousePressEvent(e))
318 return;
319
320// TQTimer::singleShot( 200, this, TQ_SLOT(internalClickTimeout()));
321// d->shortClick = true;
322// }
323 KexiDBAutoField::mousePressEvent( e );
324}
325
326void KexiDBComboBox::mouseDoubleClickEvent( TQMouseEvent *e )
327{
328 mousePressEvent( e );
329}
330
331bool KexiDBComboBox::eventFilter( TQObject *o, TQEvent *e )
332{
333 if (o==this) {
334 if (e->type()==TQEvent::Resize) {
335 d->paintedCombo->resize(size());
336 if (m_subwidget)
337 m_subwidget->setGeometry( editorGeometry() );
338 }
339 else if (e->type()==TQEvent::Enter) {
340 if (!d->isEditable
341 || /*over button if editable combo*/buttonGeometry().contains( static_cast<TQMouseEvent*>(e)->pos() ))
342 {
343 d->mouseOver = true;
344 update();
345 }
346 }
347 else if (e->type()==TQEvent::MouseMove) {
348 if (d->isEditable) {
349 const bool overButton = buttonGeometry().contains( static_cast<TQMouseEvent*>(e)->pos() );
350 if (overButton != d->mouseOver) {
351 d->mouseOver = overButton;
352 update();
353 }
354 }
355 }
356 else if (e->type()==TQEvent::Leave) {
357 d->mouseOver = false;
358 update();
359 }
360 else if (e->type()==TQEvent::KeyPress) {
361 // handle F2/F4
362 if (handleKeyPressEvent(static_cast<TQKeyEvent*>(e)))
363 return true;
364 }
365 else if (e->type()==TQEvent::FocusOut) {
366 if (popup() && popup()->isVisible()) {
367 popup()->hide();
368 undoChanges();
369 }
370 }
371 }
372 else if (!d->isEditable && d->subWidgetsWithDisabledEvents && d->subWidgetsWithDisabledEvents->find(o)) {
373 if (e->type()==TQEvent::MouseButtonPress) {
374 // clicking the subwidget should mean the same as clicking the combo box (i.e. show the popup)
375 if (handleMousePressEvent(static_cast<TQMouseEvent*>(e)))
376 return true;
377 }
378 else if (e->type()==TQEvent::KeyPress) {
379 if (handleKeyPressEvent(static_cast<TQKeyEvent*>(e)))
380 return true;
381 }
382 return e->type()!=TQEvent::Paint;
383 }
384 return KexiDBAutoField::eventFilter( o, e );
385}
386
387bool KexiDBComboBox::subwidgetStretchRequired(KexiDBAutoField* autoField) const
388{
389 Q_UNUSED(autoField);
390 return true;
391}
392
393void KexiDBComboBox::setPaletteBackgroundColor( const TQColor & color )
394{
396 TQPalette pal(palette());
397 TQColorGroup cg(pal.active());
398 pal.setColor(TQColorGroup::Base, red);
399 pal.setColor(TQColorGroup::Background, red);
400 pal.setActive(cg);
401 TQWidget::setPalette(pal);
402 update();
403}
404
406{
407 kdDebug() << "KexiDataItemInterface::valueChanged(): " << m_origValue.toString() << " ? " << value().toString() << endl;
408 return m_origValue != value();
409}
410
411void
416
418{
419 d->visibleColumnInfo = cinfo;
420 // we're assuming we already have columnInfo()
421 setColumnInfoInternal(columnInfo(), d->visibleColumnInfo);
422}
423
425{
426 return d->visibleColumnInfo;
427}
428
430{
431 if (d->isEditable && m_moveCursorToEndInInternalEditor_enabled)
433}
434
436{
437 if (d->isEditable && m_selectAllInInternalEditor_enabled)
438 selectAll();
439}
440
441void KexiDBComboBox::setValueInternal(const TQVariant& add, bool removeOld)
442{
445 if (popup())
446 popup()->hide();
448}
449
451{
452 KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((TQWidget*)m_subwidget);
453 if(iface)
454 iface->setValue(value, TQVariant(), false );
455}
456
458{
459 return KexiComboBoxBase::visibleValue();
460}
461
463{
464 if (!m_setValueInInternalEditor_enabled)
465 return;
466 KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((TQWidget*)m_subwidget);
467 if(iface)
468 iface->setValue(value, TQVariant(), false);
469}
470
475
476TQPoint KexiDBComboBox::mapFromParentToGlobal(const TQPoint& pos) const
477{
478// const KexiFormScrollView* view = KexiUtils::findParentConst<const KexiFormScrollView>(this, "KexiFormScrollView");
479 if (!parentWidget())
480 return TQPoint(-1,-1);
481 return parentWidget()->mapToGlobal(pos);
482// return view->viewport()->mapToGlobal(pos);
483}
484
486{
487 return width(); //popup() ? popup()->width() : 0;
488}
489
490void KexiDBComboBox::fontChange( const TQFont & oldFont )
491{
492 d->sizeHint = TQSize(); //force rebuild the cache
493 KexiDBAutoField::fontChange(oldFont);
494}
495
496void KexiDBComboBox::styleChange( TQStyle& oldStyle )
497{
498 KexiDBAutoField::styleChange( oldStyle );
499 d->sizeHint = TQSize(); //force rebuild the cache
500 if (m_subwidget)
501 m_subwidget->setGeometry( editorGeometry() );
502}
503
504TQSize KexiDBComboBox::sizeHint() const
505{
506 if ( isVisible() && d->sizeHint.isValid() )
507 return d->sizeHint;
508
509 const int maxWidth = 7 * fontMetrics().width(TQChar('x')) + 18;
510 const int maxHeight = TQMAX( fontMetrics().lineSpacing(), 14 ) + 2;
511 d->sizeHint = (style().sizeFromContents(TQStyle::CT_ComboBox, d->paintedCombo,
512 TQSize(maxWidth, maxHeight)).expandedTo(TQApplication::globalStrut()));
513
514 return d->sizeHint;
515}
516
520
525
526void KexiDBComboBox::slotRowAccepted(KexiTableItem *item, int row)
527{
528 d->dataEnteredByHand = false;
530 d->dataEnteredByHand = true;
531}
532
534{
535 if (d->dataEnteredByHand) {
536 KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((TQWidget*)m_subwidget);
537 if (iface) {
538 slotInternalEditorValueChanged( iface->value() );
539 }
540 }
541}
542
548
549#include "kexidbcombobox.moc"
A base class for handling data-aware combo boxes. This class is used by KexiComboBoxTableEdit and Kex...
virtual void slotRowAccepted(KexiTableItem *item, int row)
Call this from slot.
virtual void setValueInternal(const TQVariant &add, bool removeOld)
void undoChanges()
Used by KexiDBComboBox.
Internal class for displaying popup table view.
Universal "Auto Field" widget for Kexi forms.
virtual void moveCursorToEnd()
Implemented for KexiDataItemInterface.
virtual TQVariant value()
const TQColor & paletteBackgroundColor() const
Reimplemented to return internal editor's color.
virtual bool keyPressed(TQKeyEvent *ke)
virtual void setPaletteBackgroundColor(const TQColor &color)
Reimplemented to set internal editor's color.
virtual void createEditor()
virtual void selectAll()
Implemented for KexiDataItemInterface.
void setColumnInfoInternal(KexiDB::QueryColumnInfo *cinfo, KexiDB::QueryColumnInfo *visibleColumnInfo)
Used by KexiDBAutoField::setColumnInfo() and KexiDBComboBox::setColumnInfo().
virtual TQVariant value()
virtual bool keyPressed(TQKeyEvent *ke)
virtual void setValueInternal(const TQVariant &add, bool removeOld)
virtual void setVisibleColumnInfo(KexiDB::QueryColumnInfo *cinfo)
virtual void setVisibleValueInternal(const TQVariant &value)
Implemented to handle visible value instead of index.
virtual void setColumnInfo(KexiDB::QueryColumnInfo *cinfo)
virtual void beforeSignalValueChanged()
Implemented for KexiDataItemInterface.
virtual void fontChange(const TQFont &oldFont)
virtual void setValueInInternalEditor(const TQVariant &value)
Implemented for KexiComboBoxBase.
virtual TQPoint mapFromParentToGlobal(const TQPoint &pos) const
virtual void editRequested()
Implemented for KexiComboBoxBase.
virtual void createEditor()
virtual void moveCursorToEndInInternalEditor()
Implemented for KexiComboBoxBase. Does nothing if the widget is not editable.
virtual bool valueChanged()
Reimpemented because to avoid taking value from the internal editor (index is taken from the popup in...
virtual TQVariant visibleValue()
virtual void undoChanges()
virtual KexiDB::QueryColumnInfo * visibleColumnInfo() const
virtual int popupWidthHint() const
Implement this to return a hint for popup width.
virtual void setPaletteBackgroundColor(const TQColor &color)
Reimplemented to also set 'this' widget's background color, not only subwidget's.
virtual void styleChange(TQStyle &oldStyle)
virtual TQVariant valueFromInternalEditor()
Implemented for KexiComboBoxBase.
virtual void selectAllInInternalEditor()
Implemented for KexiComboBoxBase. Does nothing if the widget is not editable.
virtual void acceptRequested()
Implemented for KexiComboBoxBase.
virtual void paintEvent(TQPaintEvent *)
TQRect editorGeometry() const
Helper class that assigns additional information for the column in a query.
Definition queryschema.h:49
virtual TQVariant value()=0
void setValue(const TQVariant &value, const TQVariant &add=TQVariant(), bool removeOld=false, const TQVariant *visibleValue=0)
virtual KexiDB::QueryColumnInfo * columnInfo() const
virtual void setColumnInfo(KexiDB::QueryColumnInfo *cinfo)