summaryrefslogtreecommitdiffstats
path: root/kexi/widget/utils
diff options
context:
space:
mode:
Diffstat (limited to 'kexi/widget/utils')
-rw-r--r--kexi/widget/utils/Makefile.am19
-rw-r--r--kexi/widget/utils/kexiarrowtip.cpp164
-rw-r--r--kexi/widget/utils/kexiarrowtip.h56
-rw-r--r--kexi/widget/utils/kexicomboboxdropdownbutton.cpp87
-rw-r--r--kexi/widget/utils/kexicomboboxdropdownbutton.h49
-rw-r--r--kexi/widget/utils/kexicontextmenuutils.cpp283
-rw-r--r--kexi/widget/utils/kexicontextmenuutils.h112
-rw-r--r--kexi/widget/utils/kexidatetimeformatter.cpp367
-rw-r--r--kexi/widget/utils/kexidatetimeformatter.h165
-rw-r--r--kexi/widget/utils/kexidisplayutils.cpp172
-rw-r--r--kexi/widget/utils/kexidisplayutils.h57
-rw-r--r--kexi/widget/utils/kexidropdownbutton.cpp82
-rw-r--r--kexi/widget/utils/kexidropdownbutton.h45
-rw-r--r--kexi/widget/utils/kexiflowlayout.cpp452
-rw-r--r--kexi/widget/utils/kexiflowlayout.h79
-rw-r--r--kexi/widget/utils/kexigradientwidget.cpp358
-rw-r--r--kexi/widget/utils/kexigradientwidget.h247
-rw-r--r--kexi/widget/utils/kexirecordmarker.cpp307
-rw-r--r--kexi/widget/utils/kexirecordmarker.h72
-rw-r--r--kexi/widget/utils/kexirecordnavigator.cpp511
-rw-r--r--kexi/widget/utils/kexirecordnavigator.h190
-rw-r--r--kexi/widget/utils/kexisharedactionclient.cpp39
-rw-r--r--kexi/widget/utils/kexisharedactionclient.h49
-rw-r--r--kexi/widget/utils/kexitooltip.cpp76
-rw-r--r--kexi/widget/utils/kexitooltip.h47
-rw-r--r--kexi/widget/utils/klistviewitemtemplate.h50
26 files changed, 4135 insertions, 0 deletions
diff --git a/kexi/widget/utils/Makefile.am b/kexi/widget/utils/Makefile.am
new file mode 100644
index 000000000..5d210f1aa
--- /dev/null
+++ b/kexi/widget/utils/Makefile.am
@@ -0,0 +1,19 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+lib_LTLIBRARIES = libkexiguiutils.la
+libkexiguiutils_la_SOURCES = kexisharedactionclient.cpp kexirecordnavigator.cpp \
+ kexigradientwidget.cpp kexirecordmarker.cpp kexidisplayutils.cpp \
+ kexiflowlayout.cpp kexidatetimeformatter.cpp kexitooltip.cpp kexiarrowtip.cpp \
+ kexidropdownbutton.cpp kexicomboboxdropdownbutton.cpp kexicontextmenuutils.cpp
+
+libkexiguiutils_la_LDFLAGS = $(all_libraries) $(VER_INFO) -Wnounresolved
+libkexiguiutils_la_LIBADD = $(LIB_KDEUI)
+
+SUBDIRS = .
+
+# set the include path for X, qt and KDE - all_includes must remain last!
+INCLUDES = -I$(top_srcdir)/kexi -I$(top_srcdir)/kexi/widget/utils $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_HEADERS = kexigradientwidget.h
diff --git a/kexi/widget/utils/kexiarrowtip.cpp b/kexi/widget/utils/kexiarrowtip.cpp
new file mode 100644
index 000000000..cdffcb023
--- /dev/null
+++ b/kexi/widget/utils/kexiarrowtip.cpp
@@ -0,0 +1,164 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiarrowtip.h"
+
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qimage.h>
+#include <qtooltip.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qtimer.h>
+
+#include <kexiutils/utils.h>
+
+KexiArrowTip::KexiArrowTip(const QString& text, QWidget* parent)
+ : KexiToolTip(text, parent)
+ , m_opacity(0.0)
+{
+ QPalette pal( palette() );
+ QColorGroup cg(pal.active());
+ cg.setColor(QColorGroup::Foreground, Qt::red);
+ pal.setActive(cg);
+ setPalette(pal);
+
+ QFontMetrics fm(font());
+ QSize sz(fm.boundingRect(m_value.toString()).size());
+ sz += QSize(14, 10); //+margins
+ m_arrowHeight = sz.height()/2;
+ sz += QSize(0, m_arrowHeight); //+arrow height
+ resize(sz);
+
+ setAutoMask( false );
+
+ //generate mask
+ QPixmap maskPm(size());
+ maskPm.fill( black );
+ QPainter maskPainter(&maskPm);
+ drawFrame(maskPainter);
+ QImage maskImg( maskPm.convertToImage() );
+ QBitmap bm;
+ bm = maskImg.createHeuristicMask();
+ setMask( bm );
+}
+
+KexiArrowTip::~KexiArrowTip()
+{
+}
+
+void KexiArrowTip::show()
+{
+ if (isVisible())
+ return;
+
+ m_opacity = 0.0;
+ setWindowOpacity(0.0);
+ KexiToolTip::show();
+ increaseOpacity();
+}
+
+void KexiArrowTip::hide()
+{
+ if (!isVisible())
+ return;
+
+ decreaseOpacity();
+}
+
+void KexiArrowTip::increaseOpacity()
+{
+ m_opacity += 0.10;
+ setWindowOpacity(m_opacity);
+ if (m_opacity < 1.0)
+ QTimer::singleShot(25, this, SLOT(increaseOpacity()));
+}
+
+void KexiArrowTip::decreaseOpacity()
+{
+ if (m_opacity<=0.0) {
+ KexiToolTip::close();
+ m_opacity = 0.0;
+ return;
+ }
+ m_opacity -= 0.10;
+ setWindowOpacity(m_opacity);
+ QTimer::singleShot(25, this, SLOT(decreaseOpacity()));
+}
+
+bool KexiArrowTip::close ( bool alsoDelete )
+{
+ if (!isVisible()) {
+ return KexiToolTip::close(alsoDelete);
+ }
+ if (m_opacity>0.0)
+ decreaseOpacity();
+ else
+ return KexiToolTip::close(alsoDelete);
+ return m_opacity<=0.0;
+}
+
+void KexiArrowTip::drawContents(QPainter& p)
+{
+ p.setPen( QPen(palette().active().foreground(), 1) );
+ p.drawText(QRect(0,m_arrowHeight,width(),height()-m_arrowHeight),
+ Qt::AlignCenter, m_value.toString());
+}
+
+void KexiArrowTip::drawFrame(QPainter& p)
+{
+ QPen pen(palette().active().foreground(), 1, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
+ p.setPen( pen );
+ /*
+ /\
+ +- -----+
+ | text |
+ +--------+
+ */
+ //1st line
+ const int arrowOffset = 5; //5 pixels to right
+ QPointArray pa(8);
+ pa.setPoint(0, 0, m_arrowHeight-1);
+ pa.setPoint(1, 0, height()-1);
+ pa.setPoint(2, width()-1, height()-1);
+ pa.setPoint(3, width()-1, m_arrowHeight-1);
+ pa.setPoint(4, arrowOffset+m_arrowHeight+m_arrowHeight-2, m_arrowHeight-1);
+ pa.setPoint(5, arrowOffset+m_arrowHeight-1, 0);
+ pa.setPoint(6, arrowOffset, m_arrowHeight-1);
+ pa.setPoint(7, 0, m_arrowHeight-1);
+ p.drawPolyline(pa);
+ //-2nd, internal line
+ pa.resize(12);
+ pa.setPoint(0, 1, m_arrowHeight);
+ pa.setPoint(1, 1, height()-2);
+ pa.setPoint(2, width()-2, height()-2);
+ pa.setPoint(3, width()-2, m_arrowHeight);
+ pa.setPoint(4, arrowOffset+m_arrowHeight+m_arrowHeight-2, m_arrowHeight);
+ pa.setPoint(5, arrowOffset+m_arrowHeight-1, 1);
+ pa.setPoint(6, arrowOffset, m_arrowHeight);
+ pa.setPoint(7, 0, m_arrowHeight);
+ pa.setPoint(8, arrowOffset+1, m_arrowHeight);
+ pa.setPoint(9, arrowOffset+m_arrowHeight-1, 2);
+ pa.setPoint(10, arrowOffset+m_arrowHeight+m_arrowHeight-3, m_arrowHeight);
+ pa.setPoint(11, width()-2, m_arrowHeight);
+ p.drawPolyline(pa);
+}
+
+#include "kexiarrowtip.moc"
diff --git a/kexi/widget/utils/kexiarrowtip.h b/kexi/widget/utils/kexiarrowtip.h
new file mode 100644
index 000000000..d6de61868
--- /dev/null
+++ b/kexi/widget/utils/kexiarrowtip.h
@@ -0,0 +1,56 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIARROWTIP_H
+#define KEXIARROWTIP_H
+
+#include "kexitooltip.h"
+
+//! \brief A tooltip-like widget with additional arrow
+/*! The widget also suppors fade in and fade out effect,
+ if the underlying display system supports this.
+*/
+class KEXIGUIUTILS_EXPORT KexiArrowTip : public KexiToolTip
+{
+ Q_OBJECT
+ public:
+ KexiArrowTip(const QString& text, QWidget* parent);
+ virtual ~KexiArrowTip();
+
+ inline QString text() const { return m_value.toString(); }
+ virtual bool close() { return close(false); }
+ virtual bool close( bool alsoDelete );
+
+ public slots:
+ virtual void show();
+ virtual void hide();
+
+ protected slots:
+ void increaseOpacity();
+ void decreaseOpacity();
+
+ protected:
+ virtual void drawFrame(QPainter& p);
+ virtual void drawContents(QPainter& p);
+
+ int m_arrowHeight;
+ double m_opacity;
+};
+
+#endif
diff --git a/kexi/widget/utils/kexicomboboxdropdownbutton.cpp b/kexi/widget/utils/kexicomboboxdropdownbutton.cpp
new file mode 100644
index 000000000..407bc6fe5
--- /dev/null
+++ b/kexi/widget/utils/kexicomboboxdropdownbutton.cpp
@@ -0,0 +1,87 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexicomboboxdropdownbutton.h"
+
+#include <kpopupmenu.h>
+#include <kdebug.h>
+#include <kcombobox.h>
+
+#include <qstyle.h>
+#include <qapplication.h>
+
+KexiComboBoxDropDownButton::KexiComboBoxDropDownButton( QWidget *parent )
+ : KPushButton(parent)
+{
+ m_paintedCombo = new KComboBox(this);
+ m_paintedCombo->hide();
+ m_paintedCombo->setEditable(true);
+
+ setToggleButton(true);
+ styleChange(style());
+ m_paintedCombo->move(0,0);
+ m_paintedCombo->setFixedSize(size());
+}
+
+KexiComboBoxDropDownButton::~KexiComboBoxDropDownButton()
+{
+}
+
+void KexiComboBoxDropDownButton::drawButton(QPainter *p)
+{
+ int flags = QStyle::Style_Enabled | QStyle::Style_HasFocus;
+ if (isDown())
+ flags |= QStyle::Style_Down;
+
+ KPushButton::drawButton(p);
+
+ QRect r = rect();
+ r.setHeight(r.height()+m_fixForHeight);
+ if (m_drawComplexControl) {
+ if (m_fixForHeight>0 && m_paintedCombo->size()!=size()) {
+ m_paintedCombo->move(0,0);
+ m_paintedCombo->setFixedSize(size()+QSize(0, m_fixForHeight)); //last chance to fix size
+ }
+ style().drawComplexControl( QStyle::CC_ComboBox, p,
+ m_fixForHeight>0 ? (const QWidget*)m_paintedCombo : this, r, colorGroup(),
+ flags, (uint)(QStyle::SC_ComboBoxArrow), QStyle::SC_None );
+ }
+ else {
+ r.setWidth(r.width()+2);
+ style().drawPrimitive( QStyle::PE_ArrowDown, p, r, colorGroup(), flags);
+ }
+}
+
+void KexiComboBoxDropDownButton::styleChange( QStyle & oldStyle )
+{
+ //<hack>
+ if (qstricmp(style().name(),"thinkeramik")==0) {
+ m_fixForHeight = 3;
+ }
+ else
+ m_fixForHeight = 0;
+ //</hack>
+ m_drawComplexControl =
+ (style().inherits("KStyle") && qstricmp(style().name(),"qtcurve")!=0)
+ || qstricmp(style().name(),"platinum")==0;
+ if (m_fixForHeight==0)
+ setFixedWidth( style().querySubControlMetrics( QStyle::CC_ComboBox,
+ (const QWidget*)m_paintedCombo, QStyle::SC_ComboBoxArrow ).width() +1 );
+ KPushButton::styleChange(oldStyle);
+}
diff --git a/kexi/widget/utils/kexicomboboxdropdownbutton.h b/kexi/widget/utils/kexicomboboxdropdownbutton.h
new file mode 100644
index 000000000..da53a7e2e
--- /dev/null
+++ b/kexi/widget/utils/kexicomboboxdropdownbutton.h
@@ -0,0 +1,49 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiComboBoxDropDownButton_H
+#define KexiComboBoxDropDownButton_H
+
+#include <kpushbutton.h>
+
+class KComboBox;
+
+//! @short A drop-down button for combo box widgets
+/*! Used in KexiComboBoxTableEdit.
+*/
+class KEXIGUIUTILS_EXPORT KexiComboBoxDropDownButton : public KPushButton
+{
+ public:
+ KexiComboBoxDropDownButton( QWidget *parent );
+ virtual ~KexiComboBoxDropDownButton();
+
+ protected:
+ /*! Reimplemented after @ref KPushButton to draw drop-down arrow. */
+ virtual void drawButton(QPainter *p);
+
+ /*! Reimplemented after @ref KPushButton to adapt size to style changes. */
+ virtual void styleChange( QStyle & oldStyle );
+
+ int m_fixForHeight;
+ bool m_drawComplexControl : 1;
+ KComboBox *m_paintedCombo; //!< fake combo used only to pass it as 'this' for QStyle
+ //!< (because styles use \<static_cast\>)
+};
+
+#endif
diff --git a/kexi/widget/utils/kexicontextmenuutils.cpp b/kexi/widget/utils/kexicontextmenuutils.cpp
new file mode 100644
index 000000000..727cef6f0
--- /dev/null
+++ b/kexi/widget/utils/kexicontextmenuutils.cpp
@@ -0,0 +1,283 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexicontextmenuutils.h"
+
+#include <kactioncollection.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kfiledialog.h>
+#include <kimageio.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+
+#include <qfiledialog.h>
+#include <qapplication.h>
+
+#ifdef Q_WS_WIN
+#include <win32_utils.h>
+#include <krecentdirs.h>
+#endif
+
+//! @internal
+class KexiImageContextMenu::Private
+{
+public:
+ Private(QWidget *parent)
+ : actionCollection(parent)
+ {
+ }
+
+ KActionCollection actionCollection;
+ KAction *insertFromFileAction, *saveAsAction, *cutAction, *copyAction, *pasteAction,
+ *deleteAction
+#ifdef KEXI_NO_UNFINISHED
+ , *propertiesAction
+#endif
+ ;
+};
+
+//------------
+
+KexiImageContextMenu::KexiImageContextMenu(QWidget* parent)
+ : KPopupMenu(parent)
+ , d( new Private(this) )
+{
+ setName("KexiImageContextMenu");
+ insertTitle(QString::null);
+
+ d->insertFromFileAction = new KAction(i18n("Insert From &File..."), SmallIconSet("fileopen"), 0,
+ this, SLOT(insertFromFile()), &d->actionCollection, "insert");
+ d->insertFromFileAction->plug(this);
+ d->saveAsAction = KStdAction::saveAs(this, SLOT(saveAs()), &d->actionCollection);
+// d->saveAsAction->setText(i18n("&Save &As..."));
+ d->saveAsAction->plug(this);
+ insertSeparator();
+ d->cutAction = KStdAction::cut(this, SLOT(cut()), &d->actionCollection);
+ d->cutAction->plug(this);
+ d->copyAction = KStdAction::copy(this, SLOT(copy()), &d->actionCollection);
+ d->copyAction->plug(this);
+ d->pasteAction = KStdAction::paste(this, SLOT(paste()), &d->actionCollection);
+ d->pasteAction->plug(this);
+ d->deleteAction = new KAction(i18n("&Clear"), SmallIconSet("editdelete"), 0,
+ this, SLOT(clear()), &d->actionCollection, "delete");
+ d->deleteAction->plug(this);
+#ifdef KEXI_NO_UNFINISHED
+ d->propertiesAction = 0;
+#else
+ insertSeparator();
+ d->propertiesAction = new KAction(i18n("Properties"), 0, 0,
+ this, SLOT(showProperties()), &d->actionCollection, "properties");
+ d->propertiesAction->plug(this);
+#endif
+ connect(this, SIGNAL(aboutToShow()), this, SLOT(updateActionsAvailability()));
+}
+
+KexiImageContextMenu::~KexiImageContextMenu()
+{
+ delete d;
+}
+
+void KexiImageContextMenu::insertFromFile()
+{
+// QWidget *focusWidget = qApp->focusWidget();
+#ifdef Q_WS_WIN
+ QString recentDir;
+ QString fileName = QFileDialog::getOpenFileName(
+ KFileDialog::getStartURL(":LastVisitedImagePath", recentDir).path(),
+ convertKFileDialogFilterToQFileDialogFilter(KImageIO::pattern(KImageIO::Reading)),
+ this, 0, i18n("Insert Image From File"));
+ KURL url;
+ if (!fileName.isEmpty())
+ url.setPath( fileName );
+#else
+ KURL url( KFileDialog::getImageOpenURL(
+ ":LastVisitedImagePath", this, i18n("Insert Image From File")) );
+// QString fileName = url.isLocalFile() ? url.path() : url.prettyURL();
+
+ //! @todo download the file if remote, then set fileName properly
+#endif
+ if (!url.isValid()) {
+ //focus the app again because to avoid annoying the user with unfocused main window
+ if (qApp->mainWidget()) {
+ //focusWidget->raise();
+ //focusWidget->setFocus();
+ qApp->mainWidget()->raise();
+ }
+ return;
+ }
+ kexipluginsdbg << "fname=" << url.prettyURL() << endl;
+
+#ifdef Q_WS_WIN
+ //save last visited path
+// KURL url(fileName);
+ if (url.isLocalFile())
+ KRecentDirs::add(":LastVisitedImagePath", url.directory());
+#endif
+
+ emit insertFromFileRequested(url);
+ if (qApp->mainWidget()) {
+// focusWidget->raise();
+// focusWidget->setFocus();
+ qApp->mainWidget()->raise();
+ }
+}
+
+void KexiImageContextMenu::saveAs()
+{
+ QString origFilename, fileExtension;
+ bool dataIsEmpty = false;
+ emit aboutToSaveAsRequested(origFilename, fileExtension, dataIsEmpty);
+
+ if (dataIsEmpty) {
+ kdWarning() << "KexiImageContextMenu::saveAs(): no data!" << endl;
+ return;
+ }
+ if (!origFilename.isEmpty())
+ origFilename = QString("/") + origFilename;
+
+ if (fileExtension.isEmpty()) {
+ // PNG data is the default
+ fileExtension = "png";
+ }
+
+#ifdef Q_WS_WIN
+ QString recentDir;
+ QString fileName = QFileDialog::getSaveFileName(
+ KFileDialog::getStartURL(":LastVisitedImagePath", recentDir).path() + origFilename,
+ convertKFileDialogFilterToQFileDialogFilter(KImageIO::pattern(KImageIO::Writing)),
+ this, 0, i18n("Save Image to File"));
+#else
+ //! @todo add originalFileName! (requires access to KRecentDirs)
+ QString fileName = KFileDialog::getSaveFileName(
+ ":LastVisitedImagePath", KImageIO::pattern(KImageIO::Writing), this, i18n("Save Image to File"));
+#endif
+ if (fileName.isEmpty())
+ return;
+
+ if (QFileInfo(fileName).extension().isEmpty())
+ fileName += (QString(".")+fileExtension);
+ kdDebug() << fileName << endl;
+ KURL url;
+ url.setPath( fileName );
+
+#ifdef Q_WS_WIN
+ //save last visited path
+ if (url.isLocalFile())
+ KRecentDirs::add(":LastVisitedImagePath", url.directory());
+#endif
+
+ QFile f(fileName);
+ if (f.exists() && KMessageBox::Yes != KMessageBox::warningYesNo(this,
+ "<qt>"+i18n("File \"%1\" already exists."
+ "<p>Do you want to replace it with a new one?")
+ .arg(QDir::convertSeparators(fileName))+"</qt>",0,
+ KGuiItem(i18n("&Replace")), KGuiItem(i18n("&Don't Replace"))))
+ {
+ return;
+ }
+
+//! @todo use KURL?
+ emit saveAsRequested(fileName);
+}
+
+void KexiImageContextMenu::cut()
+{
+ emit cutRequested();
+}
+
+void KexiImageContextMenu::copy()
+{
+ emit copyRequested();
+}
+
+void KexiImageContextMenu::paste()
+{
+ emit pasteRequested();
+}
+
+void KexiImageContextMenu::clear()
+{
+ emit clearRequested();
+}
+
+void KexiImageContextMenu::showProperties()
+{
+ emit showPropertiesRequested();
+}
+
+void KexiImageContextMenu::updateActionsAvailability()
+{
+ bool valueIsNull = true;
+ bool valueIsReadOnly = true;
+ emit updateActionsAvailabilityRequested(valueIsNull, valueIsReadOnly);
+
+ d->insertFromFileAction->setEnabled( !valueIsReadOnly );
+ d->saveAsAction->setEnabled( !valueIsNull );
+ d->cutAction->setEnabled( !valueIsNull && !valueIsReadOnly );
+ d->copyAction->setEnabled( !valueIsNull );
+ d->pasteAction->setEnabled( !valueIsReadOnly );
+ d->deleteAction->setEnabled( !valueIsNull && !valueIsReadOnly );
+ if (d->propertiesAction)
+ d->propertiesAction->setEnabled( !valueIsNull );
+}
+
+KActionCollection* KexiImageContextMenu::actionCollection() const
+{
+ return &d->actionCollection;
+}
+
+//static
+bool KexiImageContextMenu::updateTitle(QPopupMenu *menu, const QString& title, const QString& iconName)
+{
+ return KexiContextMenuUtils::updateTitle(menu, title, i18n("Image"), iconName);
+}
+
+// -------------------------------------------
+
+//static
+bool KexiContextMenuUtils::updateTitle(QPopupMenu *menu, const QString& objectName,
+ const QString& objectTypeName, const QString& iconName)
+{
+ if (!menu || objectName.isEmpty() || objectTypeName.isEmpty())
+ return false;
+ const int id = menu->idAt(0);
+ QMenuItem *item = menu->findItem(id);
+ if (!item)
+ return false;
+ KPopupTitle *title = dynamic_cast<KPopupTitle *>(item->widget());
+ if (!title)
+ return false;
+
+/*! @todo look at makeFirstCharacterUpperCaseInCaptions setting [bool]
+ (see doc/dev/settings.txt) */
+ QString realTitle( i18n("Object name : Object type", "%1 : %2")
+ .arg( objectName[0].upper() + objectName.mid(1) )
+ .arg( objectTypeName ));
+
+ if (iconName.isEmpty())
+ title->setTitle(realTitle);
+ else {
+ QPixmap pixmap(SmallIcon( iconName ));
+ title->setTitle(realTitle, &pixmap);
+ }
+ return true;
+}
+
+#include "kexicontextmenuutils.moc"
diff --git a/kexi/widget/utils/kexicontextmenuutils.h b/kexi/widget/utils/kexicontextmenuutils.h
new file mode 100644
index 000000000..95258e964
--- /dev/null
+++ b/kexi/widget/utils/kexicontextmenuutils.h
@@ -0,0 +1,112 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiContextMenuUtils_H
+#define KexiContextMenuUtils_H
+
+#include <kexidb/queryschema.h>
+#include <kpopupmenu.h>
+#include <kurl.h>
+
+class KActionCollection;
+class KexiDataItemInterface;
+
+//! @short A set of helpers for updating popup menu titles
+/*! The functions set meaningful titles like "Emploee : Image".
+*/
+class KEXIGUIUTILS_EXPORT KexiContextMenuUtils
+{
+ public:
+ /*! Updates title for context menu.
+ \return true if the title has been updated. */
+ static bool updateTitle(QPopupMenu *menu, const QString& objectName,
+ const QString& objectTypeName, const QString& iconName);
+};
+
+//! @short A context menu used for images within form and table views
+/*! Used in KexiDBImageBox and KexiBlobTableEdit.
+ Contains actions like insert, save, copy, paste, clear.
+
+ Signals like insertFromFileRequested() are all connected to
+ handlers in KexiDBImageBox and KexiBlobTableEdit so these objects can
+ respond on requests for data handling.
+*/
+class KEXIGUIUTILS_EXPORT KexiImageContextMenu : public KPopupMenu
+{
+ Q_OBJECT
+
+ public:
+ KexiImageContextMenu(QWidget *parent);
+ virtual ~KexiImageContextMenu();
+
+ KActionCollection* actionCollection() const;
+
+ /*! Updates title for context menu.
+ Used in KexiDBWidgetContextMenuExtender::createTitle(QPopupMenu *menu) and KexiDBImageBox.
+ \return true if the title has been updated. */
+ static bool updateTitle(QPopupMenu *menu, const QString& title, const QString& iconName = QString::null);
+
+ public slots:
+ void updateActionsAvailability();
+
+ virtual void insertFromFile();
+ virtual void saveAs();
+ virtual void cut();
+ virtual void copy();
+ virtual void paste();
+ virtual void clear();
+ virtual void showProperties();
+
+ signals:
+ //! Emitted when actions availability should be performed. Just connect this signal
+ //! to a slot and set \a valueIsNull and \a valueIsReadOnly.
+ void updateActionsAvailabilityRequested(bool& valueIsNull, bool& valueIsReadOnly);
+
+ /*! Emitted before "insertFromFile" action was requested. */
+ void insertFromFileRequested(const KURL &url);
+
+ /*! Emitted before "saveAs" action was requested.
+ You should fill \a origFilename, \a fileExtension and \a dataIsEmpty values.
+ If \a dataIsEmpty is false, saving will be cancelled. */
+ void aboutToSaveAsRequested(QString& origFilename, QString& fileExtension, bool& dataIsEmpty);
+
+ //! Emitted when "saveAs" action was requested
+ void saveAsRequested(const QString& fileName);
+
+ //! Emitted when "cut" action was requested
+ void cutRequested();
+
+ //! Emitted when "copy" action was requested
+ void copyRequested();
+
+ //! Emitted when "paste" action was requested
+ void pasteRequested();
+
+ //! Emitted when "clear" action was requested
+ void clearRequested();
+
+ //! Emitted when "showProperties" action was requested
+ void showPropertiesRequested();
+
+ protected:
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kexi/widget/utils/kexidatetimeformatter.cpp b/kexi/widget/utils/kexidatetimeformatter.cpp
new file mode 100644
index 000000000..d8f642ca0
--- /dev/null
+++ b/kexi/widget/utils/kexidatetimeformatter.cpp
@@ -0,0 +1,367 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidatetimeformatter.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kdatepicker.h>
+#include <kdatetbl.h>
+#include <klineedit.h>
+#include <kpopupmenu.h>
+#include <kdatewidget.h>
+
+KexiDateFormatter::KexiDateFormatter()
+{
+ // use "short date" format system settings
+//! @todo allow to override the format using column property and/or global app settings
+ QString df( KGlobal::locale()->dateFormatShort() );
+ if (df.length()>2)
+ m_separator = df.mid(2,1);
+ else
+ m_separator = "-";
+ const int separatorLen = m_separator.length();
+ QString yearMask("9999");
+ QString yearDateFormat("yyyy"),
+ monthDateFormat("MM"),
+ dayDateFormat("dd"); //for setting up m_dateFormat
+ bool ok = df.length()>=8;
+ int yearpos, monthpos, daypos; //result of df.find()
+ if (ok) {//look at % variables
+//! @todo more variables are possible here, see void KLocale::setDateFormatShort() docs
+//! http://developer.kde.org/documentation/library/3.5-api/kdelibs-apidocs/kdecore/html/classKLocale.html#a59
+ yearpos = df.find("%y", 0, false); //&y or %y
+ m_longYear = !(yearpos>=0 && df.mid(yearpos+1, 1)=="y");
+ if (!m_longYear) {
+ yearMask = "99";
+ yearDateFormat = "yy";
+ }
+ monthpos = df.find("%m", 0, true); //%m or %n
+ m_monthWithLeadingZero = true;
+ if (monthpos<0) {
+ monthpos = df.find("%n", 0, false);
+ m_monthWithLeadingZero = false;
+ monthDateFormat = "M";
+ }
+ daypos = df.find("%d", 0, true);//%d or %e
+ m_dayWithLeadingZero = true;
+ if (daypos<0) {
+ daypos = df.find("%e", 0, false);
+ m_dayWithLeadingZero = false;
+ dayDateFormat = "d";
+ }
+ ok = (yearpos>=0 && monthpos>=0 && daypos>=0);
+ }
+ m_order = QDateEdit::YMD; //default
+ if (ok) {
+ if (yearpos<monthpos && monthpos<daypos) {
+ //will be set in "default: YMD"
+ }
+ else if (yearpos<daypos && daypos<monthpos) {
+ m_order = QDateEdit::YDM;
+//! @todo use QRegExp (to replace %Y by %1, etc.) instead of hardcoded "%1%299%399"
+//! because df may contain also other characters
+ m_inputMask = QString("%1%299%399").arg(yearMask).arg(m_separator).arg(m_separator);
+ m_qtFormat = yearDateFormat+m_separator+dayDateFormat+m_separator+monthDateFormat;
+ m_yearpos = 0;
+ m_daypos = yearMask.length()+separatorLen;
+ m_monthpos = m_daypos+2+separatorLen;
+ }
+ else if (daypos<monthpos && monthpos<yearpos) {
+ m_order = QDateEdit::DMY;
+ m_inputMask = QString("99%199%2%3").arg(m_separator).arg(m_separator).arg(yearMask);
+ m_qtFormat = dayDateFormat+m_separator+monthDateFormat+m_separator+yearDateFormat;
+ m_daypos = 0;
+ m_monthpos = 2+separatorLen;
+ m_yearpos = m_monthpos+2+separatorLen;
+ }
+ else if (monthpos<daypos && daypos<yearpos) {
+ m_order = QDateEdit::MDY;
+ m_inputMask = QString("99%199%2%3").arg(m_separator).arg(m_separator).arg(yearMask);
+ m_qtFormat = monthDateFormat+m_separator+dayDateFormat+m_separator+yearDateFormat;
+ m_monthpos = 0;
+ m_daypos = 2+separatorLen;
+ m_yearpos = m_daypos+2+separatorLen;
+ }
+ else
+ ok = false;
+ }
+ if (!ok || m_order == QDateEdit::YMD) {//default: YMD
+ m_inputMask = QString("%1%299%399").arg(yearMask).arg(m_separator).arg(m_separator);
+ m_qtFormat = yearDateFormat+m_separator+monthDateFormat+m_separator+dayDateFormat;
+ m_yearpos = 0;
+ m_monthpos = yearMask.length()+separatorLen;
+ m_daypos = m_monthpos+2+separatorLen;
+ }
+ m_inputMask += ";_";
+}
+
+KexiDateFormatter::~KexiDateFormatter()
+{
+}
+
+QDate KexiDateFormatter::stringToDate( const QString& str ) const
+{
+ bool ok = true;
+ int year = str.mid(m_yearpos, m_longYear ? 4 : 2).toInt(&ok);
+ if (!ok)
+ return QDate();
+ if (year < 30) {//2000..2029
+ year = 2000 + year;
+ }
+ else if (year < 100) {//1930..1999
+ year = 1900 + year;
+ }
+
+ int month = str.mid(m_monthpos, 2).toInt(&ok);
+ if (!ok)
+ return QDate();
+
+ int day = str.mid(m_daypos, 2).toInt(&ok);
+ if (!ok)
+ return QDate();
+
+ QDate date(year, month, day);
+ if (!date.isValid())
+ return QDate();
+ return date;
+}
+
+QVariant KexiDateFormatter::stringToVariant( const QString& str ) const
+{
+ if (isEmpty(str))
+ return QVariant();
+ const QDate date( stringToDate( str ) );
+ if (date.isValid())
+ return date;
+ return QVariant();
+}
+
+bool KexiDateFormatter::isEmpty( const QString& str ) const
+{
+ QString s(str);
+ return s.replace(m_separator,"").stripWhiteSpace().isEmpty();
+}
+
+QString KexiDateFormatter::dateToString( const QDate& date ) const
+{
+ return date.toString(m_qtFormat);
+}
+
+//------------------------------------------------
+
+KexiTimeFormatter::KexiTimeFormatter()
+: m_hmsRegExp( new QRegExp("(\\d*):(\\d*):(\\d*).*( am| pm){,1}", false/*!CS*/) )
+ , m_hmRegExp( new QRegExp("(\\d*):(\\d*).*( am| pm){,1}", false/*!CS*/) )
+{
+ QString tf( KGlobal::locale()->timeFormat() );
+ //m_hourpos, m_minpos, m_secpos; are result of tf.find()
+ QString hourVariable, minVariable, secVariable;
+
+ //detect position of HOUR section: find %H or %k or %I or %l
+ m_24h = true;
+ m_hoursWithLeadingZero = true;
+ m_hourpos = tf.find("%H", 0, true);
+ if (m_hourpos>=0) {
+ m_24h = true;
+ m_hoursWithLeadingZero = true;
+ }
+ else {
+ m_hourpos = tf.find("%k", 0, true);
+ if (m_hourpos>=0) {
+ m_24h = true;
+ m_hoursWithLeadingZero = false;
+ }
+ else {
+ m_hourpos = tf.find("%I", 0, true);
+ if (m_hourpos>=0) {
+ m_24h = false;
+ m_hoursWithLeadingZero = true;
+ }
+ else {
+ m_hourpos = tf.find("%l", 0, true);
+ if (m_hourpos>=0) {
+ m_24h = false;
+ m_hoursWithLeadingZero = false;
+ }
+ }
+ }
+ }
+ m_minpos = tf.find("%M", 0, true);
+ m_secpos = tf.find("%S", 0, true); //can be -1
+ m_ampmpos = tf.find("%p", 0, true); //can be -1
+
+ if (m_hourpos<0 || m_minpos<0) {
+ //set default: hr and min are needed, sec are optional
+ tf = "%H:%M:%S";
+ m_24h = true;
+ m_hoursWithLeadingZero = false;
+ m_hourpos = 0;
+ m_minpos = 3;
+ m_secpos = m_minpos + 3;
+ m_ampmpos = -1;
+ }
+ hourVariable = tf.mid(m_hourpos, 2);
+
+ m_inputMask = tf;
+// m_inputMask.replace( hourVariable, "00" );
+// m_inputMask.replace( "%M", "00" );
+// m_inputMask.replace( "%S", "00" ); //optional
+ m_inputMask.replace( hourVariable, "99" );
+ m_inputMask.replace( "%M", "99" );
+ m_inputMask.replace( "%S", "00" ); //optional
+ m_inputMask.replace( "%p", "AA" ); //am or pm
+ m_inputMask += ";_";
+
+ m_outputFormat = tf;
+}
+
+KexiTimeFormatter::~KexiTimeFormatter()
+{
+ delete m_hmsRegExp;
+ delete m_hmRegExp;
+}
+
+QTime KexiTimeFormatter::stringToTime( const QString& str ) const
+{
+ int hour, min, sec;
+ bool pm = false;
+
+ bool tryWithoutSeconds = true;
+ if (m_secpos>=0) {
+ if (-1 != m_hmsRegExp->search(str)) {
+ hour = m_hmsRegExp->cap(1).toInt();
+ min = m_hmsRegExp->cap(2).toInt();
+ sec = m_hmsRegExp->cap(3).toInt();
+ if (m_ampmpos >= 0 && m_hmsRegExp->numCaptures()>3)
+ pm = m_hmsRegExp->cap(4).stripWhiteSpace().lower()=="pm";
+ tryWithoutSeconds = false;
+ }
+ }
+ if (tryWithoutSeconds) {
+ if (-1 == m_hmRegExp->search(str))
+ return QTime(99,0,0);
+ hour = m_hmRegExp->cap(1).toInt();
+ min = m_hmRegExp->cap(2).toInt();
+ sec = 0;
+ if (m_ampmpos >= 0 && m_hmRegExp->numCaptures()>2)
+ pm = m_hmsRegExp->cap(4).lower()=="pm";
+ }
+
+ if (pm && hour < 12)
+ hour += 12; //PM
+ return QTime(hour, min, sec);
+}
+
+QVariant KexiTimeFormatter::stringToVariant( const QString& str )
+{
+ if (isEmpty( str ))
+ return QVariant();
+ const QTime time( stringToTime( str ) );
+ if (time.isValid())
+ return time;
+ return QVariant();
+}
+
+bool KexiTimeFormatter::isEmpty( const QString& str ) const
+{
+ QString s(str);
+ return s.replace(':',"").stripWhiteSpace().isEmpty();
+}
+
+QString KexiTimeFormatter::timeToString( const QTime& time ) const
+{
+ if (!time.isValid())
+ return QString::null;
+
+ QString s(m_outputFormat);
+ if (m_24h) {
+ if (m_hoursWithLeadingZero)
+ s.replace( "%H", QString::fromLatin1(time.hour()<10 ? "0" : "") + QString::number(time.hour()) );
+ else
+ s.replace( "%k", QString::number(time.hour()) );
+ }
+ else {
+ int time12 = (time.hour()>12) ? (time.hour()-12) : time.hour();
+ if (m_hoursWithLeadingZero)
+ s.replace( "%I", QString::fromLatin1(time12<10 ? "0" : "") + QString::number(time12) );
+ else
+ s.replace( "%l", QString::number(time12) );
+ }
+ s.replace( "%M", QString::fromLatin1(time.minute()<10 ? "0" : "") + QString::number(time.minute()) );
+ if (m_secpos>=0)
+ s.replace( "%S", QString::fromLatin1(time.second()<10 ? "0" : "") + QString::number(time.second()) );
+ if (m_ampmpos>=0)
+ s.replace( "%p", KGlobal::locale()->translate( time.hour()>=12 ? "pm" : "am") );
+ return s;
+}
+
+//------------------------------------------------
+
+QString dateTimeInputMask(const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter)
+{
+ QString mask(dateFormatter.inputMask());
+ mask.truncate(dateFormatter.inputMask().length()-2);
+ return mask + " " + timeFormatter.inputMask();
+}
+
+QDateTime stringToDateTime(
+ const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter, const QString& str)
+{
+ QString s( str.stripWhiteSpace() );
+ const int timepos = s.find(" ");
+ const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(s.mid(timepos+1)); //.replace(':',"").stripWhiteSpace().isEmpty();
+ if (emptyTime)
+ s = s.left(timepos);
+ if (timepos>0 && !emptyTime) {
+ return QDateTime(
+ dateFormatter.stringToDate( s.left(timepos) ),
+ timeFormatter.stringToTime( s.mid(timepos+1) )
+ );
+ }
+ else {
+ return QDateTime(
+ dateFormatter.stringToDate( s ),
+ QTime(0,0,0)
+ );
+ }
+}
+
+bool dateTimeIsEmpty( const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter,
+ const QString& str )
+{
+ int timepos = str.find(" ");
+ const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(str.mid(timepos+1)); //s.mid(timepos+1).replace(':',"").stripWhiteSpace().isEmpty();
+ return (timepos >= 0 && dateFormatter.isEmpty(str.left(timepos)) //s.left(timepos).replace(m_dateFormatter.separator(), "").stripWhiteSpace().isEmpty()
+ && emptyTime);
+}
+
+bool dateTimeIsValid( const KexiDateFormatter& dateFormatter,
+ const KexiTimeFormatter& timeFormatter, const QString& str )
+{
+ int timepos = str.find(" ");
+ const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(str.mid(timepos+1)); //s.mid(timepos+1).replace(':',"").stripWhiteSpace().isEmpty();
+ if (timepos >= 0 && dateFormatter.isEmpty(str.left(timepos)) // s.left(timepos).replace(m_dateFormatter.separator(), "").stripWhiteSpace().isEmpty()
+ && emptyTime)
+ //empty date/time is valid
+ return true;
+ return timepos>=0 && dateFormatter.stringToDate( str.left(timepos) ).isValid()
+ && (emptyTime /*date without time is also valid*/ || timeFormatter.stringToTime( str.mid(timepos+1) ).isValid());
+}
diff --git a/kexi/widget/utils/kexidatetimeformatter.h b/kexi/widget/utils/kexidatetimeformatter.h
new file mode 100644
index 000000000..252bc535e
--- /dev/null
+++ b/kexi/widget/utils/kexidatetimeformatter.h
@@ -0,0 +1,165 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIDATETIMEFORMATTER_H
+#define KEXIDATETIMEFORMATTER_H
+
+#include <qdatetimeedit.h>
+#include <qregexp.h>
+
+//! @short Date formatter used by KexiDateTableEdit and KexiDateTimeTableEdit
+class KEXIGUIUTILS_EXPORT KexiDateFormatter
+{
+ public:
+ //! Creates new formatter with KDE setting for "short date"
+ KexiDateFormatter();
+
+ //! Creates new formatter with given settings
+//! @todo KexiDateFormatter(... settings ...);
+
+ ~KexiDateFormatter();
+
+ //! Converts string \a str to date using predefined settings.
+ //! \return invalid date if the conversion is impossible
+ QDate stringToDate( const QString& str ) const;
+
+ /*! Converts string \a str to date using predefined settings
+ and returns QVariant containing the date value.
+ This method does the same as stringToDate() but if \a string
+ contains invalid date representation, e.g. contains only spaces
+ and separators, null QVariant() is returned. */
+ QVariant stringToVariant( const QString& str ) const;
+
+ //! Converts \a date to string using predefined settings.
+ //! \return null string if \a date is invalid
+ QString dateToString( const QDate& date ) const;
+
+ //! \return Input mask generated using the formatter settings.
+ //! Can be used in QLineEdit::setInputMask().
+ QString inputMask() const { return m_inputMask; }
+
+ //! \return separator for this date format, a single character like "-" or "/"
+ QString separator() const { return m_separator; }
+
+ //! \return true if \a str contains only spaces
+ //! and separators according to the date format.
+ bool isEmpty( const QString& str ) const;
+
+ protected:
+ //! Input mask generated using the formatter settings. Can be used in QLineEdit::setInputMask().
+ QString m_inputMask;
+
+ //! Order of date sections
+ QDateEdit::Order m_order;
+
+ //! 4 or 2 digits
+ bool m_longYear;
+
+ bool m_monthWithLeadingZero, m_dayWithLeadingZero;
+
+ //! Date format used in dateToString()
+ QString m_qtFormat;
+
+ //! Used in stringToDate() to convert string back to QDate
+ int m_yearpos, m_monthpos, m_daypos;
+
+ QString m_separator;
+};
+
+/*! @short Time formatter used by KexiTimeTableEdit and KexiDateTimeTableEdit
+ Following time formats are allowed: HH:MM:SS (24h), HH:MM (24h), HH:MM AM/PM (12h)
+ Separator MUST be ":" */
+class KEXIGUIUTILS_EXPORT KexiTimeFormatter
+{
+ public:
+ //! Creates new formatter with KDE setting for time
+ KexiTimeFormatter();
+
+ //! Creates new formatter with given settings
+//! @todo KexiDateFormatter(... settings ...);
+
+ ~KexiTimeFormatter();
+
+ //! converts string \a str to time using predefined settings
+ //! \return invalid time if the conversion is impossible
+ QTime stringToTime( const QString& str ) const;
+
+ /*! Converts string \a str to time using predefined settings
+ and returns QVariant containing the time value.
+ This method does the same as stringToTime() but if \a string
+ contains invalid time representation, e.g. contains only spaces
+ and separators, null QVariant() is returned. */
+ QVariant stringToVariant( const QString& str );
+
+ //! converts \a time to string using predefined settings
+ //! \return null string if \a time is invalid
+ QString timeToString( const QTime& time ) const;
+
+ //! \return Input mask generated using the formatter settings.
+ //! Can be used in QLineEdit::setInputMask().
+ QString inputMask() const { return m_inputMask; }
+
+ //! \return true if \a str contains only spaces
+ //! and separators according to the time format.
+ bool isEmpty( const QString& str ) const;
+
+ protected:
+ //! Input mask generated using the formatter settings. Can be used in QLineEdit::setInputMask().
+ QString m_inputMask;
+
+// //! Order of date sections
+// QDateEdit::Order m_order;
+
+ //! 12 or 12h
+ bool m_24h;
+
+ bool m_hoursWithLeadingZero;
+
+ //! Time format used in timeToString(). Notation from KLocale::setTimeFormat() is used.
+ QString m_outputFormat;
+
+ //! Used in stringToTime() to convert string back to QTime
+ int m_hourpos, m_minpos, m_secpos, m_ampmpos;
+
+ QRegExp *m_hmsRegExp, *m_hmRegExp;
+};
+
+//! \return a date/time input mask using date and time formatter.
+//! Date is separated from time by one space character.
+KEXIGUIUTILS_EXPORT QString dateTimeInputMask(
+ const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter);
+
+/*! \return a QDateTime value converted from string using \a dateFormatter and \a timeFormatter.
+ A single space between date and time is assumed.
+ Invalid value is returned when \a str contains no valid date or \a str contains invalid time.
+ Value with time equal 00:00:00 is returned if \a str contains empty time part. */
+KEXIGUIUTILS_EXPORT QDateTime stringToDateTime(
+ const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter, const QString& str);
+
+/*! \return true if \a str contains only spaces and separators according to formats provided by
+ \a dateFormatter and \a timeFormatter. */
+KEXIGUIUTILS_EXPORT bool dateTimeIsEmpty( const KexiDateFormatter& dateFormatter,
+ const KexiTimeFormatter& timeFormatter, const QString& str );
+
+/*! \return true if \a str gives valid date/time value according to formats provided by
+ \a dateFormatter and \a timeFormatter. */
+KEXIGUIUTILS_EXPORT bool dateTimeIsValid( const KexiDateFormatter& dateFormatter,
+ const KexiTimeFormatter& timeFormatter, const QString& str );
+
+#endif
diff --git a/kexi/widget/utils/kexidisplayutils.cpp b/kexi/widget/utils/kexidisplayutils.cpp
new file mode 100644
index 000000000..c7d238b1d
--- /dev/null
+++ b/kexi/widget/utils/kexidisplayutils.cpp
@@ -0,0 +1,172 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidisplayutils.h"
+
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qimage.h>
+#include <qwidget.h>
+
+#include <klocale.h>
+#include <kstaticdeleter.h>
+
+// a color for displaying default values or autonumbers
+#define SPECIAL_TEXT_COLOR Qt::blue
+
+static KStaticDeleter<QPixmap> KexiDisplayUtils_autonum_deleter;
+QPixmap* KexiDisplayUtils_autonum = 0;
+
+static const unsigned int autonumber_png_len = 245;
+static const unsigned char autonumber_png_data[] = {
+ 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,
+ 0x44,0x52,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x0d,0x08,0x06,0x00,0x00,
+ 0x00,0x7f,0xf5,0x94,0x3b,0x00,0x00,0x00,0x06,0x62,0x4b,0x47,0x44,0x00,
+ 0xff,0x00,0xff,0x00,0xff,0xa0,0xbd,0xa7,0x93,0x00,0x00,0x00,0x09,0x70,
+ 0x48,0x59,0x73,0x00,0x00,0x0b,0x11,0x00,0x00,0x0b,0x11,0x01,0x7f,0x64,
+ 0x5f,0x91,0x00,0x00,0x00,0x07,0x74,0x49,0x4d,0x45,0x07,0xd4,0x08,0x14,
+ 0x0c,0x09,0x11,0x18,0x18,0x1d,0x4f,0x00,0x00,0x00,0x82,0x49,0x44,0x41,
+ 0x54,0x78,0x9c,0x8d,0x91,0x41,0x0e,0x03,0x31,0x08,0x03,0x87,0xbe,0x2e,
+ 0x1c,0xb3,0xff,0xbf,0xf6,0x1d,0xee,0x81,0xa0,0x05,0xaa,0x55,0x6b,0x29,
+ 0x92,0x03,0x06,0x59,0x06,0x49,0x48,0x02,0xa4,0xe4,0xf1,0x5f,0x1b,0xa4,
+ 0x78,0x6b,0xc3,0xc2,0x24,0x61,0x86,0x00,0x24,0x8c,0x83,0x53,0x33,0xe9,
+ 0xe6,0xaf,0x29,0x4a,0x48,0x29,0xf4,0x0d,0xbc,0xc1,0xe1,0xc9,0x46,0xb5,
+ 0x72,0xfa,0xcf,0xe2,0x2a,0x4c,0x71,0xf3,0x5c,0x2d,0xd5,0x5a,0xc0,0xcd,
+ 0x62,0xea,0x6f,0xf4,0x88,0x86,0x95,0xf0,0x4a,0xf2,0xee,0x6b,0xf8,0x1e,
+ 0x03,0x55,0xf8,0x73,0xf3,0x28,0x7e,0x6d,0x6e,0x69,0xc4,0xc6,0xfb,0x52,
+ 0x23,0x8d,0x3c,0x56,0x5e,0xd0,0x2f,0x40,0xd1,0xf4,0x6b,0xc4,0xd5,0xf8,
+ 0x07,0x69,0x14,0xc6,0x69,0x9a,0x12,0x79,0x9a,0x00,0x00,0x00,0x00,0x49,
+ 0x45,0x4e,0x44,0xae,0x42,0x60,0x82
+};
+
+/* Generated by qembed */
+#include <qcstring.h>
+#include <qdict.h>
+static struct Embed {
+ unsigned int size;
+ const unsigned char *data;
+ const char *name;
+} embed_vec[] = {
+ { 245, autonumber_png_data, "autonumber.png" },
+ { 0, 0, 0 }
+};
+
+QPixmap* getPix(int id)
+{
+// QByteArray ba;
+// ba.setRawData( (char*)embed_vec[id].data, embed_vec[id].size );
+ QPixmap *pix = new QPixmap();
+ pix->loadFromData( embed_vec[id].data, embed_vec[id].size );
+ return pix;
+}
+
+static void initDisplayUtilsImages()
+{
+ if (!KexiDisplayUtils_autonum) {
+/*! @warning not reentrant! */
+ KexiDisplayUtils_autonum_deleter.setObject( KexiDisplayUtils_autonum, getPix(0) );
+ }
+}
+
+//-----------------
+
+KexiDisplayUtils::DisplayParameters::DisplayParameters()
+{
+}
+
+KexiDisplayUtils::DisplayParameters::DisplayParameters(QWidget *w)
+{
+ textColor = w->palette().active().foreground();
+ selectedTextColor = w->palette().active().highlightedText();
+ font = w->font();
+}
+
+void KexiDisplayUtils::initDisplayForAutonumberSign(DisplayParameters& par, QWidget *widget)
+{
+ initDisplayUtilsImages();
+
+ par.textColor = SPECIAL_TEXT_COLOR;
+ par.selectedTextColor = SPECIAL_TEXT_COLOR; //hmm, unused anyway
+ par.font = widget->font();
+ par.font.setItalic(true);
+ QFontMetrics fm(par.font);
+ par.textWidth = fm.width(i18n("(autonumber)"));
+ par.textHeight = fm.height();
+}
+
+void KexiDisplayUtils::initDisplayForDefaultValue(DisplayParameters& par, QWidget *widget)
+{
+ par.textColor = SPECIAL_TEXT_COLOR;
+ par.selectedTextColor = widget->palette().active().highlightedText();
+ par.font = widget->font();
+ par.font.setItalic(true);
+}
+
+void KexiDisplayUtils::paintAutonumberSign(const DisplayParameters& par, QPainter* painter,
+ int x, int y, int width, int height, int align, bool overrideColor)
+{
+ painter->save();
+
+ painter->setFont(par.font);
+ if (!overrideColor)
+ painter->setPen(par.textColor);
+
+// int text_x = x;
+ if (!(align & Qt::AlignVertical_Mask))
+ align |= Qt::AlignVCenter;
+ if (!(align & Qt::AlignHorizontal_Mask))
+ align |= Qt::AlignLeft;
+
+ int y_pixmap_pos = 0;
+ if (align & Qt::AlignVCenter) {
+ y_pixmap_pos = QMAX(0, y+1 + (height - KexiDisplayUtils_autonum->height())/2);
+ }
+ else if (align & Qt::AlignTop) {
+ y_pixmap_pos = y + QMAX(0, (par.textHeight - KexiDisplayUtils_autonum->height())/2);
+ }
+ else if (align & Qt::AlignBottom) {
+ y_pixmap_pos = y+1 + height - KexiDisplayUtils_autonum->height()
+ - QMAX(0, (par.textHeight - KexiDisplayUtils_autonum->height())/2);
+ }
+
+ if (align & (Qt::AlignLeft | Qt::AlignJustify)) {
+// text_x = x + KexiDisplayUtils_autonum->width() + 2;
+ if (!overrideColor) {
+ painter->drawPixmap( x, y_pixmap_pos, *KexiDisplayUtils_autonum );
+ x += (KexiDisplayUtils_autonum->width() + 4);
+ }
+ }
+ else if (align & Qt::AlignRight) {
+ if (!overrideColor) {
+ painter->drawPixmap( x + width - par.textWidth - KexiDisplayUtils_autonum->width() - 4,
+ y_pixmap_pos, *KexiDisplayUtils_autonum );
+ }
+ }
+ else if (align & Qt::AlignCenter) {
+ //! @todo
+ if (!overrideColor)
+ painter->drawPixmap( x + (width - par.textWidth)/2 - KexiDisplayUtils_autonum->width() - 4,
+ y_pixmap_pos, *KexiDisplayUtils_autonum );
+ }
+
+ painter->drawText(x, y, width, height, align, i18n("(autonumber)"));
+
+ painter->restore();
+}
+
diff --git a/kexi/widget/utils/kexidisplayutils.h b/kexi/widget/utils/kexidisplayutils.h
new file mode 100644
index 000000000..8790b662b
--- /dev/null
+++ b/kexi/widget/utils/kexidisplayutils.h
@@ -0,0 +1,57 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIDISPUTILS_H
+#define KEXIDISPUTILS_H
+
+#include <qfont.h>
+#include <qcolor.h>
+class QWidget;
+
+//! \brief A set of utilities related to displaying common elements in Kexi, like e.g. (autonumber) sign
+class KEXIGUIUTILS_EXPORT KexiDisplayUtils
+{
+ public:
+ //! Stores set of display parameters used in utility functions
+ class KEXIGUIUTILS_EXPORT DisplayParameters
+ {
+ public:
+ //! Creates uninitialized parameters
+ DisplayParameters();
+
+ //! Copies properties from \a w.
+ DisplayParameters(QWidget *w);
+
+ QColor textColor, selectedTextColor;
+ QFont font;
+ int textWidth, textHeight; //!< used for "(autonumber)" text only
+ };
+
+ //! Initializes display parameters for autonumber sign
+ static void initDisplayForAutonumberSign(DisplayParameters& par, QWidget *widget);
+
+ //! Paints autonumber sign using \a par parameters
+ static void paintAutonumberSign(const DisplayParameters& par, QPainter* painter,
+ int x, int y, int width, int height, int align, bool overrideColor = false);
+
+ //! Initializes display parameters for default value
+ static void initDisplayForDefaultValue(DisplayParameters& par, QWidget *widget);
+};
+
+#endif
diff --git a/kexi/widget/utils/kexidropdownbutton.cpp b/kexi/widget/utils/kexidropdownbutton.cpp
new file mode 100644
index 000000000..a17e5cfb1
--- /dev/null
+++ b/kexi/widget/utils/kexidropdownbutton.cpp
@@ -0,0 +1,82 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidropdownbutton.h"
+
+#include <kpopupmenu.h>
+#include <kdebug.h>
+
+#include <qstyle.h>
+#include <qapplication.h>
+
+KexiDropDownButton::KexiDropDownButton(QWidget *parent)
+ : QToolButton(parent, "KexiDBImageBox::Button")
+{
+ setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+//! @todo get this from a KStyle
+// setFixedWidth(QMAX(18, qApp->globalStrut().width()));
+ int fixedWidth;
+ //hack
+ if (qstricmp(style().name(),"thinkeramik")==0)
+ fixedWidth = 18; //typical width as in "windows" style
+ else
+ fixedWidth = style().querySubControlMetrics( QStyle::CC_ComboBox,
+ this, QStyle::SC_ComboBoxArrow ).width();
+ setFixedWidth( fixedWidth );
+ setPopupDelay(10/*ms*/);
+}
+
+KexiDropDownButton::~KexiDropDownButton()
+{
+}
+
+void KexiDropDownButton::drawButton( QPainter *p )
+{
+ QToolButton::drawButton(p);
+ QStyle::SFlags arrowFlags = QStyle::Style_Default;
+ if (isDown() || state()==On)
+ arrowFlags |= QStyle::Style_Down;
+ if (isEnabled())
+ arrowFlags |= QStyle::Style_Enabled;
+ style().drawPrimitive(QStyle::PE_ArrowDown, p,
+ QRect((width()-7)/2, height()-9, 7, 7), colorGroup(),
+ arrowFlags, QStyleOption() );
+}
+
+QSize KexiDropDownButton::sizeHint () const
+{
+ return QSize( fontMetrics().maxWidth() + 2*2, fontMetrics().height()*2 + 2*2 );
+}
+
+void KexiDropDownButton::keyPressEvent( QKeyEvent * e )
+{
+ const int k = e->key();
+ const bool dropDown = (e->state() == Qt::NoButton && (k==Qt::Key_Space || k==Qt::Key_Enter || k==Qt::Key_Return || k==Qt::Key_F2 || k==Qt::Key_F4))
+ || (e->state() == Qt::AltButton && k==Qt::Key_Down);
+ if (dropDown) {
+ e->accept();
+ animateClick();
+ QMouseEvent me( QEvent::MouseButtonPress, QPoint(2,2), Qt::LeftButton, Qt::NoButton );
+ QApplication::sendEvent( this, &me );
+ return;
+ }
+ QToolButton::keyPressEvent(e);
+}
+
+#include "kexidropdownbutton.moc"
diff --git a/kexi/widget/utils/kexidropdownbutton.h b/kexi/widget/utils/kexidropdownbutton.h
new file mode 100644
index 000000000..fccbd409a
--- /dev/null
+++ b/kexi/widget/utils/kexidropdownbutton.h
@@ -0,0 +1,45 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDropDownButton_H
+#define KexiDropDownButton_H
+
+#include <qtoolbutton.h>
+#include <qguardedptr.h>
+
+//! @short A button for drop-down "Image" menu
+/*! Used in KexiDBImageBox and KexiBlobTableEdit.
+ Additionally, the button reacts on pressing space, return, enter,
+ F2, F4 and alt+down buttons. */
+class KEXIGUIUTILS_EXPORT KexiDropDownButton : public QToolButton
+{
+ Q_OBJECT
+
+ public:
+ KexiDropDownButton(QWidget *parent);
+ virtual ~KexiDropDownButton();
+
+ virtual void drawButton( QPainter *p );
+
+ virtual QSize sizeHint () const;
+
+ virtual void keyPressEvent ( QKeyEvent * e );
+};
+
+#endif
diff --git a/kexi/widget/utils/kexiflowlayout.cpp b/kexi/widget/utils/kexiflowlayout.cpp
new file mode 100644
index 000000000..b8a8601e1
--- /dev/null
+++ b/kexi/widget/utils/kexiflowlayout.cpp
@@ -0,0 +1,452 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiflowlayout.h"
+
+#include <kdebug.h>
+
+/// Iterator class
+
+class KexiFlowLayoutIterator : public QGLayoutIterator
+{
+ public:
+ KexiFlowLayoutIterator( QPtrList<QLayoutItem> *list )
+ : m_idx(0), m_list( list )
+ {}
+ uint count() const;
+ QLayoutItem *current();
+ QLayoutItem *next();
+ QLayoutItem *takeCurrent();
+
+ private:
+ int m_idx;
+ QPtrList<QLayoutItem> *m_list;
+};
+
+uint
+KexiFlowLayoutIterator::count() const
+{
+ return m_list->count();
+}
+
+QLayoutItem *
+KexiFlowLayoutIterator::current()
+{
+ return (m_idx < (int)count()) ? m_list->at(m_idx) : 0;
+}
+
+QLayoutItem *
+KexiFlowLayoutIterator::next()
+{
+ m_idx++;
+ return current();
+}
+
+QLayoutItem *
+KexiFlowLayoutIterator::takeCurrent()
+{
+ return (m_idx < (int)count()) ? m_list->take(m_idx) : 0;
+}
+
+//// The layout itself
+
+KexiFlowLayout::KexiFlowLayout(QWidget *parent, int border, int space, const char *name)
+ : QLayout(parent, border, space, name)
+{
+ m_orientation = Horizontal;
+ m_justify = false;
+ m_cached_width = 0;
+}
+
+KexiFlowLayout::KexiFlowLayout(QLayout* parent, int space, const char *name)
+ : QLayout( parent, space, name )
+{
+ m_orientation = Horizontal;
+ m_justify = false;
+ m_cached_width = 0;
+}
+
+KexiFlowLayout::KexiFlowLayout(int space, const char *name)
+ : QLayout(space, name)
+ {
+ m_orientation = Horizontal;
+ m_justify = false;
+ m_cached_width = 0;
+ }
+
+KexiFlowLayout::~KexiFlowLayout()
+{
+ deleteAllItems();
+}
+
+void
+KexiFlowLayout::addItem(QLayoutItem *item)
+{
+ m_list.append(item);
+}
+
+void
+KexiFlowLayout::addSpacing(int size)
+{
+ if (m_orientation == Horizontal)
+ addItem( new QSpacerItem( size, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ) );
+ else
+ addItem( new QSpacerItem( 0, size, QSizePolicy::Minimum, QSizePolicy::Fixed ) );
+}
+
+QLayoutIterator
+KexiFlowLayout::iterator()
+{
+ return QLayoutIterator( new KexiFlowLayoutIterator(&m_list) );
+}
+
+QPtrList<QWidget>*
+KexiFlowLayout::widgetList() const
+{
+ QPtrList<QWidget> *list = new QPtrList<QWidget>();
+ for (QPtrListIterator<QLayoutItem> it(m_list); it.current(); ++it) {
+ if(it.current()->widget())
+ list->append(it.current()->widget());
+ }
+ return list;
+}
+
+void
+KexiFlowLayout::invalidate()
+{
+ QLayout::invalidate();
+ m_cached_sizeHint = QSize();
+ m_cached_minSize = QSize();
+ m_cached_width = 0;
+}
+
+bool
+KexiFlowLayout::isEmpty()
+{
+ return m_list.isEmpty();
+}
+
+bool
+KexiFlowLayout::hasHeightForWidth() const
+{
+ return (m_orientation == Horizontal);
+}
+
+int
+KexiFlowLayout::heightForWidth(int w) const
+{
+ if(m_cached_width != w) {
+ // workaround to allow this method to stay 'const'
+ KexiFlowLayout *mthis = (KexiFlowLayout*)this;
+ int h = mthis->simulateLayout( QRect(0,0,w,0) );
+ mthis->m_cached_hfw = h;
+ mthis->m_cached_width = w;
+ return h;
+ }
+ return m_cached_hfw;
+}
+
+QSize
+KexiFlowLayout::sizeHint() const
+{
+ if(m_cached_sizeHint.isEmpty()) {
+ KexiFlowLayout *mthis = (KexiFlowLayout*)this;
+ QRect r = QRect(0, 0, 2000, 2000);
+ mthis->simulateLayout(r);
+ }
+ return m_cached_sizeHint;
+}
+
+QSize
+KexiFlowLayout::minimumSize() const
+{
+//js: do we really need to simulate layout here?
+// I commented this out because it was impossible to stretch layout conveniently.
+// Now, minimum size is computed automatically based on item's minimumSize...
+#if 0
+ if(m_cached_minSize.isEmpty()) {
+ KexiFlowLayout *mthis = (KexiFlowLayout*)this;
+ QRect r = QRect(0, 0, 2000, 2000);
+ mthis->simulateLayout(r);
+ }
+#endif
+ return m_cached_minSize;
+}
+
+QSizePolicy::ExpandData
+KexiFlowLayout::expanding() const
+{
+ if(m_orientation == Vertical)
+ return QSizePolicy::Vertically;
+ else
+ return QSizePolicy::Horizontally;
+}
+
+void
+KexiFlowLayout::setGeometry(const QRect &r)
+{
+ QLayout::setGeometry(r);
+ if(m_orientation == Horizontal)
+ doHorizontalLayout(r);
+ else
+ doVerticalLayout(r);
+}
+
+int
+KexiFlowLayout::simulateLayout(const QRect &r)
+{
+ if(m_orientation == Horizontal)
+ return doHorizontalLayout(r, true);
+ else
+ return doVerticalLayout(r, true);
+}
+
+int
+KexiFlowLayout::doHorizontalLayout(const QRect &r, bool testOnly)
+{
+ int x = r.x();
+ int y = r.y();
+ int h = 0; // height of this line
+ int availableSpace = r.width() + spacing();
+ int expandingWidgets=0; // number of widgets in the line with QSizePolicy == Expanding
+ QPtrListIterator<QLayoutItem> it(m_list);
+ QPtrList<QLayoutItem> currentLine;
+ QLayoutItem *o;
+ QSize minSize, sizeHint(20, 20);
+ int minSizeHeight = 0 - spacing();
+
+ while ( (o = it.current()) != 0 ) {
+ if(o->isEmpty()) { /// do not consider hidden widgets
+ ++it;
+ continue;
+ }
+
+// kdDebug() << "- doHorizontalLayout(): " << o->widget()->className() << " " << o->widget()->name() << endl;
+ QSize oSizeHint = o->sizeHint(); // we cache these ones because it can take a while to get it (eg for child layouts)
+ if ((x + oSizeHint.width()) > r.right() && h > 0) {
+ // do the layout of current line
+ QPtrListIterator<QLayoutItem> it2(currentLine);
+ QLayoutItem *item;
+ int wx = r.x();
+ int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0;
+ while( (item = it2.current()) != 0 ) {
+ QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
+ QSize itemMinSize = item->minimumSize(); // a while to get them
+ QSize s;
+ if(m_justify) {
+ if(expandingWidgets != 0) {
+ if(item->expanding() == QSizePolicy::Horizontally || item->expanding() == QSizePolicy::BothDirections)
+ s = QSize( QMIN(itemSizeHint.width() + availableSpace / expandingWidgets
+ , r.width()), itemSizeHint.height() );
+ else
+ s = QSize( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
+ }
+ else
+ s = QSize( QMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count()
+ , r.width()), itemSizeHint.height() );
+ }
+ else
+ s = QSize ( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
+ if(!testOnly)
+ item->setGeometry( QRect(QPoint(wx, y), s) );
+ wx = wx + s.width() + spacing();
+ minSizeWidth = minSizeWidth + spacing() + itemMinSize.width();
+ sizeHintWidth = sizeHintWidth + spacing() + itemSizeHint.width();
+ lineMinHeight = QMAX( lineMinHeight, itemMinSize.height() );
+ ++it2;
+ }
+ sizeHint = sizeHint.expandedTo( QSize(sizeHintWidth, 0) );
+ minSize = minSize.expandedTo( QSize(minSizeWidth, 0) );
+ minSizeHeight = minSizeHeight + spacing() + lineMinHeight;
+ // start a new line
+ y = y + spacing() + h;
+ h = 0;
+ x = r.x();
+ currentLine.clear();
+ expandingWidgets = 0;
+ availableSpace = r.width() + spacing();
+ }
+
+ x = x + spacing() + oSizeHint.width();
+ h = QMAX( h, oSizeHint.height() );
+ currentLine.append(o);
+ if(o->expanding() == QSizePolicy::Horizontally || o->expanding() == QSizePolicy::BothDirections)
+ ++expandingWidgets;
+ availableSpace = QMAX(0, availableSpace - spacing() - oSizeHint.width());
+ ++it;
+ }
+
+ // don't forget to layout the last line
+ QPtrListIterator<QLayoutItem> it2(currentLine);
+ QLayoutItem *item;
+ int wx = r.x();
+ int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0;
+ while( (item = it2.current()) != 0 ) {
+ QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
+ QSize itemMinSize = item->minimumSize(); // a while to get them
+ QSize s;
+ if(m_justify) {
+ if(expandingWidgets != 0) {
+ if(item->expanding() == QSizePolicy::Horizontally || item->expanding() == QSizePolicy::BothDirections)
+ s = QSize( QMIN(itemSizeHint.width() + availableSpace / expandingWidgets
+ , r.width()), itemSizeHint.height() );
+ else
+ s = QSize( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
+ }
+ else
+ s = QSize( QMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count()
+ , r.width()), itemSizeHint.height() );
+ }
+ else
+ s = QSize ( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
+ if(!testOnly)
+ item->setGeometry( QRect(QPoint(wx, y), s) );
+ wx = wx + s.width() + spacing();
+ minSizeWidth = minSizeWidth + spacing() + itemMinSize.width();
+ sizeHintWidth = sizeHintWidth + spacing() + itemSizeHint.width();
+ lineMinHeight = QMAX( lineMinHeight, itemMinSize.height() );
+ ++it2;
+ }
+ sizeHint = sizeHint.expandedTo( QSize(sizeHintWidth, y + spacing() + h) );
+ minSizeHeight = minSizeHeight + spacing() + lineMinHeight;
+ minSize = minSize.expandedTo( QSize(minSizeWidth, minSizeHeight) );
+
+ // store sizeHint() and minimumSize()
+ m_cached_sizeHint = sizeHint + QSize(2* margin(), 2*margin());
+ m_cached_minSize = minSize + QSize(2* margin() , 2*margin());
+ // return our height
+ return y + h - r.y();
+}
+
+int
+KexiFlowLayout::doVerticalLayout(const QRect &r, bool testOnly)
+{
+ int x = r.x();
+ int y = r.y();
+ int w = 0; // width of this line
+ int availableSpace = r.height() + spacing();
+ int expandingWidgets=0; // number of widgets in the line with QSizePolicy == Expanding
+ QPtrListIterator<QLayoutItem> it(m_list);
+ QPtrList<QLayoutItem> currentLine;
+ QLayoutItem *o;
+ QSize minSize, sizeHint(20, 20);
+ int minSizeWidth = 0 - spacing();
+
+ while ( (o = it.current()) != 0 ) {
+ if(o->isEmpty()) { /// do not consider hidden widgets
+ ++it;
+ continue;
+ }
+
+ QSize oSizeHint = o->sizeHint(); // we cache these ones because it can take a while to get it (eg for child layouts)
+ if (y + oSizeHint.height() > r.bottom() && w > 0) {
+ // do the layout of current line
+ QPtrListIterator<QLayoutItem> it2(currentLine);
+ QLayoutItem *item;
+ int wy = r.y();
+ int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0;
+ while( (item = it2.current()) != 0 ) {
+ QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
+ QSize itemMinSize = item->minimumSize(); // a while to get them
+ QSize s;
+ if(m_justify) {
+ if(expandingWidgets != 0) {
+ if(item->expanding() == QSizePolicy::Vertically || item->expanding() == QSizePolicy::BothDirections)
+ s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / expandingWidgets
+ , r.height()) );
+ else
+ s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) );
+ }
+ else
+ s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count()
+ , r.height()) );
+ }
+ else
+ s = QSize ( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) );
+ if(!testOnly)
+ item->setGeometry( QRect(QPoint(x, wy), s) );
+ wy = wy + s.height() + spacing();
+ minSizeHeight = minSizeHeight + spacing() + itemMinSize.height();
+ sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height();
+ colMinWidth = QMAX( colMinWidth, itemMinSize.width() );
+ ++it2;
+ }
+ sizeHint = sizeHint.expandedTo( QSize(0, sizeHintHeight) );
+ minSize = minSize.expandedTo( QSize(0, minSizeHeight) );
+ minSizeWidth = minSizeWidth + spacing() + colMinWidth;
+ // start a new column
+ x = x + spacing() + w;
+ w = 0;
+ y = r.y();
+ currentLine.clear();
+ expandingWidgets = 0;
+ availableSpace = r.height() + spacing();
+ }
+
+ y = y + spacing() + oSizeHint.height();
+ w = QMAX( w, oSizeHint.width() );
+ currentLine.append(o);
+ if(o->expanding() == QSizePolicy::Vertically || o->expanding() == QSizePolicy::BothDirections)
+ ++expandingWidgets;
+ availableSpace = QMAX(0, availableSpace - spacing() - oSizeHint.height());
+ ++it;
+ }
+
+ // don't forget to layout the last line
+ QPtrListIterator<QLayoutItem> it2(currentLine);
+ QLayoutItem *item;
+ int wy = r.y();
+ int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0;
+ while( (item = it2.current()) != 0 ) {
+ QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
+ QSize itemMinSize = item->minimumSize(); // a while to get them
+ QSize s;
+ if(m_justify) {
+ if(expandingWidgets != 0) {
+ if(item->expanding() == QSizePolicy::Vertically || item->expanding() == QSizePolicy::BothDirections)
+ s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / expandingWidgets
+ , r.height()) );
+ else
+ s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) );
+ }
+ else
+ s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count()
+ , r.height()) );
+ }
+ else
+ s = QSize ( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) );
+ if(!testOnly)
+ item->setGeometry( QRect(QPoint(x, wy), s) );
+ wy = wy + s.height() + spacing();
+ minSizeHeight = minSizeHeight + spacing() + itemMinSize.height();
+ sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height();
+ colMinWidth = QMAX( colMinWidth, itemMinSize.width() );
+ ++it2;
+ }
+ sizeHint = sizeHint.expandedTo( QSize( x + spacing() + w, sizeHintHeight) );
+ minSizeWidth = minSizeWidth + spacing() + colMinWidth;
+ minSize = minSize.expandedTo( QSize(minSizeWidth, minSizeHeight) );
+
+ // store sizeHint() and minimumSize()
+ m_cached_sizeHint = sizeHint + QSize(2* margin(), 2*margin());
+ m_cached_minSize = minSize + QSize(2* margin(), 2*margin());
+ // return our width
+ return x + w - r.x();
+}
+
diff --git a/kexi/widget/utils/kexiflowlayout.h b/kexi/widget/utils/kexiflowlayout.h
new file mode 100644
index 000000000..173ddad57
--- /dev/null
+++ b/kexi/widget/utils/kexiflowlayout.h
@@ -0,0 +1,79 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIFLOWLAYOUT_H
+#define KEXIFLOWLAYOUT_H
+
+#include <qlayout.h>
+#include <qptrlist.h>
+
+//! @short a special "flow" layout
+class KEXIGUIUTILS_EXPORT KexiFlowLayout : public QLayout
+{
+ public:
+ KexiFlowLayout(QWidget *parent, int border=0, int space=-1, const char *name=0);
+ KexiFlowLayout(QLayout* parent, int space=-1, const char *name=0);
+ KexiFlowLayout(int space=-1, const char *name=0);
+
+ ~KexiFlowLayout();
+
+ /*! \return the widgets in the order of the layout,
+ ie as it is stored in m_list. You must delete the list after using it. */
+ QPtrList<QWidget>* widgetList() const;
+
+ /*! Sets layout's orientation to \a orientation. Default orientation is Vertical. */
+ void setOrientation(Orientation orientation) { m_orientation = orientation; }
+
+ /*! \return layout's orientation. */
+ Qt::Orientation orientation() const { return m_orientation; }
+
+ void setJustified(bool justify) { m_justify = justify; }
+ bool isJustified() const { return m_justify; }
+
+ virtual void addItem(QLayoutItem *item);
+ virtual void addSpacing(int size);
+ virtual QLayoutIterator iterator();
+ virtual void invalidate();
+
+ virtual bool hasHeightForWidth() const;
+ virtual int heightForWidth(int width) const;
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSize() const;
+ virtual QSizePolicy::ExpandData expanding() const;
+
+ virtual bool isEmpty();
+
+ protected:
+ virtual void setGeometry(const QRect&);
+ int simulateLayout(const QRect &r);
+ int doHorizontalLayout(const QRect&, bool testonly = false);
+ int doVerticalLayout(const QRect&, bool testonly = false);
+
+ private:
+ QPtrList<QLayoutItem> m_list;
+ int m_cached_width;
+ int m_cached_hfw;
+ bool m_justify;
+ Orientation m_orientation;
+ QSize m_cached_sizeHint;
+ QSize m_cached_minSize;
+};
+
+#endif
+
diff --git a/kexi/widget/utils/kexigradientwidget.cpp b/kexi/widget/utils/kexigradientwidget.cpp
new file mode 100644
index 000000000..0411318de
--- /dev/null
+++ b/kexi/widget/utils/kexigradientwidget.cpp
@@ -0,0 +1,358 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qimage.h>
+#include <qobjectlist.h>
+#include <qpainter.h>
+#include <qstyle.h>
+
+#include <kimageeffect.h>
+#include <kpixmap.h>
+
+#include "kexigradientwidget.h"
+
+KexiGradientWidget::KexiGradientWidget( QWidget *parent, const char *name, WFlags f )
+ : QWidget( parent, name, f ), p_displayMode( NoGradient ),
+ p_gradientType( VerticalGradient ),
+ p_color1( Qt::white ), p_color2( Qt::blue ), p_currentChild( 0 ),
+ p_opacity( 0.5 ), p_cacheDirty( true )
+{
+ p_customBackgroundWidgets.setAutoDelete( false );
+ p_knownWidgets.setAutoDelete( false );
+
+ p_backgroundColor = QWidget::paletteBackgroundColor();
+
+ connect ( &p_rebuildDelayTimer, SIGNAL( timeout() ), this, SLOT( setCacheDirty() ) );
+
+ installEventFilter( this );
+}
+
+KexiGradientWidget::~KexiGradientWidget()
+{
+}
+
+bool KexiGradientWidget::isValidChildWidget( QObject* child ) {
+ const QWidget* wgt = dynamic_cast<QWidget*>( child );
+
+ if ( wgt == 0L )
+ return false;
+
+ if ( wgt->inherits( "QScrollView" ) )
+ return false;
+ if ( wgt->inherits( "QComboBox" ) )
+ return false;
+ if ( wgt->inherits( "QLineEdit" ) )
+ return false;
+ if ( wgt->inherits( "KexiDBForm" ) )
+ return false;
+
+ return true;
+}
+
+void KexiGradientWidget::buildChildrenList( WidgetList& list, QWidget* p ) {
+ QObjectList* objects = p->queryList( "QWidget", 0, false, false );
+
+ for ( QObjectList::Iterator it = objects->begin(); it != objects->end(); ++it ) {
+ if ( isValidChildWidget( ( *it ) ) == false )
+ continue;
+ list.append( dynamic_cast<QWidget*>( ( *it ) ) );
+ buildChildrenList( list, dynamic_cast<QWidget*>( ( *it ) ) );
+ }
+
+ delete objects;
+}
+
+void KexiGradientWidget::rebuildCache( void ) {
+ WidgetList childWidgetList;
+ buildChildrenList( childWidgetList, this );
+
+ /**
+ Disable the effect and behave like a normal QWidget.
+ */
+ if ( p_displayMode == NoGradient ) {
+// if ( p_backgroundPixmap.isNull() ) {
+ //unsetPalette();
+ //} else {
+ QWidget::setPaletteBackgroundPixmap( p_backgroundPixmap );
+ //}
+ QWidget::setPaletteBackgroundColor( p_backgroundColor );
+
+ for ( WidgetList::Iterator it = childWidgetList.begin();
+ it != childWidgetList.end(); ++it ) {
+
+ if ( p_customBackgroundWidgets.contains( ( *it ) ) == false ) {
+ ( *it )->unsetPalette();
+ }
+ }
+ /**
+ The cache is now in a current state.
+ */
+ p_cacheDirty = false;
+ return;
+ }
+
+ KPixmap tempPixmap;
+ QImage gradientImage;
+ QImage bgImage;
+
+ /**
+ Draw the gradient
+ */
+ gradientImage = KImageEffect::gradient( size(), p_color1, p_color2,
+ (KImageEffect::GradientType)p_gradientType );
+
+ /**
+ Draw the widget-background in a pixmap and fade it with the gradient.
+ */
+ if ( p_displayMode == FadedGradient ) {
+ tempPixmap.resize( size() );
+ QPainter p( &tempPixmap, this );
+
+ if ( p_backgroundPixmap.isNull() ) {
+ /*
+ Need to unset the palette, otherwise the old gradient
+ will be used as a background, not the widget's default bg.
+ */
+ unsetPalette();
+ p.fillRect( 0, 0, width(), height(), palette().brush(
+ isEnabled() ? QPalette::Active : QPalette::Disabled,
+ QColorGroup::Background ) );
+ } else {
+ p.drawTiledPixmap( 0, 0, width(), height(), p_backgroundPixmap );
+ }
+
+ p.end();
+
+ bgImage = tempPixmap;
+
+ KImageEffect::blend( gradientImage, bgImage, (float)p_opacity );
+
+ tempPixmap.convertFromImage( bgImage );
+ } else if ( p_displayMode == SimpleGradient ) {
+ /**
+ Use the gradient as the final background-pixmap
+ if displaymode is set to SimpleGradient.
+ */
+ tempPixmap.convertFromImage( gradientImage );
+ }
+
+ /**
+ All children need to have our background set.
+ */
+ KPixmap partPixmap;
+ QRect area;
+ QWidget* childWidget = 0;
+ const QPoint topLeft( 0, 0 );
+
+ for ( WidgetList::Iterator it = childWidgetList.begin();
+ it != childWidgetList.end(); ++it ) {
+
+ childWidget = ( *it );
+
+ /**
+ Exclude widgets with a custom palette.
+ */
+ if ( p_customBackgroundWidgets.contains( childWidget ) ) {
+ continue;
+ }
+
+ partPixmap.resize( childWidget->size() );
+ /**
+ Get the part of the tempPixmap that is
+ under the current child-widget.
+ */
+ if ( childWidget->parent() == this ) {
+ area = childWidget->geometry();
+ } else {
+ area.setTopLeft( childWidget->mapTo( this,
+ childWidget->clipRegion().boundingRect().topLeft() ) );
+ area.setSize( childWidget->size() );
+ }
+ bitBlt( &partPixmap, topLeft, &tempPixmap, area );
+
+ p_currentChild = childWidget;
+ childWidget->setPaletteBackgroundPixmap( partPixmap );
+ }
+
+ QWidget::setPaletteBackgroundPixmap( tempPixmap );
+ /**
+ Unset the dirty-flag at the end of the method.
+ QWidget::setPaletteBackgroundPixmap() causes this
+ to get set to true again, so set it to false
+ right after setting the pixmap.
+ */
+ p_cacheDirty = false;
+}
+
+void KexiGradientWidget::paintEvent( QPaintEvent* e ) {
+ /**
+ Rebuild the background-pixmap if necessary.
+ */
+ if ( p_cacheDirty == true ) {
+ rebuildCache();
+ }
+
+ /**
+ Draw the widget as usual
+ */
+ QWidget::paintEvent( e );
+}
+
+bool KexiGradientWidget::eventFilter( QObject* object, QEvent* event ) {
+ QWidget* child = dynamic_cast<QWidget*>( object );
+
+ /**
+ Manage list of child-widgets.
+ */
+ if ( object == this ) {
+ if ( event->type() == QEvent::ChildInserted ) {
+ child = dynamic_cast<QWidget*>( dynamic_cast<QChildEvent*>( event )->child() );
+ if ( isValidChildWidget( child ) == false ) {
+ return false;
+ }
+ /**
+ Add the new child-widget to our list of known widgets.
+ */
+ p_knownWidgets.append( child );
+ /**
+ ... and install 'this' as the child's event-filter.
+ */
+ child->installEventFilter( this );
+ } else if ( event->type() == QEvent::ChildRemoved ) {
+ /**
+ Remove the child-widget from the list of known widgets.
+ */
+ p_knownWidgets.remove( dynamic_cast<QWidget*>( dynamic_cast<QChildEvent*>( event )->child() ) );
+ }
+ return false;
+ }
+
+ /**
+ Manage custombackground-list.
+ */
+ if ( event->type() == QEvent::PaletteChange ) {
+ /**
+ p_currentChild will be == 0L, when the user
+ sets it's palette manually.
+ In this case, it has to be added to the customBackground-list.
+ */
+ if ( p_currentChild == 0L && child != 0L ) {
+ if ( p_customBackgroundWidgets.contains( child ) == false ) {
+ p_customBackgroundWidgets.append( child );
+ return false;
+ }
+ }
+ /**
+ Check if the widget whose PaletteChange-event we handle
+ isn't the widget we set the background in rebuildCache().
+ */
+ if ( child != p_currentChild && child != 0L ) {
+ /**
+ Add the new child to the list of widgets, we don't set
+ the background ourselves if it isn't in the list.
+ */
+ if ( p_customBackgroundWidgets.contains( child ) == false ) {
+ if ( child->paletteBackgroundPixmap() != 0L ) {
+ p_customBackgroundWidgets.append( child );
+ }
+ } else {
+ /**
+ If the palette is now the default-palette again,
+ remove it from the "don't set background in rebuildCache()"-list
+ and rebuild the cache, so it again will get the gradient background.
+ */
+ if ( child->paletteBackgroundPixmap() == 0L ) {
+ p_customBackgroundWidgets.remove( child );
+ if ( p_displayMode != NoGradient ) {
+ p_cacheDirty = true;
+ }
+ }
+ }
+ }
+ p_currentChild = 0;
+ }
+
+ if ( event->type() == QEvent::Move ) {
+ if ( p_customBackgroundWidgets.contains( child ) == false ) {
+ updateChildBackground( child );
+ }
+ }
+ return false;
+}
+
+void KexiGradientWidget::updateChildBackground( QWidget* childWidget )
+{
+ KPixmap partPixmap;
+ KPixmap bgPixmap;
+ QRect area;
+ const QPoint topLeft( 0, 0 );
+
+ bgPixmap = paletteBackgroundPixmap() ? (*paletteBackgroundPixmap()) : QPixmap();
+ if ( bgPixmap.isNull() )
+ return;
+
+ /**
+ Exclude widgtes that don't have a parent.
+ This happens when children are removed
+ which are in the knownWidgets-list.
+ */
+ if ( childWidget->parent() == 0L )
+ return;
+
+ /**
+ Exclude widgets with a custom palette.
+ */
+ if ( p_customBackgroundWidgets.contains( childWidget ) ) {
+ return;
+ }
+
+ partPixmap.resize( childWidget->size() );
+ /**
+ Get the part of the tempPixmap that is
+ under the current child-widget.
+ */
+ if ( childWidget->parent() == this ) {
+ area = childWidget->geometry();
+ } else {
+ area.setTopLeft( childWidget->mapTo( this,
+ childWidget->clipRegion().boundingRect().topLeft() ) );
+ area.setSize( childWidget->size() );
+ }
+ bitBlt( &partPixmap, topLeft, &bgPixmap, area );
+
+ p_currentChild = childWidget;
+ childWidget->setPaletteBackgroundPixmap( partPixmap );
+}
+
+void KexiGradientWidget::setPaletteBackgroundColor( const QColor& color )
+{
+ p_backgroundColor = color;
+ if ( p_displayMode == NoGradient ) {
+ QWidget::setPaletteBackgroundColor( p_backgroundColor );
+ }
+}
+
+const QColor& KexiGradientWidget::paletteBackgroundColor() const
+{
+ return p_backgroundColor;
+}
+
+#include "kexigradientwidget.moc"
diff --git a/kexi/widget/utils/kexigradientwidget.h b/kexi/widget/utils/kexigradientwidget.h
new file mode 100644
index 000000000..0032e7b10
--- /dev/null
+++ b/kexi/widget/utils/kexigradientwidget.h
@@ -0,0 +1,247 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIGRADIENTWIDGET_H
+#define KEXIGRADIENTWIDGET_H
+
+#include <qtimer.h>
+#include <qwidget.h>
+
+#include <kimageeffect.h>
+#include <kpixmap.h>
+
+#define REBUILD_DELAY 100
+
+//! @short A simple widget that can use different types of gradients as the background.
+/*!
+ @author Christian Nitschkowski
+*/
+class KEXIGUIUTILS_EXPORT KexiGradientWidget : public QWidget {
+ typedef QPtrList<QWidget> WidgetList;
+
+ Q_OBJECT
+ Q_PROPERTY(DisplayMode displayMode READ displayMode WRITE setDisplayMode DESIGNABLE true)
+ Q_PROPERTY(GradientType gradientType READ gradientType WRITE setGradientType DESIGNABLE true)
+ Q_PROPERTY(QColor gradientColor1 READ gradientColor1 WRITE setGradientColor1 DESIGNABLE true)
+ Q_PROPERTY(QColor gradientColor2 READ gradientColor2 WRITE setGradientColor2 DESIGNABLE true)
+ Q_PROPERTY(double blendOpacity READ blendOpacity WRITE setBlendOpacity DESIGNABLE true)
+ Q_ENUMS( DisplayMode GradientType )
+
+ public:
+ /*!
+ Modes for displaying the gradient.
+ */
+ enum DisplayMode {
+ NoGradient, //!< No gradient at all. Will behave just like a QWidget
+ FadedGradient, //!< Gradient will be faded with the widgets background
+ SimpleGradient //!< Gradient will replace the usual widget background
+ };
+
+ /*!
+ Gradient type specification.
+ See GradientType for more details (part of the KDEFX library)
+ */
+ enum GradientType {
+ VerticalGradient = KImageEffect::VerticalGradient,
+ HorizontalGradient = KImageEffect::HorizontalGradient,
+ DiagonalGradient = KImageEffect::DiagonalGradient,
+ CrossDiagonalGradient = KImageEffect::CrossDiagonalGradient,
+ PyramidGradient = KImageEffect::PyramidGradient,
+ RectangleGradient = KImageEffect::RectangleGradient,
+ PipeCrossGradient = KImageEffect::PipeCrossGradient,
+ EllipticGradient = KImageEffect::EllipticGradient
+ };
+
+ KexiGradientWidget( QWidget *parent = 0, const char *name = 0, WFlags f = 0 );
+
+ virtual ~KexiGradientWidget();
+
+ virtual void setPaletteBackgroundPixmap( const QPixmap& pixmap ) {
+ p_backgroundPixmap = pixmap;
+ p_rebuildDelayTimer.start( REBUILD_DELAY, true );
+ }
+
+ virtual const QColor& paletteBackgroundColor() const;
+
+ /*!
+ Set the displaymode \a mode.
+ The widget will be updated automatically.
+ */
+ void setDisplayMode( DisplayMode mode ) {
+ p_displayMode = mode;
+ p_cacheDirty = true;
+ update();
+ }
+
+ /*!
+ Get the current displaymode.
+ */
+ DisplayMode displayMode() const {
+ return p_displayMode;
+ }
+
+ /*!
+ Set the gradient-type.
+ */
+ void setGradientType( GradientType type ) {
+ p_gradientType = type;
+ p_cacheDirty = true;
+ update();
+ }
+
+ /*!
+ Get the current gradient-type.
+ */
+ GradientType gradientType() const {
+ return p_gradientType;
+ }
+
+ /*! Set color #1 for the gradient-effect.
+ \a color is the new color. */
+ void setGradientColor1( const QColor& color ) {
+ p_color1 = color;
+ p_cacheDirty = true;
+ }
+
+ /*! Set color #2 for the gradient-effect.
+ \a color is the new color. */
+ void setGradientColor2( const QColor& color ) {
+ p_color2 = color;
+ p_cacheDirty = true;
+ }
+
+ /*!
+ Set both colors for the gradient.
+ \a color1 is the first color,
+ \a color2 the second.
+ */
+ void setGradientColors( const QColor& color1, const QColor& color2 ) {
+ p_color1 = color1;
+ p_color2 = color2;
+ p_cacheDirty = true;
+ }
+
+ /*! \return the color #1 used for the gradient. */
+ QColor gradientColor1() const { return p_color1; }
+
+ /*! \return the color #2 used for the gradient. */
+ QColor gradientColor2() const { return p_color2; }
+
+ /*!
+ Sets the opacity of the gradient when fading with background.
+ \a opacity has to be between 0.0 and 1.0.
+ */
+ void setBlendOpacity( double opacity ) {
+ p_opacity = opacity;
+ p_cacheDirty = true;
+ }
+
+ double blendOpacity() const { return p_opacity; }
+
+ public slots:
+ virtual void setPaletteBackgroundColor( const QColor& color );
+
+ protected:
+ virtual bool eventFilter( QObject* object, QEvent* event );
+ virtual void enabledChange( bool enabled ) {
+ p_cacheDirty = true;
+ QWidget::enabledChange( enabled );
+ }
+
+ virtual void paletteChange( const QPalette& pal ) {
+ p_cacheDirty = true;
+ QWidget::paletteChange( pal );
+ }
+
+ virtual void paintEvent( QPaintEvent* e );
+
+ virtual void resizeEvent( QResizeEvent* e ) {
+ p_rebuildDelayTimer.start( REBUILD_DELAY, true );
+ QWidget::resizeEvent( e );
+ }
+
+ virtual void styleChange( QStyle& style ) {
+ p_cacheDirty = true;
+ QWidget::styleChange( style );
+ }
+
+ private:
+ /*!
+ Builds a list of children of \a p.
+ Only widgets that work correctly with KexiGradientWidget
+ will be in this list.
+ The results will be stored in \a list.
+ The method recursively calls itself until all children of \a p
+ have been found and stored in the list.
+ */
+ static void buildChildrenList( WidgetList& list, QWidget* p );
+ /*!
+ \a return if the \a child is a widget that should
+ get a background set.
+ */
+ static bool isValidChildWidget( QObject* child );
+
+ /*!
+ Rebuilds the cache completely.
+ This is done automatically if necessary.
+ */
+ void rebuildCache();
+
+ /*!
+ Sets the background of \a childWidget.
+ This is necessary when the child has been moved.
+ For performance-reasons this is used only for Move-events.
+ The same code is used for PaletteChange-events, but in a
+ different location.
+ */
+ void updateChildBackground( QWidget* childWidget );
+
+ private:
+ WidgetList p_knownWidgets;
+ WidgetList p_customBackgroundWidgets;
+ DisplayMode p_displayMode;
+ GradientType p_gradientType;
+ KPixmap p_backgroundPixmap;
+ QColor p_color1;
+ QColor p_color2;
+ QTimer p_rebuildDelayTimer;
+ QWidget* p_currentChild;
+ double p_opacity;
+ bool p_cacheDirty;
+
+ QColor p_backgroundColor;
+
+ public slots:
+ /*!
+ The cache needs to be rebuild once the widget
+ is set up completely.
+ */
+ virtual void polish() {
+ QWidget::polish();
+ rebuildCache();
+ }
+
+ private slots:
+ void setCacheDirty() {
+ rebuildCache();
+ }
+
+ };
+
+#endif
diff --git a/kexi/widget/utils/kexirecordmarker.cpp b/kexi/widget/utils/kexirecordmarker.cpp
new file mode 100644
index 000000000..d434fcaf6
--- /dev/null
+++ b/kexi/widget/utils/kexirecordmarker.cpp
@@ -0,0 +1,307 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2002 Till Busch <till@bux.at>
+ Copyright (C) 2002 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexirecordmarker.h"
+
+#include <qcolor.h>
+#include <qstyle.h>
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qimage.h>
+#include <qapplication.h>
+
+#include <kdebug.h>
+#include <kstaticdeleter.h>
+
+#include <kexiutils/utils.h>
+
+static KStaticDeleter<QImage> KexiRecordMarker_pen_deleter, KexiRecordMarker_plus_deleter;
+QImage* KexiRecordMarker_pen = 0, *KexiRecordMarker_plus = 0;
+
+static const unsigned char img_pen_data[] = {
+ 0x00,0x00,0x03,0x30,0x78,0x9c,0xfb,0xff,0xff,0x3f,0xc3,0x7f,0x32,0x30,
+ 0x10,0x80,0x88,0xff,0xe4,0xe8,0x85,0xe9,0xc7,0xc6,0x26,0x55,0x3f,0x3a,
+ 0x4d,0x8e,0x7e,0x72,0xfc,0x32,0xd2,0xf5,0xa3,0xeb,0xa5,0xb5,0x7e,0x5c,
+ 0xe9,0x85,0x54,0xfb,0xb1,0xa5,0x1b,0x52,0xdc,0x0e,0x00,0xf2,0xea,0x0a,
+ 0x13
+};
+static const unsigned char img_plus_data[] = {
+ 0x00,0x00,0x01,0x90,0x78,0x9c,0xfb,0xff,0xff,0x3f,0xc3,0x7f,0x28,0x86,
+ 0x82,0xff,0x50,0x0c,0x17,0x47,0xc7,0xd4,0x50,0x87,0x05,0xc0,0xd5,0xe1,
+ 0x10,0xa7,0x16,0x26,0xca,0x5e,0x7c,0xfe,0x20,0x47,0x1d,0xb2,0x5a,0x5c,
+ 0xea,0x40,0x72,0x00,0x03,0x6e,0x74,0x8c
+};
+
+static struct EmbedImage {
+ int width, height, depth;
+ const unsigned char *data;
+ ulong compressed;
+ int numColors;
+ const QRgb *colorTable;
+ bool alpha;
+ const char *name;
+} embed_image[] = {
+ { 17, 12, 32, (const unsigned char*)img_pen_data, 57, 0, 0, true, "tableview_pen.png" },
+ { 10, 10, 32, (const unsigned char*)img_pen_data, 50, 0, 0, true, "tableview_plus.png" }
+};
+
+QImage* getImg(const unsigned char* data, int id)
+{
+ QByteArray baunzip;
+ baunzip = qUncompress( data, embed_image[id].compressed );
+ QImage *img = new QImage( QImage((uchar*)baunzip.data(),
+ embed_image[id].width, embed_image[id].height,
+ embed_image[id].depth, (QRgb*)embed_image[id].colorTable,
+ embed_image[id].numColors, QImage::BigEndian
+ ).copy() );
+ if ( embed_image[id].alpha )
+ img->setAlphaBuffer(true);
+ return img;
+}
+
+static void initRecordMarkerImages()
+{
+ if (!KexiRecordMarker_pen) {
+/*! @warning not reentrant! */
+ KexiRecordMarker_pen_deleter.setObject( KexiRecordMarker_pen, getImg(img_pen_data, 0) );
+ KexiRecordMarker_plus_deleter.setObject( KexiRecordMarker_plus, getImg(img_plus_data, 1) );
+ }
+}
+
+//----------------------------------------------------------------
+
+//! @internal
+class KexiRecordMarker::Private
+{
+public:
+ Private()
+ : rowHeight(1)
+ , offset(0)
+ , currentRow(-1)
+ , highlightedRow(-1)
+ , editRow(-1)
+ , rows(0)
+ , selectionBackgroundColor(qApp->palette().active().highlight())
+ , showInsertRow(true)
+ {
+ }
+ int rowHeight;
+ int offset;
+ int currentRow;
+ int highlightedRow;
+ int editRow;
+ int rows;
+ QColor selectionBackgroundColor;
+ bool showInsertRow : 1;
+};
+
+//----------------------------------------------------------------
+
+KexiRecordMarker::KexiRecordMarker(QWidget *parent, const char* name)
+ : QWidget(parent, name)
+ , d( new Private() )
+{
+ initRecordMarkerImages();
+}
+
+KexiRecordMarker::~KexiRecordMarker()
+{
+ delete d;
+}
+
+QImage* KexiRecordMarker::penImage()
+{
+ initRecordMarkerImages();
+ return KexiRecordMarker_pen;
+}
+
+QImage* KexiRecordMarker::plusImage()
+{
+ initRecordMarkerImages();
+ return KexiRecordMarker_plus;
+}
+
+void KexiRecordMarker::addLabel(bool upd)
+{
+ d->rows++;
+ if (upd)
+ update();
+}
+
+void KexiRecordMarker::removeLabel(bool upd)
+{
+ if (d->rows > 0) {
+ d->rows--;
+ if (upd)
+ update();
+ }
+}
+
+void KexiRecordMarker::addLabels(int num, bool upd)
+{
+ d->rows += num;
+ if (upd)
+ update();
+}
+
+void KexiRecordMarker::clear(bool upd)
+{
+ d->rows=0;
+ if (upd)
+ update();
+}
+
+int KexiRecordMarker::rows() const
+{
+ if (d->showInsertRow)
+ return d->rows +1;
+ else
+ return d->rows;
+}
+
+void KexiRecordMarker::paintEvent(QPaintEvent *e)
+{
+ QPainter p(this);
+ QRect r(e->rect());
+
+ int first = (r.top() + d->offset) / d->rowHeight;
+ int last = (r.bottom() + d->offset) / d->rowHeight;
+ if(last > (d->rows-1+(d->showInsertRow?1:0)))
+ last = d->rows-1+(d->showInsertRow?1:0);
+
+ QColorGroup selectedColorGroup(colorGroup());
+ selectedColorGroup.setColor( QColorGroup::Button,
+ KexiUtils::blendedColors( selectedColorGroup.color(QColorGroup::Background),
+ d->selectionBackgroundColor, 2, 1) );
+ selectedColorGroup.setColor( QColorGroup::Background,
+ selectedColorGroup.color(QColorGroup::Button) ); //set background color as well (e.g. for thinkeramik)
+ QColorGroup highlightedColorGroup(colorGroup());
+ highlightedColorGroup.setColor( QColorGroup::Button,
+ KexiUtils::blendedColors( highlightedColorGroup.color(QColorGroup::Background),
+ d->selectionBackgroundColor, 4, 1) );
+ highlightedColorGroup.setColor( QColorGroup::Background,
+ highlightedColorGroup.color(QColorGroup::Button) ); //set background color as well (e.g. for thinkeramik)
+ for(int i=first; i <= last; i++)
+ {
+ int y = ((d->rowHeight * i)-d->offset);
+ QRect r(0, y, width(), d->rowHeight);
+ p.drawRect(r);
+ style().drawPrimitive( QStyle::PE_HeaderSection, &p, r,
+ (d->currentRow == i) ? selectedColorGroup : (d->highlightedRow == i ? highlightedColorGroup : colorGroup()),
+ QStyle::Style_Raised | (isEnabled() ? QStyle::Style_Enabled : 0));
+ }
+ if (d->editRow!=-1 && d->editRow >= first && d->editRow <= (last/*+1 for insert row*/)) {
+ //show pen when editing
+ int ofs = d->rowHeight / 4;
+ int pos = ((d->rowHeight*(d->currentRow>=0?d->currentRow:0))-d->offset)-ofs/2+1;
+ p.drawImage((d->rowHeight-KexiRecordMarker_pen->width())/2,
+ (d->rowHeight-KexiRecordMarker_pen->height())/2+pos,*KexiRecordMarker_pen);
+ }
+ else if (d->currentRow >= first && d->currentRow <= last
+ && (!d->showInsertRow || (d->showInsertRow && d->currentRow < last)))/*don't display marker for 'insert' row*/
+ {
+ //show marker
+ p.setBrush(colorGroup().foreground());
+ p.setPen(QPen(Qt::NoPen));
+ QPointArray points(3);
+ int ofs = d->rowHeight / 4;
+ int ofs2 = (width() - ofs) / 2 -1;
+ int pos = ((d->rowHeight*d->currentRow)-d->offset)-ofs/2+2;
+ points.putPoints(0, 3, ofs2, pos+ofs, ofs2 + ofs, pos+ofs*2,
+ ofs2,pos+ofs*3);
+ p.drawPolygon(points);
+// kdDebug() <<"KexiRecordMarker::paintEvent(): POLYGON" << endl;
+/* int half = d->rowHeight / 2;
+ points.setPoints(3, 2, pos + 2, width() - 5, pos + half, 2, pos + (2 * half) - 2);*/
+ }
+ if (d->showInsertRow && d->editRow < last
+ && last == (d->rows-1+(d->showInsertRow?1:0)) ) {
+ //show plus sign
+ int pos = ((d->rowHeight*last)-d->offset)+(d->rowHeight-KexiRecordMarker_plus->height())/2;
+// p.drawImage((width()-d->plusImg.width())/2-1, pos, d->plusImg);
+ p.drawImage((width()-KexiRecordMarker_plus->width())/2, pos, *KexiRecordMarker_plus);
+ }
+}
+
+void KexiRecordMarker::setCurrentRow(int row)
+{
+ if (row == d->currentRow)
+ return;
+ int oldRow = d->currentRow;
+ d->currentRow=row;
+
+ if (oldRow != -1)
+ update(0,(d->rowHeight*(oldRow))-d->offset-1, width()+2, d->rowHeight+2);
+ if (d->currentRow != -1)
+ update(0,(d->rowHeight*d->currentRow)-d->offset-1, width()+2, d->rowHeight+2);
+}
+
+void KexiRecordMarker::setHighlightedRow(int row)
+{
+ if (row == d->highlightedRow)
+ return;
+ int oldRow = d->highlightedRow;
+ d->highlightedRow = row;
+
+ if (oldRow != -1)
+ update(0,(d->rowHeight*(oldRow))-d->offset-1, width()+2, d->rowHeight+2);
+ if (d->currentRow != -1)
+ update(0,(d->rowHeight*d->highlightedRow)-d->offset-1, width()+2, d->rowHeight+2);
+}
+
+void KexiRecordMarker::setOffset(int offset)
+{
+ int oldOff = d->offset;
+ d->offset = offset;
+ scroll(0,oldOff-offset);
+}
+
+void KexiRecordMarker::setCellHeight(int cellHeight)
+{
+ d->rowHeight = cellHeight;
+}
+
+void KexiRecordMarker::setEditRow(int row)
+{
+ d->editRow = row;
+//TODO: update only needed area!
+ update();
+}
+
+void KexiRecordMarker::showInsertRow(bool show)
+{
+ d->showInsertRow = show;
+//TODO: update only needed area!
+ update();
+}
+
+void KexiRecordMarker::setSelectionBackgroundColor(const QColor &color)
+{
+ d->selectionBackgroundColor = color;
+}
+
+QColor KexiRecordMarker::selectionBackgroundColor() const
+{
+ return d->selectionBackgroundColor;
+}
+
+#include "kexirecordmarker.moc"
diff --git a/kexi/widget/utils/kexirecordmarker.h b/kexi/widget/utils/kexirecordmarker.h
new file mode 100644
index 000000000..1408f83b9
--- /dev/null
+++ b/kexi/widget/utils/kexirecordmarker.h
@@ -0,0 +1,72 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2002 Till Busch <till@bux.at>
+ Copyright (C) 2002 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIRECORDMARKER_H
+#define KEXIRECORDMARKER_H
+
+#include <qwidget.h>
+
+class QImage;
+
+//! \brief Record marker, usually displayed at the left side of a table view or a continuous form.
+class KEXIGUIUTILS_EXPORT KexiRecordMarker : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KexiRecordMarker(QWidget *parent, const char* name = 0);
+ ~KexiRecordMarker();
+
+ int rows() const;
+
+ static QImage* penImage();
+ static QImage* plusImage();
+
+ public slots:
+ void setOffset(int offset);
+ void setCellHeight(int cellHeight);
+ void setCurrentRow(int row);
+ void setHighlightedRow(int row);
+
+ /*! Sets 'edit row' flag for \a row. Use row==-1 if you want to switch the flag off. */
+ void setEditRow(int row);
+ void showInsertRow(bool show);
+
+ QColor selectionBackgroundColor() const;
+ void setSelectionBackgroundColor(const QColor &color);
+
+ void addLabel(bool upd=true);
+ void removeLabel(bool upd=true);
+
+ /*! Adds \a num labels */
+ void addLabels(int num, bool upd=true);
+
+ void clear(bool upd=true);
+
+ protected:
+ virtual void paintEvent(QPaintEvent *e);
+
+ class Private;
+ Private * const d;
+};
+
+#endif
diff --git a/kexi/widget/utils/kexirecordnavigator.cpp b/kexi/widget/utils/kexirecordnavigator.cpp
new file mode 100644
index 000000000..f0dff0878
--- /dev/null
+++ b/kexi/widget/utils/kexirecordnavigator.cpp
@@ -0,0 +1,511 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qtoolbutton.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qvalidator.h>
+#include <qtooltip.h>
+#include <qscrollview.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <kguiitem.h>
+#include <kstaticdeleter.h>
+
+#include "kexirecordnavigator.h"
+#include "kexirecordmarker.h"
+
+//! @internal
+class KexiRecordNavigatorPrivate
+{
+ public:
+ KexiRecordNavigatorPrivate()
+ : handler(0)
+ , editingIndicatorLabel(0)
+ , editingIndicatorEnabled(false)
+ , editingIndicatorVisible(false)
+ {
+ }
+ KexiRecordNavigatorHandler *handler;
+ QHBoxLayout *lyr;
+
+ QLabel *editingIndicatorLabel;
+ bool editingIndicatorEnabled : 1;
+ bool editingIndicatorVisible : 1;
+};
+
+//--------------------------------------------------
+
+KexiRecordNavigatorHandler::KexiRecordNavigatorHandler()
+{
+}
+
+KexiRecordNavigatorHandler::~KexiRecordNavigatorHandler()
+{
+}
+
+//--------------------------------------------------
+
+KexiRecordNavigator::KexiRecordNavigator(QWidget *parent, int leftMargin, const char *name)
+ : QFrame(parent, name)
+ , m_view(0)
+ , m_isInsertingEnabled(true)
+ , d( new KexiRecordNavigatorPrivate() )
+{
+ if (parent->inherits("QScrollView"))
+ setParentView( dynamic_cast<QScrollView*>(parent) );
+ setFrameStyle(QFrame::NoFrame);
+ d->lyr = new QHBoxLayout(this,0,0,"nav_lyr");
+
+ m_textLabel = new QLabel(this);
+ d->lyr->addWidget( m_textLabel );
+ setLabelText(i18n("Row:"));
+
+ int bw = 6+SmallIcon("navigator_first").width(); //QMIN( horizontalScrollBar()->height(), 20);
+ QFont f = font();
+ f.setPixelSize((bw > 12) ? 12 : bw);
+ QFontMetrics fm(f);
+ m_nav1DigitWidth = fm.width("8");
+
+ d->lyr->addWidget( m_navBtnFirst = new QToolButton(this) );
+ m_navBtnFirst->setFixedWidth(bw);
+ m_navBtnFirst->setFocusPolicy(NoFocus);
+ m_navBtnFirst->setIconSet( SmallIconSet("navigator_first") );
+ QToolTip::add(m_navBtnFirst, i18n("First row"));
+
+ d->lyr->addWidget( m_navBtnPrev = new QToolButton(this) );
+ m_navBtnPrev->setFixedWidth(bw);
+ m_navBtnPrev->setFocusPolicy(NoFocus);
+ m_navBtnPrev->setIconSet( SmallIconSet("navigator_prev") );
+ m_navBtnPrev->setAutoRepeat(true);
+ QToolTip::add(m_navBtnPrev, i18n("Previous row"));
+
+ d->lyr->addSpacing( 6 );
+
+ d->lyr->addWidget( m_navRecordNumber = new KLineEdit(this) );
+ m_navRecordNumber->setAlignment(AlignRight | AlignVCenter);
+ m_navRecordNumber->setFocusPolicy(ClickFocus);
+ m_navRecordNumber->installEventFilter(this);
+// m_navRowNumber->setFixedWidth(fw);
+ m_navRecordNumberValidator = new QIntValidator(1, INT_MAX, this);
+ m_navRecordNumber->setValidator(m_navRecordNumberValidator);
+ m_navRecordNumber->installEventFilter(this);
+ QToolTip::add(m_navRecordNumber, i18n("Current row number"));
+
+ KLineEdit *lbl_of = new KLineEdit(i18n("of"), this);
+ lbl_of->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Preferred);
+ lbl_of->setMaximumWidth(fm.width(lbl_of->text())+8);
+ lbl_of->setReadOnly(true);
+ lbl_of->setLineWidth(0);
+ lbl_of->setFocusPolicy(NoFocus);
+ lbl_of->setAlignment(AlignCenter);
+ d->lyr->addWidget( lbl_of );
+
+ d->lyr->addWidget( m_navRecordCount = new KLineEdit(this) );
+ m_navRecordCount->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Preferred);
+ m_navRecordCount->setReadOnly(true);
+ m_navRecordCount->setLineWidth(0);
+ m_navRecordCount->setFocusPolicy(NoFocus);
+ m_navRecordCount->setAlignment(AlignLeft | AlignVCenter);
+ QToolTip::add(m_navRecordCount, i18n("Number of rows"));
+
+ lbl_of->setFont(f);
+ m_navRecordNumber->setFont(f);
+ m_navRecordCount->setFont(f);
+ setFont(f);
+
+ d->lyr->addWidget( m_navBtnNext = new QToolButton(this) );
+ m_navBtnNext->setFixedWidth(bw);
+ m_navBtnNext->setFocusPolicy(NoFocus);
+ m_navBtnNext->setIconSet( SmallIconSet("navigator_next") );
+ m_navBtnNext->setAutoRepeat(true);
+ QToolTip::add(m_navBtnNext, i18n("Next row"));
+
+ d->lyr->addWidget( m_navBtnLast = new QToolButton(this) );
+ m_navBtnLast->setFixedWidth(bw);
+ m_navBtnLast->setFocusPolicy(NoFocus);
+ m_navBtnLast->setIconSet( SmallIconSet("navigator_last") );
+ QToolTip::add(m_navBtnLast, i18n("Last row"));
+
+ d->lyr->addSpacing( 6 );
+ d->lyr->addWidget( m_navBtnNew = new QToolButton(this) );
+ m_navBtnNew->setFixedWidth(bw);
+ m_navBtnNew->setFocusPolicy(NoFocus);
+ m_navBtnNew->setIconSet( SmallIconSet("navigator_new") );
+ QToolTip::add(m_navBtnNew, i18n("New row"));
+ m_navBtnNext->setEnabled(isInsertingEnabled());
+
+ d->lyr->addSpacing( 6 );
+ d->lyr->addStretch(10);
+
+ connect(m_navBtnPrev,SIGNAL(clicked()),this,SLOT(slotPrevButtonClicked()));
+ connect(m_navBtnNext,SIGNAL(clicked()),this,SLOT(slotNextButtonClicked()));
+ connect(m_navBtnLast,SIGNAL(clicked()),this,SLOT(slotLastButtonClicked()));
+ connect(m_navBtnFirst,SIGNAL(clicked()),this,SLOT(slotFirstButtonClicked()));
+ connect(m_navBtnNew,SIGNAL(clicked()),this,SLOT(slotNewButtonClicked()));
+
+ setRecordCount(0);
+ setCurrentRecordNumber(0);
+
+ updateGeometry(leftMargin);
+}
+
+KexiRecordNavigator::~KexiRecordNavigator()
+{
+ delete d;
+}
+
+void KexiRecordNavigator::setInsertingEnabled(bool set)
+{
+ if (m_isInsertingEnabled==set)
+ return;
+ m_isInsertingEnabled = set;
+ if (isEnabled())
+ m_navBtnNew->setEnabled( m_isInsertingEnabled );
+}
+
+void KexiRecordNavigator::setEnabled( bool set )
+{
+ QFrame::setEnabled(set);
+ if (set && !m_isInsertingEnabled)
+ m_navBtnNew->setEnabled( false );
+}
+
+bool KexiRecordNavigator::eventFilter( QObject *o, QEvent *e )
+{
+ if (o==m_navRecordNumber) {
+ bool recordEntered = false;
+ bool ret;
+ if (e->type()==QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ switch (ke->key()) {
+ case Qt::Key_Escape: {
+ ke->accept();
+ m_navRecordNumber->undo();
+ if (m_view)
+ m_view->setFocus();
+ return true;
+ }
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_Tab:
+ case Qt::Key_BackTab:
+ {
+ recordEntered=true;
+ ke->accept(); //to avoid pressing Enter later
+ ret = true;
+ }
+ default:;
+ }
+ }
+ else if (e->type()==QEvent::FocusOut) {
+ if (static_cast<QFocusEvent*>(e)->reason()!=QFocusEvent::Tab
+ && static_cast<QFocusEvent*>(e)->reason()!=QFocusEvent::Backtab
+ && static_cast<QFocusEvent*>(e)->reason()!=QFocusEvent::Other)
+ recordEntered=true;
+ ret = false;
+ }
+
+ if (recordEntered) {
+ bool ok=true;
+ uint r = m_navRecordNumber->text().toUInt(&ok);
+ if (!ok || r<1)
+ r = (recordCount()>0)?1:0;
+ if (m_view && (hasFocus() || e->type()==QEvent::KeyPress))
+ m_view->setFocus();
+ setCurrentRecordNumber(r);
+ emit recordNumberEntered(r);
+ if (d->handler)
+ d->handler->moveToRecordRequested(r-1);
+ return ret;
+ }
+ }
+/*
+ bool ok=true;
+ int r = text.toInt(&ok);
+ if (!ok || r<1)
+ r = 1;
+ emit recordNumberEntered(r);*/
+ return false;
+}
+
+void KexiRecordNavigator::setCurrentRecordNumber(uint r)
+{
+ uint recCnt = recordCount();
+ if (r>(recCnt+(m_isInsertingEnabled?1:0)))
+ r = recCnt+(m_isInsertingEnabled?1:0);
+ QString n;
+ if (r>0)
+ n = QString::number(r);
+ else
+ n = " ";
+// if (d->navRecordNumber->text().length() != n.length()) {//resize
+// d->navRecordNumber->setFixedWidth(
+// d->nav1DigitWidth*QMAX( QMAX(n.length(),2)+1,d->navRecordCount->text().length()+1)+6
+// );
+// }
+
+ m_navRecordNumber->setText(n);
+ m_navRecordCount->deselect();
+ updateButtons(recCnt);
+}
+
+void KexiRecordNavigator::updateButtons(uint recCnt)
+{
+ const uint r = currentRecordNumber();
+ if (isEnabled()) {
+ m_navBtnPrev->setEnabled(r > 1);
+ m_navBtnFirst->setEnabled(r > 1);
+ m_navBtnNext->setEnabled(r > 0
+ && r < (recCnt +(m_isInsertingEnabled?(1+d->editingIndicatorVisible/*if we're editing, next btn is avail.*/):0) ) );
+ m_navBtnLast->setEnabled(r!=(recCnt+(m_isInsertingEnabled?1:0)) && (m_isInsertingEnabled || recCnt>0));
+ }
+}
+
+void KexiRecordNavigator::setRecordCount(uint count)
+{
+ const QString & n = QString::number(count);
+ if (m_isInsertingEnabled && currentRecordNumber()==0) {
+ setCurrentRecordNumber(1);
+ }
+ if (m_navRecordCount->text().length() != n.length()) {//resize
+ m_navRecordCount->setFixedWidth(m_nav1DigitWidth*n.length()+6);
+
+ if (m_view && m_view->horizontalScrollBar()->isVisible()) {
+ //+width of the delta
+ resize(width()+(n.length()-m_navRecordCount->text().length())*m_nav1DigitWidth, height());
+// horizontalScrollBar()->move(d->navPanel->x()+d->navPanel->width()+20,horizontalScrollBar()->y());
+ }
+ }
+ //update row number widget's width
+ const int w = m_nav1DigitWidth*QMAX( QMAX(n.length(),2)+1,m_navRecordNumber->text().length()+1)+6;
+ if (m_navRecordNumber->width()!=w) //resize
+ m_navRecordNumber->setFixedWidth(w);
+
+ m_navRecordCount->setText(n);
+ m_navRecordCount->deselect();
+ if (m_view)
+ m_view->updateScrollBars();
+ updateButtons(recordCount());
+}
+
+uint KexiRecordNavigator::currentRecordNumber() const
+{
+ bool ok=true;
+ int r = m_navRecordNumber->text().toInt(&ok);
+ if (!ok || r<1)
+ r = 0;
+ return r;
+}
+
+uint KexiRecordNavigator::recordCount() const
+{
+ bool ok=true;
+ int r = m_navRecordCount->text().toInt(&ok);
+ if (!ok || r<1)
+ r = 0;
+ return r;
+}
+
+void KexiRecordNavigator::setParentView(QScrollView *view)
+{
+ m_view = view;
+}
+
+void KexiRecordNavigator::updateGeometry(int leftMargin)
+{
+ QFrame::updateGeometry();
+ if (m_view) {
+ int navWidth;
+ if (m_view->horizontalScrollBar()->isVisible()) {
+ navWidth = sizeHint().width();
+ }
+ else {
+ navWidth = leftMargin + m_view->clipper()->width();
+ }
+
+ setGeometry(
+ m_view->frameWidth(),
+ m_view->height() - m_view->horizontalScrollBar()->sizeHint().height()-m_view->frameWidth(),
+ navWidth,
+ m_view->horizontalScrollBar()->sizeHint().height()
+ );
+
+ m_view->updateScrollBars();
+ }
+}
+
+void KexiRecordNavigator::setHBarGeometry( QScrollBar & hbar, int x, int y, int w, int h )
+{
+ hbar.setGeometry( x + width(), y, w - width(), h );
+}
+
+void KexiRecordNavigator::setLabelText(const QString& text)
+{
+ m_textLabel->setText(text.isEmpty() ? QString::null : (QString::fromLatin1(" ")+text+" "));
+}
+
+void KexiRecordNavigator::setInsertingButtonVisible(bool set)
+{
+ if (set)
+ m_navBtnNew->show();
+ else
+ m_navBtnNew->hide();
+}
+
+void KexiRecordNavigator::slotPrevButtonClicked()
+{
+ emit prevButtonClicked();
+ if (d->handler)
+ d->handler->moveToPreviousRecordRequested();
+}
+
+void KexiRecordNavigator::slotNextButtonClicked()
+{
+ emit nextButtonClicked();
+ if (d->handler)
+ d->handler->moveToNextRecordRequested();
+}
+
+void KexiRecordNavigator::slotLastButtonClicked()
+{
+ emit lastButtonClicked();
+ if (d->handler)
+ d->handler->moveToLastRecordRequested();
+}
+
+void KexiRecordNavigator::slotFirstButtonClicked()
+{
+ emit firstButtonClicked();
+ if (d->handler)
+ d->handler->moveToFirstRecordRequested();
+}
+
+void KexiRecordNavigator::slotNewButtonClicked()
+{
+ emit newButtonClicked();
+ if (d->handler)
+ d->handler->addNewRecordRequested();
+}
+
+
+void KexiRecordNavigator::setRecordHandler(KexiRecordNavigatorHandler *handler)
+{
+ d->handler = handler;
+}
+
+bool KexiRecordNavigator::editingIndicatorVisible() const
+{
+ return d->editingIndicatorVisible;
+}
+
+bool KexiRecordNavigator::editingIndicatorEnabled() const
+{
+ return d->editingIndicatorEnabled;
+}
+
+void KexiRecordNavigator::setEditingIndicatorEnabled(bool set)
+{
+ d->editingIndicatorEnabled = set;
+ if (d->editingIndicatorEnabled) {
+ if (!d->editingIndicatorLabel) {
+ d->editingIndicatorLabel = new QLabel(this);
+ d->editingIndicatorLabel->setAlignment(Qt::AlignCenter);
+ QPixmap pix;
+ pix.convertFromImage( *KexiRecordMarker::penImage() );
+ d->editingIndicatorLabel->setFixedWidth( pix.width() + 2*2 );
+ d->lyr->insertWidget( 0, d->editingIndicatorLabel );
+ }
+ d->editingIndicatorLabel->show();
+ }
+ else {
+ if (d->editingIndicatorLabel) {
+ d->editingIndicatorLabel->hide();
+ }
+ }
+}
+
+void KexiRecordNavigator::showEditingIndicator(bool show)
+{
+ d->editingIndicatorVisible = show;
+ updateButtons(recordCount()); //this will refresh 'next btn'
+ if (!d->editingIndicatorEnabled)
+ return;
+ if (d->editingIndicatorVisible) {
+ QPixmap pix;
+ pix.convertFromImage( *KexiRecordMarker::penImage() );
+ d->editingIndicatorLabel->setPixmap( pix );
+ QToolTip::add( d->editingIndicatorLabel, i18n("Editing indicator") );
+ }
+ else {
+ d->editingIndicatorLabel->setPixmap( QPixmap() );
+ QToolTip::remove( d->editingIndicatorLabel );
+ }
+}
+
+//------------------------------------------------
+
+//! @internal
+class KexiRecordNavigatorActionsInternal {
+ public:
+ KexiRecordNavigatorActionsInternal()
+ : moveToFirstRecord(i18n("First row"), "navigator_first", i18n("Go to first row"))
+ , moveToPreviousRecord(i18n("Previous row"), "navigator_prev", i18n("Go to previous row"))
+ , moveToNextRecord(i18n("Next row"), "navigator_next", i18n("Go to next row"))
+ , moveToLastRecord(i18n("Last row"), "navigator_last", i18n("Go to last row"))
+ , moveToNewRecord(i18n("New row"), "navigator_new", i18n("Go to new row"))
+ {
+ }
+ static void init();
+ KGuiItem moveToFirstRecord;
+ KGuiItem moveToPreviousRecord;
+ KGuiItem moveToNextRecord;
+ KGuiItem moveToLastRecord;
+ KGuiItem moveToNewRecord;
+};
+
+static KStaticDeleter<KexiRecordNavigatorActionsInternal> KexiRecordNavigatorActions_deleter;
+KexiRecordNavigatorActionsInternal* KexiRecordNavigatorActions_internal = 0;
+
+void KexiRecordNavigatorActionsInternal::init()
+{
+ if (!KexiRecordNavigatorActions_internal)
+ KexiRecordNavigatorActions_deleter.setObject(KexiRecordNavigatorActions_internal,
+ new KexiRecordNavigatorActionsInternal());
+}
+
+const KGuiItem& KexiRecordNavigator::Actions::moveToFirstRecord()
+{ KexiRecordNavigatorActionsInternal::init(); return KexiRecordNavigatorActions_internal->moveToFirstRecord; }
+
+const KGuiItem& KexiRecordNavigator::Actions::moveToPreviousRecord()
+{ KexiRecordNavigatorActionsInternal::init(); return KexiRecordNavigatorActions_internal->moveToPreviousRecord; }
+
+const KGuiItem& KexiRecordNavigator::Actions::moveToNextRecord()
+{ KexiRecordNavigatorActionsInternal::init(); return KexiRecordNavigatorActions_internal->moveToNextRecord; }
+
+const KGuiItem& KexiRecordNavigator::Actions::moveToLastRecord()
+{ KexiRecordNavigatorActionsInternal::init(); return KexiRecordNavigatorActions_internal->moveToLastRecord; }
+
+const KGuiItem& KexiRecordNavigator::Actions::moveToNewRecord()
+{ KexiRecordNavigatorActionsInternal::init(); return KexiRecordNavigatorActions_internal->moveToNewRecord; }
+
+#include "kexirecordnavigator.moc"
diff --git a/kexi/widget/utils/kexirecordnavigator.h b/kexi/widget/utils/kexirecordnavigator.h
new file mode 100644
index 000000000..674746e22
--- /dev/null
+++ b/kexi/widget/utils/kexirecordnavigator.h
@@ -0,0 +1,190 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIRECORDNAVIGATOR_H
+#define KEXIRECORDNAVIGATOR_H
+
+#include <qframe.h>
+#include <kexi_export.h>
+
+class QToolButton;
+class QIntValidator;
+class QScrollView;
+class QScrollBar;
+class QLabel;
+class KLineEdit;
+class KGuiItem;
+class KexiRecordNavigatorPrivate;
+
+//! \brief KexiRecordNavigatorHandler interface handles requests generated by KexiRecordNavigator
+class KEXIGUIUTILS_EXPORT KexiRecordNavigatorHandler
+{
+ public:
+ KexiRecordNavigatorHandler();
+ virtual ~KexiRecordNavigatorHandler();
+
+ //! Moving to record \a r is requested. Records are counted from 0.
+ virtual void moveToRecordRequested(uint r) = 0;
+ virtual void moveToLastRecordRequested() = 0;
+ virtual void moveToPreviousRecordRequested() = 0;
+ virtual void moveToNextRecordRequested() = 0;
+ virtual void moveToFirstRecordRequested() = 0;
+ virtual void addNewRecordRequested() = 0;
+};
+
+
+//! \brief KexiRecordNavigator class provides a record navigator.
+/*! Record navigator is usually used for data tables (e.g. KexiTableView)
+ or data-aware forms.
+
+ You can plug KexiRecordNavigator object to your data-aware object in two ways:
+ 1) By connectiong to slots prevButtonClicked(), etc.
+ 2) A bit cleaner way: by inheriting from KexiRecordNavigatorHandler interface
+ in your data-aware class and implementing all it's prototype methods like
+ moveToRecordRequested(), and then caling setRecordHandler() on navigator's object.
+ Note that using this way, you can allow to exist more than one navigator widget
+ connected with your data-aware object (don't matter if this is sane).
+ */
+class KEXIGUIUTILS_EXPORT KexiRecordNavigator : public QFrame
+{
+ Q_OBJECT
+
+ public:
+ KexiRecordNavigator(QWidget *parent, int leftMargin = 0, const char *name=0);
+ virtual ~KexiRecordNavigator();
+
+ void setParentView(QScrollView *view);
+
+ /*! Sets record navigator handler. This allows to react
+ on actions performed within navigator and vice versa. */
+ void setRecordHandler(KexiRecordNavigatorHandler *handler);
+
+ /*! \return true if data inserting is enabled (the default). */
+ inline bool isInsertingEnabled() const { return m_isInsertingEnabled; }
+
+ /*! \return current record number displayed for this navigator.
+ can return 0, if the 'text box's content is cleared. */
+ uint currentRecordNumber() const;
+
+ /*! \return record count displayed for this navigator. */
+ uint recordCount() const;
+
+ /*! Sets horizontal bar's \a hbar (at the bottom) geometry so this record navigator
+ is properly positioned together with horizontal scroll bar. This method is used
+ in QScrollView::setHBarGeometry() implementations:
+ see KexiTableView::setHBarGeometry() and KexiFormScrollView::setHBarGeometry()
+ for usage examples. */
+ void setHBarGeometry( QScrollBar & hbar, int x, int y, int w, int h );
+
+ /*! @internal used for keyboard handling. */
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ /*! \return true if "editing" indicator is visible for this navigator.
+ @see showEditingIndicator() */
+ bool editingIndicatorVisible() const;
+
+ /*! \return true if "editing" indicator is enabled for this navigator.
+ Only meaningful if setEditingIndicatorEnabled(true) is called. */
+ bool editingIndicatorEnabled() const;
+
+ //! @short A set of GUI items usable for displaying related actions.
+ /*! For instance, the items are used by Kexi main window to create shared actions. */
+ class KEXIGUIUTILS_EXPORT Actions {
+ public:
+ static const KGuiItem& moveToFirstRecord();
+ static const KGuiItem& moveToPreviousRecord();
+ static const KGuiItem& moveToNextRecord();
+ static const KGuiItem& moveToLastRecord();
+ static const KGuiItem& moveToNewRecord();
+ };
+
+ public slots:
+ /*! Sets insertingEnabled flag. If true, "+" button will be enabled. */
+ void setInsertingEnabled(bool set);
+
+ /*! Sets visibility of "inserting" button. */
+ void setInsertingButtonVisible(bool set);
+
+ /*! Sets visibility of the place where "editing" indicator will be displayed.
+ "editing" indicator will display KexiRecordMarker::penImage() image when
+ setEditingIndicatorVisible() is called.
+ This method is currently used e.g. within standard kexi forms
+ (see KexiFormScrollView class). */
+ void setEditingIndicatorEnabled(bool set);
+
+ /*! Shows or hides "editing" indicator. */
+ void showEditingIndicator(bool show);
+
+ virtual void setEnabled(bool set);
+
+ /*! Sets current record number for this navigator,
+ i.e. a value that will be displayed in the 'record number' text box.
+ This can also affect button's enabling and disabling.
+ If @p r is 0, 'record number' text box's content is cleared. */
+ void setCurrentRecordNumber(uint r);
+
+ /*! Sets record count for this navigator.
+ This can also affect button's enabling and disabling.
+ By default count is 0. */
+ void setRecordCount(uint count);
+
+ void updateGeometry(int leftMargin);
+
+ /*! Sets label text at the left of the for record navigator's button.
+ By default this label contains translated "Row:" text. */
+ void setLabelText(const QString& text);
+
+ signals:
+ void prevButtonClicked();
+ void nextButtonClicked();
+ void lastButtonClicked();
+ void firstButtonClicked();
+ void newButtonClicked();
+ void recordNumberEntered( uint r );
+
+ protected slots:
+ void slotPrevButtonClicked();
+ void slotNextButtonClicked();
+ void slotLastButtonClicked();
+ void slotFirstButtonClicked();
+ void slotNewButtonClicked();
+ //void slotRecordNumberReturnPressed(const QString& text);
+
+ protected:
+ void updateButtons(uint recCnt);
+
+ QLabel *m_textLabel;
+ QToolButton *m_navBtnFirst;
+ QToolButton *m_navBtnPrev;
+ QToolButton *m_navBtnNext;
+ QToolButton *m_navBtnLast;
+ QToolButton *m_navBtnNew;
+ KLineEdit *m_navRecordNumber;
+ QIntValidator *m_navRecordNumberValidator;
+ KLineEdit *m_navRecordCount; //!< readonly counter
+ uint m_nav1DigitWidth;
+// uint m_recordCount;
+ QScrollView *m_view;
+ bool m_isInsertingEnabled : 1;
+
+ KexiRecordNavigatorPrivate *d;
+};
+
+#endif
diff --git a/kexi/widget/utils/kexisharedactionclient.cpp b/kexi/widget/utils/kexisharedactionclient.cpp
new file mode 100644
index 000000000..4dbd9299d
--- /dev/null
+++ b/kexi/widget/utils/kexisharedactionclient.cpp
@@ -0,0 +1,39 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexisharedactionclient.h"
+
+#include <kaction.h>
+
+KexiSharedActionClient::KexiSharedActionClient()
+ : m_sharedActions(101, false)
+{
+}
+
+KexiSharedActionClient::~KexiSharedActionClient()
+{
+}
+
+void KexiSharedActionClient::plugSharedAction(KAction* a)
+{
+ if (!a)
+ return;
+ m_sharedActions.insert(a->name(), a);
+}
+
diff --git a/kexi/widget/utils/kexisharedactionclient.h b/kexi/widget/utils/kexisharedactionclient.h
new file mode 100644
index 000000000..80181bc7e
--- /dev/null
+++ b/kexi/widget/utils/kexisharedactionclient.h
@@ -0,0 +1,49 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXISHAREDACTIONCLIENT_H
+#define KEXISHAREDACTIONCLIENT_H
+
+#include <qasciidict.h>
+
+class KAction;
+#include <kexi_export.h>
+
+//! The KexiSharedActionClient is an interface using application-wide (shared) actions.
+/** See KexiTableView and KexiFormScrollView for example usage.
+*/
+class KEXIGUIUTILS_EXPORT KexiSharedActionClient
+{
+ public:
+ KexiSharedActionClient();
+ virtual ~KexiSharedActionClient();
+
+ /*! Plugs action \a a for a widget. The action will be later looked up (by name)
+ on key press event, to get proper shortcut. If found, we know that the action is already
+ performed at main window's level, so we should give up. Otherwise - default shortcut
+ will be used (example: Shift+Enter key for "data_save_row" action). \sa KexiTableView::shortCutPressed()
+ */
+ void plugSharedAction(KAction* a);
+
+ protected:
+ //! Actions pluged for this widget using plugSharedAction(), available by name.
+ QAsciiDict<KAction> m_sharedActions;
+};
+
+#endif
diff --git a/kexi/widget/utils/kexitooltip.cpp b/kexi/widget/utils/kexitooltip.cpp
new file mode 100644
index 000000000..69a8b583c
--- /dev/null
+++ b/kexi/widget/utils/kexitooltip.cpp
@@ -0,0 +1,76 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexitooltip.h"
+
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qimage.h>
+#include <qtooltip.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qtimer.h>
+
+
+KexiToolTip::KexiToolTip(const QVariant& value, QWidget* parent)
+ : QWidget(parent, "KexiToolTip", Qt::WStyle_Customize | Qt::WType_Popup | Qt::WStyle_NoBorder
+ | Qt::WX11BypassWM | Qt::WDestructiveClose)
+ , m_value(value)
+{
+ setPalette( QToolTip::palette() );
+ setFocusPolicy(QWidget::NoFocus);
+}
+
+KexiToolTip::~KexiToolTip()
+{
+}
+
+QSize KexiToolTip::sizeHint() const
+{
+ QSize sz(fontMetrics().boundingRect(m_value.toString()).size());
+ return sz;
+}
+
+void KexiToolTip::show()
+{
+ updateGeometry();
+ QWidget::show();
+}
+
+void KexiToolTip::paintEvent( QPaintEvent *pev )
+{
+ QWidget::paintEvent(pev);
+ QPainter p(this);
+ drawFrame(p);
+ drawContents(p);
+}
+
+void KexiToolTip::drawFrame(QPainter& p)
+{
+ p.setPen( QPen(palette().active().foreground(), 1) );
+ p.drawRect(rect());
+}
+
+void KexiToolTip::drawContents(QPainter& p)
+{
+ p.drawText(rect(), Qt::AlignCenter, m_value.toString());
+}
+
+#include "kexitooltip.moc"
diff --git a/kexi/widget/utils/kexitooltip.h b/kexi/widget/utils/kexitooltip.h
new file mode 100644
index 000000000..cbb0931f4
--- /dev/null
+++ b/kexi/widget/utils/kexitooltip.h
@@ -0,0 +1,47 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXITOOLTIP_H
+#define KEXITOOLTIP_H
+
+#include <qwidget.h>
+#include <qvariant.h>
+
+//! \brief A tooltip that can display rich content
+class KEXIGUIUTILS_EXPORT KexiToolTip : public QWidget
+{
+ Q_OBJECT
+ public:
+ KexiToolTip(const QVariant& value, QWidget* parent);
+ virtual ~KexiToolTip();
+
+ virtual QSize sizeHint() const;
+
+ public slots:
+ virtual void show();
+
+ protected:
+ virtual void paintEvent( QPaintEvent *pev );
+ virtual void drawFrame(QPainter& p);
+ virtual void drawContents(QPainter& p);
+
+ QVariant m_value;
+};
+
+#endif
diff --git a/kexi/widget/utils/klistviewitemtemplate.h b/kexi/widget/utils/klistviewitemtemplate.h
new file mode 100644
index 000000000..1c89f96c3
--- /dev/null
+++ b/kexi/widget/utils/klistviewitemtemplate.h
@@ -0,0 +1,50 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KLISTVIEWITEMTEMPLATE_H
+#define KLISTVIEWITEMTEMPLATE_H
+
+#include <klistview.h>
+
+//! QListViewItem class with ability for storing additional data member
+template<class type>
+class KListViewItemTemplate : public KListViewItem
+{
+ public:
+ KListViewItemTemplate(type _data, QListView *parent)
+ : KListViewItem(parent), data(_data) {}
+ KListViewItemTemplate(type _data, QListViewItem *parent)
+ : KListViewItem(parent), data(_data) {}
+ KListViewItemTemplate(type _data, QListView *parent, QListViewItem *after)
+ : KListViewItem(parent, after), data(_data) {}
+ KListViewItemTemplate(type _data, QListViewItem *parent, QListViewItem *after)
+ : KListViewItem(parent, after), data(_data) {}
+ KListViewItemTemplate(type _data, QListView *parent, QString label1, QString label2=QString::null, QString label3=QString::null, QString label4=QString::null, QString label5=QString::null, QString label6=QString::null, QString label7=QString::null, QString label8=QString::null)
+ : KListViewItem(parent, label1, label2, label3, label4, label5, label6, label7, label8), data(_data) {}
+ KListViewItemTemplate(type _data, QListViewItem *parent, QString label1, QString label2=QString::null, QString label3=QString::null, QString label4=QString::null, QString label5=QString::null, QString label6=QString::null, QString label7=QString::null, QString label8=QString::null)
+ : KListViewItem(parent, label1, label2, label3, label4, label5, label6, label7, label8), data(_data) {}
+ KListViewItemTemplate(type _data, QListView *parent, QListViewItem *after, QString label1, QString label2=QString::null, QString label3=QString::null, QString label4=QString::null, QString label5=QString::null, QString label6=QString::null, QString label7=QString::null, QString label8=QString::null)
+ : KListViewItem(parent, after, label1, label2, label3, label4, label5, label6, label7, label8), data(_data) {}
+ KListViewItemTemplate(type _data, QListViewItem *parent, QListViewItem *after, QString label1, QString label2=QString::null, QString label3=QString::null, QString label4=QString::null, QString label5=QString::null, QString label6=QString::null, QString label7=QString::null, QString label8=QString::null)
+ : KListViewItem(parent, after, label1, label2, label3, label4, label5, label6, label7, label8), data(_data) {}
+
+ type data;
+};
+
+#endif