summaryrefslogtreecommitdiffstats
path: root/kexi/widget
diff options
context:
space:
mode:
Diffstat (limited to 'kexi/widget')
-rw-r--r--kexi/widget/Makefile.am51
-rw-r--r--kexi/widget/kexibrowser.cpp890
-rw-r--r--kexi/widget/kexibrowser.h183
-rw-r--r--kexi/widget/kexibrowser_p.h43
-rw-r--r--kexi/widget/kexibrowseritem.cpp91
-rw-r--r--kexi/widget/kexibrowseritem.h70
-rw-r--r--kexi/widget/kexicharencodingcombobox.cpp114
-rw-r--r--kexi/widget/kexicharencodingcombobox.h48
-rw-r--r--kexi/widget/kexicustompropertyfactory.cpp110
-rw-r--r--kexi/widget/kexicustompropertyfactory.h45
-rw-r--r--kexi/widget/kexicustompropertyfactory_p.cpp108
-rw-r--r--kexi/widget/kexicustompropertyfactory_p.h71
-rw-r--r--kexi/widget/kexidataawareview.cpp383
-rw-r--r--kexi/widget/kexidataawareview.h117
-rw-r--r--kexi/widget/kexidatasourcecombobox.cpp333
-rw-r--r--kexi/widget/kexidatasourcecombobox.h88
-rw-r--r--kexi/widget/kexidatatable.cpp82
-rw-r--r--kexi/widget/kexidatatable.h80
-rw-r--r--kexi/widget/kexidbconnectionwidget.cpp407
-rw-r--r--kexi/widget/kexidbconnectionwidget.h179
-rw-r--r--kexi/widget/kexidbconnectionwidgetbase.ui464
-rw-r--r--kexi/widget/kexidbconnectionwidgetdetailsbase.ui194
-rw-r--r--kexi/widget/kexidbconnectionwidgetdetailsbase.ui.h29
-rw-r--r--kexi/widget/kexidbdrivercombobox.cpp92
-rw-r--r--kexi/widget/kexidbdrivercombobox.h102
-rw-r--r--kexi/widget/kexidswelcome.cpp90
-rw-r--r--kexi/widget/kexidswelcome.h48
-rw-r--r--kexi/widget/kexieditor.cpp261
-rw-r--r--kexi/widget/kexieditor.h120
-rw-r--r--kexi/widget/kexifieldcombobox.cpp250
-rw-r--r--kexi/widget/kexifieldcombobox.h82
-rw-r--r--kexi/widget/kexifieldlistview.cpp180
-rw-r--r--kexi/widget/kexifieldlistview.h82
-rw-r--r--kexi/widget/kexifilterdlg.cpp149
-rw-r--r--kexi/widget/kexifilterdlg.h50
-rw-r--r--kexi/widget/kexiprjtypeselector.cpp44
-rw-r--r--kexi/widget/kexiprjtypeselector.h38
-rw-r--r--kexi/widget/kexiprjtypeselectorbase.ui162
-rw-r--r--kexi/widget/kexiprjtypeselectorbase.ui.h23
-rw-r--r--kexi/widget/kexipropertyeditorview.cpp223
-rw-r--r--kexi/widget/kexipropertyeditorview.h117
-rw-r--r--kexi/widget/kexiquerydesignersqleditor.cpp35
-rw-r--r--kexi/widget/kexiquerydesignersqleditor.h39
-rw-r--r--kexi/widget/kexiqueryparameters.cpp139
-rw-r--r--kexi/widget/kexiqueryparameters.h44
-rw-r--r--kexi/widget/kexiscrollview.cpp407
-rw-r--r--kexi/widget/kexiscrollview.h93
-rw-r--r--kexi/widget/kexisectionheader.cpp162
-rw-r--r--kexi/widget/kexisectionheader.h54
-rw-r--r--kexi/widget/kexismalltoolbutton.cpp133
-rw-r--r--kexi/widget/kexismalltoolbutton.h59
-rw-r--r--kexi/widget/pixmapcollection.cpp440
-rw-r--r--kexi/widget/pixmapcollection.h162
-rw-r--r--kexi/widget/relations/Makefile.am38
-rw-r--r--kexi/widget/relations/kexirelationview.cpp639
-rw-r--r--kexi/widget/relations/kexirelationview.h167
-rw-r--r--kexi/widget/relations/kexirelationviewconnection.cpp298
-rw-r--r--kexi/widget/relations/kexirelationviewconnection.h75
-rw-r--r--kexi/widget/relations/kexirelationviewtable.cpp429
-rw-r--r--kexi/widget/relations/kexirelationviewtable.h157
-rw-r--r--kexi/widget/relations/kexirelationwidget.cpp425
-rw-r--r--kexi/widget/relations/kexirelationwidget.h137
-rw-r--r--kexi/widget/relations/r1.xpm10
-rw-r--r--kexi/widget/relations/rn.xpm9
-rw-r--r--kexi/widget/tableview/Makefile.am49
-rw-r--r--kexi/widget/tableview/autonumber.pngbin0 -> 244 bytes
-rw-r--r--kexi/widget/tableview/kexiblobtableedit.cpp595
-rw-r--r--kexi/widget/tableview/kexiblobtableedit.h170
-rw-r--r--kexi/widget/tableview/kexibooltableedit.cpp180
-rw-r--r--kexi/widget/tableview/kexibooltableedit.h87
-rw-r--r--kexi/widget/tableview/kexicelleditorfactory.cpp198
-rw-r--r--kexi/widget/tableview/kexicelleditorfactory.h79
-rw-r--r--kexi/widget/tableview/kexicomboboxbase.cpp597
-rw-r--r--kexi/widget/tableview/kexicomboboxbase.h170
-rw-r--r--kexi/widget/tableview/kexicomboboxpopup.cpp373
-rw-r--r--kexi/widget/tableview/kexicomboboxpopup.h92
-rw-r--r--kexi/widget/tableview/kexicomboboxtableedit.cpp446
-rw-r--r--kexi/widget/tableview/kexicomboboxtableedit.h166
-rw-r--r--kexi/widget/tableview/kexidataawareobjectiface.cpp2108
-rw-r--r--kexi/widget/tableview/kexidataawareobjectiface.h918
-rw-r--r--kexi/widget/tableview/kexidataawarepropertyset.cpp260
-rw-r--r--kexi/widget/tableview/kexidataawarepropertyset.h149
-rw-r--r--kexi/widget/tableview/kexidatatableview.cpp121
-rw-r--r--kexi/widget/tableview/kexidatatableview.h94
-rw-r--r--kexi/widget/tableview/kexidatetableedit.cpp290
-rw-r--r--kexi/widget/tableview/kexidatetableedit.h66
-rw-r--r--kexi/widget/tableview/kexidatetimetableedit.cpp165
-rw-r--r--kexi/widget/tableview/kexidatetimetableedit.h69
-rw-r--r--kexi/widget/tableview/kexiinputtableedit.cpp395
-rw-r--r--kexi/widget/tableview/kexiinputtableedit.h126
-rw-r--r--kexi/widget/tableview/kexitableedit.cpp237
-rw-r--r--kexi/widget/tableview/kexitableedit.h233
-rw-r--r--kexi/widget/tableview/kexitableitem.cpp62
-rw-r--r--kexi/widget/tableview/kexitableitem.h58
-rw-r--r--kexi/widget/tableview/kexitableview.cpp2607
-rw-r--r--kexi/widget/tableview/kexitableview.h639
-rw-r--r--kexi/widget/tableview/kexitableview_p.cpp67
-rw-r--r--kexi/widget/tableview/kexitableview_p.h155
-rw-r--r--kexi/widget/tableview/kexitableviewdata.cpp886
-rw-r--r--kexi/widget/tableview/kexitableviewdata.h540
-rw-r--r--kexi/widget/tableview/kexitableviewheader.cpp202
-rw-r--r--kexi/widget/tableview/kexitableviewheader.h75
-rw-r--r--kexi/widget/tableview/kexitextformatter.cpp237
-rw-r--r--kexi/widget/tableview/kexitextformatter.h64
-rw-r--r--kexi/widget/tableview/kexitimetableedit.cpp158
-rw-r--r--kexi/widget/tableview/kexitimetableedit.h64
-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
132 files changed, 28826 insertions, 0 deletions
diff --git a/kexi/widget/Makefile.am b/kexi/widget/Makefile.am
new file mode 100644
index 00000000..95dc30dd
--- /dev/null
+++ b/kexi/widget/Makefile.am
@@ -0,0 +1,51 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+lib_LTLIBRARIES = libkexiextendedwidgets.la
+libkexiextendedwidgets_la_SOURCES = kexidataawareview.cpp \
+ kexibrowser.cpp kexibrowseritem.cpp \
+ kexidatatable.cpp kexiquerydesignersqleditor.cpp kexiqueryparameters.cpp \
+ kexisectionheader.cpp pixmapcollection.cpp \
+ kexiscrollview.cpp kexidbconnectionwidgetbase.ui kexidbconnectionwidget.cpp \
+ kexidbconnectionwidgetdetailsbase.ui kexidbdrivercombobox.cpp \
+ kexieditor.cpp \
+ kexifieldlistview.cpp kexifieldcombobox.cpp kexidatasourcecombobox.cpp \
+ kexipropertyeditorview.cpp kexismalltoolbutton.cpp \
+ kexicustompropertyfactory.cpp kexicustompropertyfactory_p.cpp \
+ kexicharencodingcombobox.cpp \
+ kexiprjtypeselectorbase.ui kexiprjtypeselector.cpp
+
+libkexiextendedwidgets_la_LDFLAGS = $(all_libraries) $(VER_INFO) -Wnounresolved
+libkexiextendedwidgets_la_LIBADD = $(LIB_KDEUI) ./utils/libkexiguiutils.la tableview/libkexidatatable.la ../core/libkexicore.la -lktexteditor
+
+SUBDIRS = utils tableview . relations
+
+# kde_appsdir Where your application's menu entry (.desktop) should go to.
+# kde_icondir Where your icon should go to - better use KDE_ICON.
+# kde_sounddir Where your sounds should go to.
+# kde_htmldir Where your docs should go to. (contains lang subdirs)
+# kde_datadir Where you install application data. (Use a subdir)
+# kde_locale Where translation files should go to. (contains lang subdirs)
+# kde_cgidir Where cgi-bin executables should go to.
+# kde_confdir Where config files should go to (system-wide ones with default values).
+# kde_mimedir Where mimetypes .desktop files should go to.
+# kde_servicesdir Where services .desktop files should go to.
+# kde_servicetypesdir Where servicetypes .desktop files should go to.
+# kde_toolbardir Where general toolbar icons should go to (deprecated, use KDE_ICON).
+# kde_wallpaperdir Where general wallpapers should go to.
+# kde_templatesdir Where templates for the "New" menu (Konqueror/KDesktop) should go to.
+# kde_bindir Where executables should go to. Use bin_PROGRAMS or bin_SCRIPTS.
+# kde_libdir Where shared libraries should go to. Use lib_LTLIBRARIES.
+# kde_moduledir Where modules (e.g. parts) should go to. Use kde_module_LTLIBRARIES.
+# kde_styledir Where Qt/KDE widget styles should go to (new in KDE 3).
+# kde_designerdir Where Qt Designer plugins should go to (new in KDE 3).
+
+# set the include path for X, qt and KDE - all_includes must remain last!
+INCLUDES = -I$(top_srcdir)/kexi -I$(top_srcdir)/kexi/widget/tableview \
+ $(LIB_KEXI_KMDI_INCLUDES) \
+ -I$(top_srcdir)/kexi/core \
+ -I$(top_srcdir)/lib/kofficecore -I$(top_srcdir)/lib $(all_includes)
+
+noinst_HEADERS = kexibrowseritem.h kexibrowser_p.h
+
+METASOURCES = AUTO
+
diff --git a/kexi/widget/kexibrowser.cpp b/kexi/widget/kexibrowser.cpp
new file mode 100644
index 00000000..ca637cd2
--- /dev/null
+++ b/kexi/widget/kexibrowser.cpp
@@ -0,0 +1,890 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-2006 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.
+*/
+
+#include "kexibrowser.h"
+#include "kexibrowser_p.h"
+#include "kexibrowseritem.h"
+
+#include <qheader.h>
+#include <qpoint.h>
+#include <qpixmapcache.h>
+#include <qtoolbutton.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <klistview.h>
+#include <kmessagebox.h>
+#include <klineedit.h>
+#include <kimageeffect.h>
+#include <kconfig.h>
+
+#include <kexi.h>
+#include <kexipart.h>
+#include <kexipartinfo.h>
+#include <kexipartitem.h>
+#include <kexiproject.h>
+#include <kexidialogbase.h>
+#include <keximainwindow.h>
+#include <kexiutils/identifier.h>
+#include <widget/utils/kexiflowlayout.h>
+#include <widget/kexismalltoolbutton.h>
+#include <kexidb/utils.h>
+
+/*
+class KexiBrowserView : public QWidget
+{
+ KexiBrowserView*(
+};
+
+KexiBrowserView::KexiBrowserView(KexiMainWindow *mainWin)
+ : QWidget(mainWin, "KexiBrowserView")
+{
+ QVBoxLayout *lyr = new QVBoxLayout(this);
+ KexiFlowLayout *buttons_flyr = new KexiFlowLayout(lyr);
+ m_browser = KexiBrowser(this, mainWin);
+ lyr->addWidget(m_browser);
+ setFocusProxy(m_browser);
+}*/
+
+KexiBrowser::KexiBrowser(QWidget* parent, KexiMainWindow *mainWin, int features)
+ : QWidget(parent, "KexiBrowser")
+ , m_mainWin(mainWin)
+ , m_features(features)
+ , m_actions( new KActionCollection(this) )
+ , m_baseItems(199, false)
+ , m_normalItems(199)
+ , m_prevSelectedPart(0)
+ , m_singleClick(false)
+// , m_nameEndsWithAsterisk(false)
+ , m_readOnly(false)
+// , m_enableExecuteArea(true)
+{
+ setCaption(i18n("Project Navigator"));
+ setIcon(*m_mainWin->icon());
+
+ QVBoxLayout *lyr = new QVBoxLayout(this);
+ KexiFlowLayout *buttons_flyr = new KexiFlowLayout(lyr);
+
+ m_list = new KexiBrowserListView(this);
+ lyr->addWidget(m_list);
+ m_list->installEventFilter(this);
+ m_list->renameLineEdit()->installEventFilter(this);
+ connect( kapp, SIGNAL( settingsChanged(int) ), SLOT( slotSettingsChanged(int) ) );
+ slotSettingsChanged(0);
+
+ m_list->header()->hide();
+ m_list->addColumn("");
+ m_list->setShowToolTips(true);
+ m_list->setSorting(0);
+ m_list->sort();
+ m_list->setAllColumnsShowFocus(true);
+ m_list->setTooltipColumn(0);
+ m_list->renameLineEdit()->setValidator( new KexiUtils::IdentifierValidator(this) );
+ m_list->setResizeMode(QListView::LastColumn);
+ connect(m_list, SIGNAL(contextMenu(KListView *, QListViewItem *, const QPoint &)),
+ this, SLOT(slotContextMenu(KListView*, QListViewItem *, const QPoint&)));
+ connect(m_list, SIGNAL(selectionChanged(QListViewItem*)), this,
+ SLOT(slotSelectionChanged(QListViewItem*)));
+
+ KConfig *config = kapp->config();
+ config->setGroup("MainWindow");
+ if ((m_features & SingleClickOpensItemOptionEnabled)
+ && config->readBoolEntry("SingleClickOpensItem", false))
+ {
+ connect(m_list, SIGNAL(executed(QListViewItem*)), this,
+ SLOT(slotExecuteItem(QListViewItem*)));
+ }
+ else {
+ connect(m_list, SIGNAL(doubleClicked(QListViewItem*)), this,
+ SLOT(slotExecuteItem(QListViewItem*)));
+ m_list->enableExecuteArea = false;
+ }
+
+ // actions
+ m_openAction = new KAction(i18n("&Open"), "fileopen", 0/*Qt::Key_Enter conflict!*/, this,
+ SLOT(slotOpenObject()), this, "open_object");
+ m_openAction->setToolTip(i18n("Open object"));
+ m_openAction->setWhatsThis(i18n("Opens object selected in the list"));
+
+// m_openAction->plug(m_toolbar);
+ KexiSmallToolButton *btn;
+ if (m_features & Toolbar) {
+ btn = new KexiSmallToolButton(this, m_openAction);
+ buttons_flyr->add(btn);
+ }
+
+ if (m_mainWin->userMode()) {
+//! @todo some of these actions can be supported once we deliver ACLs...
+ m_deleteAction = 0;
+ m_renameAction = 0;
+ m_designAction = 0;
+ m_editTextAction = 0;
+ m_newObjectAction = 0;
+ m_newObjectPopup = 0;
+ }
+ else {
+ m_deleteAction = new KAction(i18n("&Delete"), "editdelete", 0/*Qt::Key_Delete*/,
+ this, SLOT(slotRemove()), m_actions, "edit_delete");
+ //! @todo 1.1: just add "Delete" tooltip and what's this
+ m_deleteAction->setToolTip(i18n("&Delete").replace("&",""));
+
+ m_renameAction = new KAction(i18n("&Rename"), "", 0,
+ this, SLOT(slotRename()), m_actions, "edit_rename");
+#ifdef KEXI_SHOW_UNIMPLEMENTED
+ //todo plugSharedAction("edit_cut",SLOT(slotCut()));
+ //todo plugSharedAction("edit_copy",SLOT(slotCopy()));
+ //todo plugSharedAction("edit_paste",SLOT(slotPaste()));
+#endif
+
+ m_designAction = new KAction(i18n("&Design"), "edit", 0/*Qt::CTRL + Qt::Key_Enter conflict!*/, this,
+ SLOT(slotDesignObject()), this, "design_object");
+ m_designAction->setToolTip(i18n("Design object"));
+ m_designAction->setWhatsThis(i18n("Starts designing of the object selected in the list"));
+ if (m_features & Toolbar) {
+ btn = new KexiSmallToolButton(this, m_designAction);
+ buttons_flyr->add(btn);
+ }
+
+ m_editTextAction = new KAction(i18n("Open in &Text View"), "", 0, this,
+ SLOT(slotEditTextObject()), this, "editText_object");
+ m_editTextAction->setToolTip(i18n("Open object in text view"));
+ m_editTextAction->setWhatsThis(i18n("Opens selected object in the list in text view"));
+
+ m_newObjectAction = new KAction("", "filenew", 0, this, SLOT(slotNewObject()), this, "new_object");
+ if (m_features & Toolbar) {
+ m_newObjectToolButton = new KexiSmallToolButton(this, "", QIconSet(), "new_object");
+ m_newObjectPopup = new KPopupMenu(this, "newObjectPopup");
+ connect(m_newObjectPopup, SIGNAL(aboutToShow()), this, SLOT(slotNewObjectPopupAboutToShow()));
+ // KexiPart::Part* part = Kexi::partManager().part("kexi/table");
+ // m_newObjectPopup->insertItem( SmallIconSet(part->info()->createItemIcon()), part->instanceName() );
+ m_newObjectToolButton->setPopup(m_newObjectPopup);
+ m_newObjectToolButton->setPopupDelay(QApplication::startDragTime());
+ connect(m_newObjectToolButton, SIGNAL(clicked()), this, SLOT(slotNewObject()));
+ buttons_flyr->add(m_newObjectToolButton);
+
+ m_deleteObjectToolButton = new KexiSmallToolButton(this, m_deleteAction);
+ m_deleteObjectToolButton->setTextLabel("");
+ buttons_flyr->add(m_deleteObjectToolButton);
+ }
+ }
+
+ m_executeAction = new KAction(i18n("Execute"), "player_play", 0, this,
+ SLOT(slotExecuteObject()), this, "data_execute");
+
+ m_exportActionMenu = new KActionMenu(i18n("Export"));
+ m_dataExportAction = new KAction(i18n("Export->To File as Data &Table... ", "To &File as Data Table..."),
+ "table", 0, this, SLOT(slotExportAsDataTable()), this, "exportAsDataTable");
+ m_dataExportAction->setWhatsThis(
+ i18n("Exports data from the currently selected table or query data to a file."));
+ m_exportActionMenu->insert( m_dataExportAction );
+
+ m_printAction = new KAction(i18n("&Print..."), "fileprint", 0, this,
+ SLOT(slotPrintItem()), this, "printItem");
+ m_printAction->setWhatsThis(
+ i18n("Prints data from the currently selected table or query."));
+ m_pageSetupAction = new KAction(i18n("Page Setup..."), "", 0, this,
+ SLOT(slotPageSetupForItem()), this, "pageSetupForItem");
+ m_pageSetupAction->setWhatsThis(
+ i18n("Shows page setup for printing the active table or query."));
+
+ if (m_mainWin->userMode()) {
+//! @todo some of these actions can be supported once we deliver ACLs...
+ m_partPopup = 0;
+ }
+ else {
+ m_partPopup = new KPopupMenu(this, "partPopup");
+ m_partPopupTitle_id = m_partPopup->insertTitle("");
+ m_newObjectAction->plug(m_partPopup);
+#ifdef KEXI_SHOW_UNIMPLEMENTED
+ m_partPopup->insertSeparator();
+ plugSharedAction("edit_paste", m_partPopup);
+#endif
+ }
+
+ if (m_features & ContextMenus) {
+ //init popups
+ m_itemPopup = new KPopupMenu(this, "itemPopup");
+ m_itemPopupTitle_id = m_itemPopup->insertTitle("");
+ m_openAction->plug(m_itemPopup);
+ m_openAction_id = m_itemPopup->idAt(m_itemPopup->count()-1);
+
+ if (m_designAction) {
+ m_designAction->plug(m_itemPopup);
+ m_designAction_id = m_itemPopup->idAt(m_itemPopup->count()-1);
+ }
+
+ if (m_editTextAction) {
+ m_editTextAction->plug(m_itemPopup);
+ m_editTextAction_id = m_itemPopup->idAt(m_itemPopup->count()-1);
+ }
+
+ if (m_newObjectAction) {
+ m_newObjectAction->plug(m_itemPopup);
+ m_itemPopup->insertSeparator();
+ }
+#ifdef KEXI_SHOW_UNIMPLEMENTED
+ //todo plugSharedAction("edit_cut", m_itemPopup);
+ //todo plugSharedAction("edit_copy", m_itemPopup);
+ //todo m_itemPopup->insertSeparator();
+#endif
+ m_executeAction->plug(m_itemPopup);
+ m_executeAction_id = m_itemPopup->idAt(m_itemPopup->count()-1);
+
+ m_exportActionMenu->plug(m_itemPopup);
+ m_exportActionMenu_id = m_exportActionMenu->menuId(0);
+ m_itemPopup->insertSeparator();
+ m_exportActionMenu_id_sep = m_itemPopup->idAt(m_itemPopup->count()-1);
+
+ m_printAction->plug(m_itemPopup);
+ m_printAction_id = m_itemPopup->idAt(m_itemPopup->count()-1);
+
+ m_pageSetupAction->plug(m_itemPopup);
+ m_pageSetupAction_id = m_itemPopup->idAt(m_itemPopup->count()-1);
+ if (m_renameAction || m_deleteAction) {
+ m_itemPopup->insertSeparator();
+ m_pageSetupAction_id_sep = m_itemPopup->idAt(m_itemPopup->count()-1);
+ }
+ else {
+ m_pageSetupAction_id_sep = -1;
+ }
+
+ if (m_renameAction)
+ m_renameAction->plug(m_itemPopup);
+ if (m_deleteAction)
+ m_deleteAction->plug(m_itemPopup);
+ }
+ else {
+ m_itemPopup = 0;
+ }
+
+ if (!(m_features & Writable)) {
+ setReadOnly(true);
+ }
+}
+
+KexiBrowser::~KexiBrowser()
+{
+}
+
+void KexiBrowser::setProject(KexiProject* prj, const QString& itemsMimeType,
+ QString* partManagerErrorMessages)
+{
+ clear();
+ m_itemsMimeType = itemsMimeType;
+ m_list->setRootIsDecorated(m_itemsMimeType.isEmpty());
+
+ KexiPart::PartInfoList *pl = Kexi::partManager().partInfoList();
+ for (KexiPart::Info *info = pl->first(); info; info = pl->next()) {
+ if (!info->isVisibleInNavigator())
+ continue;
+ if (!m_itemsMimeType.isEmpty() && info->mimeType()!=m_itemsMimeType.latin1())
+ continue;
+
+// kdDebug() << "KexiMainWindowImpl::initNavigator(): adding " << it->groupName() << endl;
+
+/* KexiPart::Part *p=Kexi::partManager().part(it);
+ if (!p) {
+ //TODO: js - OPTIONALLY: show error
+ continue;
+ }
+ p->createGUIClient(this);*/
+
+ //load part - we need this to have GUI merged with part's actions
+//! @todo FUTURE - don't do that when DESIGN MODE is OFF
+ KexiPart::Part *p=Kexi::partManager().part(info);
+ if (p) {
+ KexiBrowserItem *groupItem = 0;
+ if (m_itemsMimeType.isEmpty()) {
+ groupItem = addGroup(*info);
+ if (!groupItem)
+ continue;
+ }
+ //lookup project's objects (part items)
+//! @todo FUTURE - don't do that when DESIGN MODE is OFF
+ KexiPart::ItemDict *item_dict = prj->items(info);
+ if (!item_dict)
+ continue;
+ for (KexiPart::ItemDictIterator item_it( *item_dict ); item_it.current(); ++item_it)
+ addItem(*item_it.current(), groupItem, info);
+ if (!m_itemsMimeType.isEmpty())
+ break; //the only group added, so our work is completed
+ }
+ else {
+ //add this error to the list that will be displayed later
+ QString msg, details;
+ KexiDB::getHTMLErrorMesage(&Kexi::partManager(), msg, details);
+ if (!msg.isEmpty() && partManagerErrorMessages) {
+ if (partManagerErrorMessages->isEmpty()) {
+ *partManagerErrorMessages = QString("<qt><p>")
+ +i18n("Errors encountered during loading plugins:")+"<ul>";
+ }
+ partManagerErrorMessages->append( QString("<li>") + msg );
+ if (!details.isEmpty())
+ partManagerErrorMessages->append(QString("<br>")+details);
+ partManagerErrorMessages->append("</li>");
+ }
+ }
+ }
+ if (partManagerErrorMessages && !partManagerErrorMessages->isEmpty())
+ partManagerErrorMessages->append("</ul></p>");
+}
+
+QString KexiBrowser::itemsMimeType() const
+{
+ return m_itemsMimeType;
+}
+
+KexiBrowserItem *KexiBrowser::addGroup(KexiPart::Info& info)
+{
+ if(!info.isVisibleInNavigator())
+ return 0;
+
+ KexiBrowserItem *item = new KexiBrowserItem(m_list, &info);
+ m_baseItems.insert(info.mimeType().lower(), item);
+ return item;
+// kdDebug() << "KexiBrowser::addGroup()" << endl;
+}
+
+KexiBrowserItem* KexiBrowser::addItem(KexiPart::Item& item)
+{
+ //part object for this item
+ KexiBrowserItem *parent = item.mimeType().isEmpty() ? 0 : m_baseItems.find(item.mimeType().lower());
+ return addItem(item, parent, parent->info());
+}
+
+KexiBrowserItem* KexiBrowser::addItem(KexiPart::Item& item, KexiBrowserItem *parent, KexiPart::Info* info)
+{
+// if (!parent) //TODO: add "Other" part group for that
+ // return 0;
+// kdDebug() << "KexiBrowser::addItem() found parent:" << parent << endl;
+ KexiBrowserItem *bitem;
+ if (parent)
+ bitem = new KexiBrowserItem(parent, info, &item);
+ else
+ bitem = new KexiBrowserItem(m_list, info, &item);
+ m_normalItems.insert(item.identifier(), bitem);
+ return bitem;
+}
+
+void
+KexiBrowser::slotRemoveItem(const KexiPart::Item &item)
+{
+ KexiBrowserItem *to_remove=m_normalItems.take(item.identifier());
+ if (!to_remove)
+ return;
+
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(m_list->selectedItem());
+
+ QListViewItem *new_item_to_select = 0;
+ if (it==to_remove) {//compute item to select if current one will be removed
+ new_item_to_select = it->itemBelow();//nearest item to select
+ if (!new_item_to_select || new_item_to_select->parent()!=it->parent()) {
+ new_item_to_select = it->itemAbove();
+ }
+ }
+ delete to_remove;
+
+ if (new_item_to_select)
+ m_list->setSelected(new_item_to_select, true);
+}
+
+void
+KexiBrowser::slotContextMenu(KListView* /*list*/, QListViewItem *item, const QPoint &pos)
+{
+ if(!item || !(m_features & ContextMenus))
+ return;
+ KexiBrowserItem *bit = static_cast<KexiBrowserItem*>(item);
+ KPopupMenu *pm = 0;
+ if (bit->item()) {
+ pm = m_itemPopup;
+ //update popup title
+ QString title_text = bit->text(0).stripWhiteSpace();
+ KexiBrowserItem *par_it = static_cast<KexiBrowserItem*>(bit->parent());
+ KexiPart::Part* par_part = 0;
+ if (par_it && par_it->info() && ((par_part = Kexi::partManager().part(par_it->info()))) && !par_part->instanceCaption().isEmpty()) {
+ //add part type name
+ title_text += (" : " + par_part->instanceCaption());
+ }
+ pm->changeTitle(m_itemPopupTitle_id, *bit->pixmap(0), title_text);
+ }
+ else if (m_partPopup) {
+ pm = m_partPopup;
+ QString title_text = bit->text(0).stripWhiteSpace();
+ pm->changeTitle(m_partPopupTitle_id, *bit->pixmap(0), title_text);
+/* KexiPart::Part* part = Kexi::partManager().part(bit->info());
+ if (part)
+ m_newObjectAction->setText(i18n("&Create Object: %1...").arg( part->instanceName() ));
+ else
+ m_newObjectAction->setText(i18n("&Create Object..."));
+ m_newObjectAction->setIconSet( SmallIconSet(bit->info()->itemIcon()) );*/
+ m_list->setCurrentItem(item);
+ m_list->repaintItem(item);
+ }
+ if (pm)
+ pm->exec(pos);
+}
+
+void
+KexiBrowser::slotExecuteItem(QListViewItem *vitem)
+{
+// kdDebug() << "KexiBrowser::slotExecuteItem()" << endl;
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(vitem);
+ if (!it)
+ return;
+ if (!it->item() && !m_singleClick /*annoying when in single click mode*/) {
+ m_list->setOpen( vitem, !vitem->isOpen() );
+ return;
+ }
+ if (it->info()->isExecuteSupported())
+ emit executeItem( it->item() );
+ else
+ emit openOrActivateItem( it->item(), Kexi::DataViewMode );
+}
+
+void
+KexiBrowser::slotSelectionChanged(QListViewItem* i)
+{
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(i);
+ if (!it)
+ return;
+ KexiPart::Part* part = Kexi::partManager().part(it->info());
+ if (!part) {
+ it = static_cast<KexiBrowserItem*>(it->parent());
+ if (it) {
+ part = Kexi::partManager().part(it->info());
+ }
+ }
+
+ const bool gotitem = it && it->item();
+ //bool gotgroup = it && !it->item();
+//TODO: also check if the item is not read only
+ if (m_deleteAction) {
+ m_deleteAction->setEnabled(gotitem && !m_readOnly);
+ if (m_features & Toolbar) {
+ m_deleteObjectToolButton->setEnabled(gotitem && !m_readOnly);
+ }
+ }
+#ifdef KEXI_SHOW_UNIMPLEMENTED
+//todo setAvailable("edit_cut",gotitem);
+//todo setAvailable("edit_copy",gotitem);
+//todo setAvailable("edit_edititem",gotitem);
+#endif
+
+ m_openAction->setEnabled(gotitem && part && (part->supportedViewModes() & Kexi::DataViewMode));
+ if (m_designAction)
+ m_designAction->setEnabled(gotitem && part && (part->supportedViewModes() & Kexi::DesignViewMode));
+ if (m_editTextAction)
+ m_editTextAction->setEnabled(gotitem && part && (part->supportedViewModes() & Kexi::TextViewMode));
+
+ if (m_features & ContextMenus) {
+ m_itemPopup->setItemVisible(m_openAction_id, m_openAction->isEnabled());
+ if (m_designAction)
+ m_itemPopup->setItemVisible(m_designAction_id, m_designAction->isEnabled());
+ if (m_editTextAction)
+ m_itemPopup->setItemVisible(m_editTextAction_id, part && m_editTextAction->isEnabled());
+ if (m_executeAction)
+ m_itemPopup->setItemVisible(m_executeAction_id, gotitem && it->info()->isExecuteSupported());
+ if (m_exportActionMenu) {
+ m_itemPopup->setItemVisible(m_exportActionMenu_id, gotitem && it->info()->isDataExportSupported());
+ m_itemPopup->setItemVisible(m_exportActionMenu_id_sep, gotitem && it->info()->isDataExportSupported());
+ }
+ if (m_printAction)
+ m_itemPopup->setItemVisible(m_printAction_id, gotitem && it->info()->isPrintingSupported());
+ if (m_pageSetupAction) {
+ m_itemPopup->setItemVisible(m_pageSetupAction_id, gotitem && it->info()->isPrintingSupported());
+ if (m_pageSetupAction_id_sep!=-1)
+ m_itemPopup->setItemVisible(m_pageSetupAction_id_sep, gotitem && it->info()->isPrintingSupported());
+ }
+ }
+
+ if (m_prevSelectedPart != part) {
+ m_prevSelectedPart = part;
+ if (part) {
+ if (m_newObjectAction) {
+ m_newObjectAction->setText(i18n("&Create Object: %1...").arg( part->instanceCaption() ));
+ m_newObjectAction->setIcon( part->info()->createItemIcon() );
+ if (m_features & Toolbar) {
+ m_newObjectToolButton->setIconSet( part->info()->createItemIcon() );
+ QToolTip::add(m_newObjectToolButton,
+ i18n("Create object: %1").arg( part->instanceCaption().lower() ));
+ QWhatsThis::add(m_newObjectToolButton,
+ i18n("Creates a new object: %1").arg( part->instanceCaption().lower() ));
+ }
+ }
+ } else {
+ if (m_newObjectAction) {
+ m_newObjectAction->setText(i18n("&Create Object..."));
+ // m_newObjectToolbarAction->setIconSet( SmallIconSet("filenew") );
+ // m_newObjectToolbarAction->setText(m_newObjectAction->text());
+ if (m_features & Toolbar) {
+ m_newObjectToolButton->setIconSet( "filenew" );
+ QToolTip::add(m_newObjectToolButton, i18n("Create object"));
+ QWhatsThis::add(m_newObjectToolButton, i18n("Creates a new object"));
+ }
+ }
+ }
+ }
+ emit selectionChanged(it ? it->item() : 0);
+}
+
+void KexiBrowser::installEventFilter ( const QObject * filterObj )
+{
+ if (!filterObj)
+ return;
+ m_list->installEventFilter ( filterObj );
+ QWidget::installEventFilter ( filterObj );
+}
+
+bool KexiBrowser::eventFilter ( QObject *o, QEvent * e )
+{
+ if (o==m_list && e->type()==QEvent::Resize) {
+ kdDebug() << "resize!" << endl;
+ }
+ if (o==m_list->renameLineEdit()) {
+ if (e->type()==QEvent::Hide)
+ itemRenameDone();
+ }
+ else if (e->type()==QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ if (ke->key()==Qt::Key_Enter || ke->key()==Qt::Key_Return) {
+ if (0==(ke->state() & (Qt::ShiftButton|Qt::ControlButton|Qt::AltButton))) {
+ QListViewItem *it = m_list->selectedItem();
+ if (it)
+ slotExecuteItem(it);
+ }
+ else if (Qt::ControlButton==(ke->state() & (Qt::ShiftButton|Qt::ControlButton|Qt::AltButton))) {
+ slotDesignObject();
+ }
+ }
+ }
+ else if (e->type()==QEvent::AccelOverride) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ //override delete action
+ if (ke->key()==Qt::Key_Delete && ke->state()==Qt::NoButton) {
+ slotRemove();
+ ke->accept();
+ return true;
+ }
+ //override rename action
+ if (ke->key()==Qt::Key_F2 && ke->state()==Qt::NoButton) {
+ slotRename();
+ ke->accept();
+ return true;
+ }
+/* else if (ke->key()==Qt::Key_Enter || ke->key()==Qt::Key_Return) {
+ if (ke->state()==ControlButton) {
+ slotDesignObject();
+ }
+ else if (ke->state()==0 && !m_list->renameLineEdit()->isVisible()) {
+ QListViewItem *it = m_list->selectedItem();
+ if (it)
+ slotExecuteItem(it);
+ }
+ ke->accept();
+ return true;
+ }*/
+ }
+ return false;
+}
+
+void KexiBrowser::slotRemove()
+{
+ if (!m_deleteAction || !m_deleteAction->isEnabled() || !(m_features & Writable))
+ return;
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(m_list->selectedItem());
+ if (!it || !it->item())
+ return;
+ emit removeItem( it->item() );
+}
+
+void KexiBrowser::slotNewObject()
+{
+ if (!m_newObjectAction || !(m_features & Writable))
+ return;
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(m_list->selectedItem());
+ if (!it || !it->info())
+ return;
+ emit newItem( it->info() );
+}
+
+void KexiBrowser::slotOpenObject()
+{
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(m_list->selectedItem());
+ if (!it || !it->item())
+ return;
+ emit openItem( it->item(), Kexi::DataViewMode );
+}
+
+void KexiBrowser::slotDesignObject()
+{
+ if (!m_designAction)
+ return;
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(m_list->selectedItem());
+ if (!it || !it->item())
+ return;
+ emit openItem( it->item(), Kexi::DesignViewMode );
+}
+
+void KexiBrowser::slotEditTextObject()
+{
+ if (!m_editTextAction)
+ return;
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(m_list->selectedItem());
+ if (!it || !it->item())
+ return;
+ emit openItem( it->item(), Kexi::TextViewMode );
+}
+
+void KexiBrowser::slotCut()
+{
+ if (!(m_features & Writable))
+ return;
+// KEXI_UNFINISHED_SHARED_ACTION("edit_cut");
+ //TODO
+}
+
+void KexiBrowser::slotCopy()
+{
+// KEXI_UNFINISHED_SHARED_ACTION("edit_copy");
+ //TODO
+}
+
+void KexiBrowser::slotPaste()
+{
+ if (!(m_features & Writable))
+ return;
+// KEXI_UNFINISHED_SHARED_ACTION("edit_paste");
+ //TODO
+}
+
+void KexiBrowser::slotRename()
+{
+ if (!m_renameAction || !(m_features & Writable))
+ return;
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(m_list->selectedItem());
+ if (it)
+ m_list->rename(it, 0);
+}
+
+void KexiBrowser::itemRenameDone()
+{
+ if (!(m_features & Writable))
+ return;
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(m_list->selectedItem());
+ if (!it)
+ return;
+ QString txt = it->text(0).stripWhiteSpace();
+ bool ok = it->item()->name().lower()!=txt.lower(); //the new name must be different
+ if (ok) {
+ /* TODO */
+ emit renameItem(it->item(), txt, ok);
+ }
+ if (!ok) {
+ txt = it->item()->name(); //revert
+ }
+ //"modified" flag has been removed before editing: readd it
+ if (m_list->nameEndsWithAsterisk) {
+ txt += "*";
+ m_list->nameEndsWithAsterisk = false;
+ }
+ it->setText(0, txt);
+ it->parent()->sort();
+ setFocus();
+}
+
+void KexiBrowser::setFocus()
+{
+ if (!m_list->selectedItem() && m_list->firstChild())//select first
+ m_list->setSelected(m_list->firstChild(), true);
+ m_list->setFocus();
+}
+
+void KexiBrowser::updateItemName( KexiPart::Item& item, bool dirty )
+{
+ if (!(m_features & Writable))
+ return;
+ KexiBrowserItem *bitem = m_normalItems[item.identifier()];
+ if (!bitem)
+ return;
+ bitem->setText( 0, item.name() + (dirty ? "*" : "") );
+}
+
+void KexiBrowser::slotSettingsChanged(int)
+{
+ m_singleClick = KGlobalSettings::singleClick();
+}
+
+void KexiBrowser::selectItem(KexiPart::Item& item)
+{
+ KexiBrowserItem *bitem = m_normalItems[item.identifier()];
+ if (!bitem)
+ return;
+ m_list->setSelected(bitem, true);
+ m_list->ensureItemVisible(bitem);
+ m_list->setCurrentItem(bitem);
+}
+
+void KexiBrowser::clearSelection()
+{
+ m_list->clearSelection();
+ QListViewItem *item = m_list->firstChild();
+ if (item) {
+ m_list->ensureItemVisible(item);
+ }
+}
+
+void KexiBrowser::slotNewObjectPopupAboutToShow()
+{
+ if ((m_features & Toolbar) && m_newObjectPopup && m_newObjectPopup->count()==0) {
+ //preload items
+ KexiPart::PartInfoList *list = Kexi::partManager().partInfoList(); //this list is properly sorted
+ for (KexiPart::PartInfoListIterator it(*list); it.current(); ++it) {
+ //add an item to "New object" toolbar popup
+ KAction *action = m_mainWin->actionCollection()->action(
+ KexiPart::nameForCreateAction(*it.current()) );
+ if (action) {
+ action->plug(m_newObjectPopup);
+ }
+ else {
+ //! @todo err
+ }
+ }
+ }
+}
+
+void KexiBrowser::slotExecuteObject()
+{
+ if (!m_executeAction)
+ return;
+ KexiPart::Item* item = selectedPartItem();
+ if (item)
+ emit executeItem( item );
+}
+
+void KexiBrowser::slotExportAsDataTable()
+{
+ if (!m_dataExportAction)
+ return;
+ KexiPart::Item* item = selectedPartItem();
+ if (item)
+ emit exportItemAsDataTable( item );
+}
+
+KexiPart::Item* KexiBrowser::selectedPartItem() const
+{
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(m_list->selectedItem());
+ return it ? it->item() : 0;
+}
+
+bool KexiBrowser::actionEnabled(const QCString& actionName) const
+{
+ if (actionName=="project_export_data_table" && (m_features & ContextMenus))
+ return m_itemPopup->isItemVisible(m_exportActionMenu_id);
+ kdWarning() << "KexiBrowser::actionEnabled() no such action: " << actionName << endl;
+ return false;
+}
+
+void KexiBrowser::slotPrintItem()
+{
+ if (!m_printAction)
+ return;
+ KexiPart::Item* item = selectedPartItem();
+ if (item)
+ emit printItem( item );
+}
+
+void KexiBrowser::slotPageSetupForItem()
+{
+ if (!m_pageSetupAction)
+ return;
+ KexiPart::Item* item = selectedPartItem();
+ if (item)
+ emit pageSetupForItem( item );
+}
+
+
+void KexiBrowser::setReadOnly(bool set)
+{
+ m_readOnly = set;
+ if (m_deleteAction)
+ m_deleteAction->setEnabled(!m_readOnly);
+ if (m_renameAction)
+ m_renameAction->setEnabled(!m_readOnly);
+ if (m_newObjectAction) {
+ m_newObjectAction->setEnabled(!m_readOnly);
+ if (m_features & Toolbar) {
+ m_newObjectPopup->setEnabled(!m_readOnly);
+ m_newObjectToolButton->setEnabled(!m_readOnly);
+ }
+ }
+}
+
+bool KexiBrowser::isReadOnly() const
+{
+ return m_readOnly;
+}
+
+void KexiBrowser::clear()
+{
+ m_list->clear();
+}
+
+//--------------------------------------------
+KexiBrowserListView::KexiBrowserListView(QWidget *parent)
+ : KListView(parent, "KexiBrowserListView")
+ , nameEndsWithAsterisk(false)
+ , enableExecuteArea(true)
+{
+}
+
+KexiBrowserListView::~KexiBrowserListView()
+{
+}
+
+void KexiBrowserListView::rename(QListViewItem *item, int c)
+{
+ if (renameLineEdit()->isVisible())
+ return;
+ KexiBrowserItem *it = static_cast<KexiBrowserItem*>(item);
+ if (it->item() && c==0) {
+ //only edit 1st column for items, not item groups
+//TODO: also check it this item is not read-only
+// item->setText(0, item->text(0).mid(1,item->text(0).length()-2));
+ //remove "modified" flag for editing
+ nameEndsWithAsterisk = item->text(0).endsWith("*");
+ if (nameEndsWithAsterisk)
+ item->setText(0, item->text(0).left(item->text(0).length()-1));
+ KListView::rename(item, c);
+ adjustColumn(0);
+ }
+}
+
+bool KexiBrowserListView::isExecuteArea( const QPoint& point )
+{
+ return enableExecuteArea && KListView::isExecuteArea(point);
+}
+
+#include "kexibrowser.moc"
+#include "kexibrowser_p.moc"
diff --git a/kexi/widget/kexibrowser.h b/kexi/widget/kexibrowser.h
new file mode 100644
index 00000000..1bdc5d25
--- /dev/null
+++ b/kexi/widget/kexibrowser.h
@@ -0,0 +1,183 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-2006 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 KEXIBROWSER_H
+#define KEXIBROWSER_H
+
+#include <klistview.h>
+#include <qasciidict.h>
+#include <qintdict.h>
+
+class QListViewItem;
+class KIconLoader;
+class KPopupMenu;
+class KAction;
+class KActionMenu;
+class KActionCollection;
+class KListView;
+class KToolBar;
+class KexiBrowserItem;
+class KexiView;
+class KexiMainWindow;
+class KexiSmallToolButton;
+class KexiBrowserListView;
+
+namespace KexiPart
+{
+ class Info;
+ class Item;
+ class Part;
+}
+class KexiProject;
+
+//! @short The Main Kexi navigator widget
+class KEXIEXTWIDGETS_EXPORT KexiBrowser : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ enum Features {
+ Writable = 1, //!< the browser supports actions that modify the project (e.g. delete, rename)
+ ContextMenus = 2, //!< the browser supports context menu
+ Toolbar = 4, //!< the browser displays
+ SingleClickOpensItemOptionEnabled = 8, //!< enables "SingleClickOpensItem" option
+ DefaultFeatures = Writable | ContextMenus | Toolbar
+ | SingleClickOpensItemOptionEnabled //!< the default
+ };
+
+ KexiBrowser(QWidget* parent, KexiMainWindow *mainWin, int features = DefaultFeatures);
+ virtual ~KexiBrowser();
+
+ /*! Sets project \a prj for this browser. If \a partManagerErrorMessages is not NULL
+ it will be set to error message if there's a problem with loading any KexiPart.
+ If \a itemsMimeType is empty (the default), items of all mime types are displayed,
+ items for only one mime type are displayed. In the latter case, no group (parent)
+ items are displayed.
+ Previous items are removed. */
+ void setProject(KexiProject* prj, const QString& itemsMimeType = QString::null,
+ QString* partManagerErrorMessages = 0);
+
+ /*! \return items' mime type previously set by setProject. Returns empty string
+ if setProject() was not executed yet or itemsMimeType argument of setProject() was
+ empty (i.e. all mime types are displayed). */
+ QString itemsMimeType() const;
+
+ KexiPart::Item* selectedPartItem() const;
+
+ void installEventFilter ( const QObject * filterObj );
+ virtual bool eventFilter ( QObject *o, QEvent * e );
+
+ bool actionEnabled(const QCString& actionName) const;
+
+ public slots:
+ KexiBrowserItem* addGroup(KexiPart::Info& info);
+ KexiBrowserItem* addItem(KexiPart::Item& item);
+ void slotRemoveItem(const KexiPart::Item &item);
+ virtual void setFocus();
+ void updateItemName(KexiPart::Item& item, bool dirty);
+ void selectItem(KexiPart::Item& item);
+ void clearSelection();
+ void clear();
+
+ //! Sets by main window to disable actions that may try to modify the project.
+ //! Does not disable actions like opening objects.
+ void setReadOnly(bool set);
+
+ bool isReadOnly() const;
+
+ signals:
+ void openItem( KexiPart::Item*, int viewMode );
+
+ /*! this signal is emmited when user double clicked (or single -depending on settings)
+ or pressed return ky on the part item.
+ This signal differs from openItem() signal in that if the object is already opened
+ in view mode other than \a viewMode, the mode is not changed. */
+ void openOrActivateItem( KexiPart::Item*, int viewMode );
+
+ void newItem( KexiPart::Info* );
+
+ void removeItem( KexiPart::Item* );
+
+ void renameItem( KexiPart::Item *item, const QString& _newName, bool &succes );
+
+ void selectionChanged( KexiPart::Item* item );
+
+ void executeItem( KexiPart::Item* );
+
+ void exportItemAsDataTable( KexiPart::Item* );
+
+ void printItem( KexiPart::Item* );
+
+ void pageSetupForItem( KexiPart::Item* );
+
+ protected slots:
+ void slotContextMenu(KListView*, QListViewItem *i, const QPoint &point);
+ void slotExecuteItem(QListViewItem *item);
+ void slotSelectionChanged(QListViewItem* i);
+ void slotSettingsChanged(int);
+ void slotNewObjectPopupAboutToShow();
+
+ void slotNewObject();
+ void slotOpenObject();
+ void slotDesignObject();
+ void slotEditTextObject();
+ void slotRemove();
+ void slotCut();
+ void slotCopy();
+ void slotPaste();
+ void slotRename();
+ void slotExecuteObject();
+ void slotExportAsDataTable();
+ void slotPrintItem();
+ void slotPageSetupForItem();
+
+ protected:
+ void itemRenameDone();
+ KexiBrowserItem* addItem(KexiPart::Item& item, KexiBrowserItem *parent, KexiPart::Info* info);
+
+ KexiMainWindow *m_mainWin;
+ int m_features;
+ KexiBrowserListView *m_list;
+ KActionCollection *m_actions;
+ QAsciiDict<KexiBrowserItem> m_baseItems;
+ QIntDict<KexiBrowserItem> m_normalItems;
+ KPopupMenu *m_itemPopup, *m_partPopup;
+ KAction *m_deleteAction, *m_renameAction,
+ *m_newObjectAction, // *m_newObjectToolbarAction,
+ *m_openAction, *m_designAction, *m_editTextAction,
+ *m_executeAction,
+ *m_dataExportAction, *m_printAction, *m_pageSetupAction;
+ KActionMenu* m_exportActionMenu;
+ KPopupMenu* m_newObjectPopup;
+ int m_itemPopupTitle_id, m_partPopupTitle_id,
+ m_openAction_id, m_designAction_id, m_editTextAction_id,
+ m_executeAction_id,
+ m_exportActionMenu_id, m_exportActionMenu_id_sep,
+ m_printAction_id, m_pageSetupAction_id, m_pageSetupAction_id_sep;
+
+ KexiPart::Part *m_prevSelectedPart;
+ KToolBar *m_toolbar;
+ KexiSmallToolButton *m_newObjectToolButton, *m_deleteObjectToolButton;
+ QString m_itemsMimeType;
+ bool m_singleClick : 1;
+ bool m_readOnly : 1;
+};
+
+#endif
diff --git a/kexi/widget/kexibrowser_p.h b/kexi/widget/kexibrowser_p.h
new file mode 100644
index 00000000..b5610d24
--- /dev/null
+++ b/kexi/widget/kexibrowser_p.h
@@ -0,0 +1,43 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-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 KEXIBROWSER_P_H
+#define KEXIBROWSER_P_H
+
+#include <klistview.h>
+
+/*! @internal */
+class KexiBrowserListView : public KListView
+{
+ Q_OBJECT
+ public:
+ KexiBrowserListView(QWidget *parent);
+ ~KexiBrowserListView();
+
+ virtual bool isExecuteArea( const QPoint& point );
+
+ bool nameEndsWithAsterisk : 1;
+ bool enableExecuteArea : 1; //!< used in isExecuteArea()
+ public slots:
+ virtual void rename(QListViewItem *item, int c);
+ protected:
+};
+
+#endif
diff --git a/kexi/widget/kexibrowseritem.cpp b/kexi/widget/kexibrowseritem.cpp
new file mode 100644
index 00000000..087039f0
--- /dev/null
+++ b/kexi/widget/kexibrowseritem.cpp
@@ -0,0 +1,91 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-2004 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.
+*/
+
+#include "kexibrowseritem.h"
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <core/kexipartinfo.h>
+
+KexiBrowserItem::KexiBrowserItem(KListView *parent, KexiPart::Info *i)
+ : KListViewItem(parent, i->groupName())
+ , m_info(i)
+ , m_item(0)
+{
+ setPixmap(0, SmallIcon(i->itemIcon()));
+ setOpen(true);
+//ugly setSelectable(false);
+ initItem();
+ m_fifoSorting = 1; //because this is top level item
+}
+
+KexiBrowserItem::KexiBrowserItem(KListViewItem *parent, KexiPart::Info *i, KexiPart::Item *item)
+ : KListViewItem(parent, item->name())
+ , m_info(i)
+ , m_item(item)
+{
+ setPixmap(0, SmallIcon(i->itemIcon()));
+ initItem();
+}
+
+KexiBrowserItem::KexiBrowserItem(KListView *parent, KexiPart::Info *i, KexiPart::Item *item)
+ : KListViewItem(parent, item->name())
+ , m_info(i)
+ , m_item(item)
+{
+ setPixmap(0, SmallIcon(i->itemIcon()));
+ initItem();
+}
+
+KexiBrowserItem::~KexiBrowserItem()
+{
+}
+
+void KexiBrowserItem::initItem()
+{
+ m_fifoSorting = 0;
+ int sortKey = 0;
+ // set sorting key with FIFO order
+ if (parent()) {
+ sortKey = parent()->childCount();
+ } else if (listView()) {
+ sortKey = listView()->childCount();
+ }
+ m_sortKey.sprintf("%2.2d",sortKey);
+// kdDebug() << "m_sortKey=" << m_sortKey << endl;
+}
+
+void
+KexiBrowserItem::clearChildren()
+{
+ KexiBrowserItem* child;
+
+ while((child = static_cast<KexiBrowserItem*>(firstChild())))
+ {
+ delete child;
+ }
+}
+
+QString KexiBrowserItem::key( int column, bool ascending ) const
+{
+// kdDebug() << "KexiBrowserItem::key() : " << (m_fifoSorting ? m_sortKey : KListViewItem::key(column,ascending)) << endl;
+ return m_fifoSorting ? m_sortKey : KListViewItem::key(column,ascending);
+}
+
diff --git a/kexi/widget/kexibrowseritem.h b/kexi/widget/kexibrowseritem.h
new file mode 100644
index 00000000..12c2fef6
--- /dev/null
+++ b/kexi/widget/kexibrowseritem.h
@@ -0,0 +1,70 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-2004 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 KEXIBROWSERITEM_H
+#define KEXIBROWSERITEM_H
+
+#include <klistview.h>
+#include <qstring.h>
+
+#include <core/kexipartitem.h>
+
+namespace KexiPart
+{
+ class Info;
+}
+
+//! @short List view item for the navigator widget (KexiBrowser)
+//! Used for creating group items as well as object items
+class KEXIEXTWIDGETS_EXPORT KexiBrowserItem : public KListViewItem
+{
+ public:
+ //! Creates group item for part \a i
+ KexiBrowserItem(KListView *parent, KexiPart::Info *i);
+
+ //! Creates item for object \a item defined by part \a i for \a parent
+ KexiBrowserItem(KListViewItem *parent, KexiPart::Info *i, KexiPart::Item *item);
+
+ //! Creates item for object \a item defined by part \a i, without parent
+ //! (used in a case when KexiBrowser::itemsMimeType() is not empty)
+ KexiBrowserItem(KListView *parent, KexiPart::Info *i, KexiPart::Item *item);
+
+ virtual ~KexiBrowserItem();
+
+ void clearChildren();
+
+ //! \return part info; should not be null.
+ KexiPart::Info *info() const { return m_info; }
+
+ //! \return part item. Can be null if the browser item is a "folder", i.e. a parent node.
+ KexiPart::Item* item() const { return m_item; }
+
+ protected:
+ void initItem();
+ virtual QString key( int column, bool ascending ) const;
+
+ KexiPart::Info *m_info;
+ KexiPart::Item *m_item;
+
+ QString m_sortKey;
+ bool m_fifoSorting : 1;
+};
+
+#endif
diff --git a/kexi/widget/kexicharencodingcombobox.cpp b/kexi/widget/kexicharencodingcombobox.cpp
new file mode 100644
index 00000000..c1f50542
--- /dev/null
+++ b/kexi/widget/kexicharencodingcombobox.cpp
@@ -0,0 +1,114 @@
+/* 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 "kexicharencodingcombobox.h"
+
+#include <qtextcodec.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+
+KexiCharacterEncodingComboBox::KexiCharacterEncodingComboBox(
+ QWidget* parent, const QString& selectedEncoding )
+ : KComboBox( parent, "KexiCharacterEncodingComboBox" )
+ , m_defaultEncodingAdded(false)
+{
+ QString defaultEncoding(QString::fromLatin1(KGlobal::locale()->encoding()));
+ QString defaultEncodingDescriptiveName;
+
+ QString _selectedEncoding = selectedEncoding;
+ if (_selectedEncoding.isEmpty())
+ _selectedEncoding = QString::fromLatin1(KGlobal::locale()->encoding());
+
+ QStringList descEncodings(KGlobal::charsets()->descriptiveEncodingNames());
+ QStringList::ConstIterator it = descEncodings.constBegin();
+
+ for (uint id = 0; it!=descEncodings.constEnd(); ++it)
+ {
+ bool found = false;
+ QString name( KGlobal::charsets()->encodingForName( *it ) );
+ QTextCodec *codecForEnc = KGlobal::charsets()->codecForName(name, found);
+ if (found) {
+ insertItem(*it);
+ if (codecForEnc->name() == defaultEncoding || name == defaultEncoding) {
+ defaultEncodingDescriptiveName = *it;
+ //remember, do not add, will be prepended later
+ }
+ else {
+ m_encodingDescriptionForName.insert(name, *it);
+ }
+ if (codecForEnc->name() == _selectedEncoding || name == _selectedEncoding) {
+ setCurrentItem(id);
+ }
+ id++;
+ }
+ }
+
+ //prepend default encoding, if present
+ if (!defaultEncodingDescriptiveName.isEmpty()) {
+ m_defaultEncodingAdded = true;
+ QString desc = i18n("Text encoding: Default", "Default: %1")
+ .arg(defaultEncodingDescriptiveName);
+ insertItem( desc, 0 );
+ if (_selectedEncoding==defaultEncoding) {
+ setCurrentItem(0);
+ }
+ else
+ setCurrentItem(currentItem()+1);
+ m_encodingDescriptionForName.insert(defaultEncoding, desc);
+ }
+}
+
+KexiCharacterEncodingComboBox::~KexiCharacterEncodingComboBox()
+{
+}
+
+QString KexiCharacterEncodingComboBox::selectedEncoding() const
+{
+ if (defaultEncodingSelected()) {
+ return QString::fromLatin1(KGlobal::locale()->encoding());
+ }
+ else {
+ return KGlobal::charsets()->encodingForName( currentText() );
+ }
+}
+
+void KexiCharacterEncodingComboBox::setSelectedEncoding(const QString& encodingName)
+{
+ QString desc = m_encodingDescriptionForName[encodingName];
+ if (desc.isEmpty()) {
+ kdWarning() << "KexiCharacterEncodingComboBox::setSelectedEncoding(): "
+ "no such encoding \"" << encodingName << "\"" << endl;
+ return;
+ }
+ setCurrentText(desc);
+}
+
+bool KexiCharacterEncodingComboBox::defaultEncodingSelected() const
+{
+ return m_defaultEncodingAdded && 0==currentItem();
+}
+
+void KexiCharacterEncodingComboBox::selectDefaultEncoding()
+{
+ if (m_defaultEncodingAdded)
+ setCurrentItem(0);
+}
diff --git a/kexi/widget/kexicharencodingcombobox.h b/kexi/widget/kexicharencodingcombobox.h
new file mode 100644
index 00000000..9af5a69f
--- /dev/null
+++ b/kexi/widget/kexicharencodingcombobox.h
@@ -0,0 +1,48 @@
+/* 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 KEXICHARENCODINGCOMBO_H
+#define KEXICHARENCODINGCOMBO_H
+
+#include <qmap.h>
+#include <kcombobox.h>
+
+/*! @short Combobox widget providing a list of possible character encodings.
+*/
+class KEXIEXTWIDGETS_EXPORT KexiCharacterEncodingComboBox : public KComboBox
+{
+ public:
+ //! Constructs a new combobox. \a selectedEncoding can be provided to preselect encoding.
+ //! If it is not provided, default encoding is selected for current system settings.
+ KexiCharacterEncodingComboBox( QWidget* parent = 0,
+ const QString& selectedEncoding = QString::null );
+ ~KexiCharacterEncodingComboBox();
+
+ QString selectedEncoding() const;
+ void setSelectedEncoding(const QString& encodingName);
+ //! Selects default encoding, if present
+ void selectDefaultEncoding();
+ bool defaultEncodingSelected() const;
+
+ protected:
+ QMap<QString,QString> m_encodingDescriptionForName;
+ bool m_defaultEncodingAdded : 1;
+};
+
+#endif
diff --git a/kexi/widget/kexicustompropertyfactory.cpp b/kexi/widget/kexicustompropertyfactory.cpp
new file mode 100644
index 00000000..3df9e233
--- /dev/null
+++ b/kexi/widget/kexicustompropertyfactory.cpp
@@ -0,0 +1,110 @@
+/* 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.
+*/
+
+#include "kexicustompropertyfactory.h"
+#include "kexicustompropertyfactory_p.h"
+#include <kexiutils/identifier.h>
+
+#include <koproperty/customproperty.h>
+
+using namespace KoProperty;
+
+//! @internal
+class PixmapIdCustomProperty : public CustomProperty
+{
+ public:
+ PixmapIdCustomProperty(Property *parent)
+ : CustomProperty(parent) {
+ }
+ virtual ~PixmapIdCustomProperty() {};
+ virtual void setValue(const QVariant &value, bool rememberOldValue) {
+ Q_UNUSED( value );
+ Q_UNUSED( rememberOldValue);
+ }
+ virtual QVariant value() const { return m_property->value(); }
+ virtual bool handleValue() const {
+ return false; //m_property->type()==KexiCustomPropertyFactory::PixmapData;
+ }
+};
+
+//! @internal
+class IdentifierCustomProperty : public CustomProperty
+{
+ public:
+ IdentifierCustomProperty(Property *parent)
+ : CustomProperty(parent) {
+ }
+ virtual ~IdentifierCustomProperty() {};
+ virtual void setValue(const QVariant &value, bool rememberOldValue)
+ {
+ Q_UNUSED(rememberOldValue);
+ if (!value.toString().isEmpty())
+ m_value = KexiUtils::string2Identifier(value.toString()).lower();
+ }
+ virtual QVariant value() const { return m_value; }
+ virtual bool handleValue() const {
+ return true;
+ }
+ QString m_value;
+};
+
+//---------------
+
+KexiCustomPropertyFactory::KexiCustomPropertyFactory(QObject* parent)
+: CustomPropertyFactory(parent)
+{
+}
+
+KexiCustomPropertyFactory::~KexiCustomPropertyFactory()
+{
+}
+
+CustomProperty* KexiCustomPropertyFactory::createCustomProperty(Property *parent)
+{
+ const int type = parent->type();
+ if (type==(int)KexiCustomPropertyFactory::PixmapId)
+ return new PixmapIdCustomProperty(parent);
+ else if (type==(int)KexiCustomPropertyFactory::Identifier)
+ return new IdentifierCustomProperty(parent);
+ return 0;
+}
+
+Widget* KexiCustomPropertyFactory::createCustomWidget(Property *prop)
+{
+ const int type = prop->type();
+ if (type==(int)KexiCustomPropertyFactory::PixmapId)
+ return new KexiImagePropertyEdit(prop);
+ else if (type==(int)KexiCustomPropertyFactory::Identifier)
+ return new KexiIdentifierPropertyEdit(prop);
+
+ return 0;
+}
+
+void KexiCustomPropertyFactory::init()
+{
+ if (KoProperty::FactoryManager::self()->factoryForEditorType(KexiCustomPropertyFactory::PixmapId))
+ return; //already registered
+
+ // register custom editors and properties
+ KexiCustomPropertyFactory *factory = new KexiCustomPropertyFactory(KoProperty::FactoryManager::self());
+ QValueList<int> types;
+ types << KexiCustomPropertyFactory::PixmapId << KexiCustomPropertyFactory::Identifier;
+ KoProperty::FactoryManager::self()->registerFactoryForProperties(types, factory);
+ KoProperty::FactoryManager::self()->registerFactoryForEditors(types, factory);
+}
diff --git a/kexi/widget/kexicustompropertyfactory.h b/kexi/widget/kexicustompropertyfactory.h
new file mode 100644
index 00000000..c6e6e973
--- /dev/null
+++ b/kexi/widget/kexicustompropertyfactory.h
@@ -0,0 +1,45 @@
+/* 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 KEXICUSTOMPROPFACTORY_H
+#define KEXICUSTOMPROPFACTORY_H
+
+#include <koproperty/property.h>
+#include <koproperty/factory.h>
+
+//! Kexi-specific custom property factory for KoProperty library
+class KEXIEXTWIDGETS_EXPORT KexiCustomPropertyFactory : public KoProperty::CustomPropertyFactory
+{
+ public:
+ enum PropertyType {
+ PixmapId = KoProperty::UserDefined+0, //!< Shared Kexi pixmap
+ Identifier = KoProperty::UserDefined+1 //!< string allowing nonempty identifiers
+ };
+
+ //! Called once to register all propert and editor types provided by this factory.
+ static void init();
+
+ KexiCustomPropertyFactory(QObject* parent);
+ virtual ~KexiCustomPropertyFactory();
+
+ virtual KoProperty::CustomProperty* createCustomProperty(KoProperty::Property *parent);
+ virtual KoProperty::Widget* createCustomWidget(KoProperty::Property *prop);
+};
+
+#endif
diff --git a/kexi/widget/kexicustompropertyfactory_p.cpp b/kexi/widget/kexicustompropertyfactory_p.cpp
new file mode 100644
index 00000000..0e0a054d
--- /dev/null
+++ b/kexi/widget/kexicustompropertyfactory_p.cpp
@@ -0,0 +1,108 @@
+/* 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.
+*/
+
+#include "kexicustompropertyfactory_p.h"
+
+#include <qlineedit.h>
+#include <kdebug.h>
+#include <koproperty/property.h>
+#include <kexiutils/identifier.h>
+
+using namespace KoProperty;
+
+KexiImagePropertyEdit::KexiImagePropertyEdit(
+ Property *property, QWidget *parent, const char *name)
+ : PixmapEdit(property, parent, name)
+ , m_id(0)
+{
+}
+
+KexiImagePropertyEdit::~KexiImagePropertyEdit()
+{
+}
+
+void KexiImagePropertyEdit::selectPixmap()
+{
+ QString fileName( PixmapEdit::selectPixmapFileName() );
+ if (fileName.isEmpty())
+ return;
+ KexiBLOBBuffer::Handle h(KexiBLOBBuffer::self()->insertPixmap( KURL(fileName) ));
+ setValue((uint)/*! @todo unsafe*/h.id());
+#if 0 //will be reenabled for new image collection
+ if(!m_manager->activeForm() || !property())
+ return;
+
+ ObjectTreeItem *item = m_manager->activeForm()->objectTree()->lookup(m_manager->activeForm()->selectedWidget()->name());
+ QString name = item ? item->pixmapName(property()->name()) : "";
+ PixmapCollectionChooser dialog( m_manager->activeForm()->pixmapCollection(), name, topLevelWidget() );
+ if(dialog.exec() == QDialog::Accepted) {
+ setValue(dialog.pixmap(), true);
+ item->setPixmapName(property()->name(), dialog.pixmapName());
+ }
+#endif
+}
+
+QVariant KexiImagePropertyEdit::value() const
+{
+ return (uint)/*! @todo unsafe*/m_id;
+}
+
+void KexiImagePropertyEdit::setValue(const QVariant &value, bool emitChange)
+{
+ m_id = value.toInt();
+ PixmapEdit::setValue(KexiBLOBBuffer::self()->objectForId(m_id).pixmap(), emitChange);
+}
+
+void KexiImagePropertyEdit::drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r,
+ const QVariant &value)
+{
+ KexiBLOBBuffer::Handle h( KexiBLOBBuffer::self()->objectForId(value.toInt()) );
+ PixmapEdit::drawViewer(p, cg, r, h.pixmap());
+}
+
+//----------------------------------------------------------------
+
+KexiIdentifierPropertyEdit::KexiIdentifierPropertyEdit(
+ Property *property, QWidget *parent, const char *name)
+ : StringEdit(property, parent, name)
+{
+ m_edit->setValidator(
+ new KexiUtils::IdentifierValidator(m_edit, "KexiIdentifierPropertyEdit Validator") );
+}
+
+KexiIdentifierPropertyEdit::~KexiIdentifierPropertyEdit()
+{
+}
+
+void KexiIdentifierPropertyEdit::setValue(const QVariant &value, bool emitChange)
+{
+ QString string(value.toString());
+ if (string.isEmpty()) {
+ kdWarning() << "KexiIdentifierPropertyEdit::setValue(): "
+ "Value cannot be empty. This call has no effect." << endl;
+ return;
+ }
+ QString identifier( KexiUtils::string2Identifier(string) );
+ if (identifier!=string)
+ kdDebug() << QString("KexiIdentifierPropertyEdit::setValue(): "
+ "String \"%1\" converted to identifier \"%2\".").arg(string).arg(identifier) << endl;
+ StringEdit::setValue( identifier, emitChange );
+}
+
+#include "kexicustompropertyfactory_p.moc"
diff --git a/kexi/widget/kexicustompropertyfactory_p.h b/kexi/widget/kexicustompropertyfactory_p.h
new file mode 100644
index 00000000..f1ec4b0f
--- /dev/null
+++ b/kexi/widget/kexicustompropertyfactory_p.h
@@ -0,0 +1,71 @@
+/* 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 KEXICUSTOMPROPFACTORY_P_H
+#define KEXICUSTOMPROPFACTORY_P_H
+
+#include <koproperty/editors/pixmapedit.h>
+#include <koproperty/editors/stringedit.h>
+#include <kexiblobbuffer.h>
+
+//! Kexi-specific image editor for property editor's item
+class KexiImagePropertyEdit : public KoProperty::PixmapEdit
+{
+ Q_OBJECT
+
+ public:
+ KexiImagePropertyEdit(KoProperty::Property *property,
+ QWidget *parent=0, const char *name=0);
+ virtual ~KexiImagePropertyEdit();
+
+ virtual QVariant value() const;
+ virtual void setValue(const QVariant &value, bool emitChange=true);
+ virtual void drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r,
+ const QVariant &value);
+
+ public slots:
+ virtual void selectPixmap();
+
+ protected:
+ KexiBLOBBuffer::Id_t m_id;
+};
+
+/*! Identifier editor based on ordinary string editor but always keeps a valid identifier
+ or empty value. It's line edit has IdentifierValidator::IdentifierValidator set, so user
+ is unable to enter invalid characters. Any chages to a null value or empty string,
+ have no effect.
+
+ @todo move this to koproperty library (when KexiUtils is moves to kofficecore)
+ */
+class KexiIdentifierPropertyEdit : public KoProperty::StringEdit
+{
+ Q_OBJECT
+
+ public:
+ KexiIdentifierPropertyEdit(KoProperty::Property *property,
+ QWidget *parent=0, const char *name=0);
+ virtual ~KexiIdentifierPropertyEdit();
+
+ /*! Reimplemented: sets \a value but it is converted to identifier
+ using KexiUtils::string2Identifier().
+ If \a value is null or empty string, this method has no effect. */
+ virtual void setValue(const QVariant &value, bool emitChange=true);
+};
+
+#endif
diff --git a/kexi/widget/kexidataawareview.cpp b/kexi/widget/kexidataawareview.cpp
new file mode 100644
index 00000000..418f0aa1
--- /dev/null
+++ b/kexi/widget/kexidataawareview.cpp
@@ -0,0 +1,383 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-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 "kexidataawareview.h"
+
+#include <kexidataawareobjectiface.h>
+#include <utils/kexisharedactionclient.h>
+#include <core/keximainwindow.h>
+
+#include <qlayout.h>
+
+#include <kpopupmenu.h>
+
+KexiDataAwareView::KexiDataAwareView(KexiMainWindow *mainWin, QWidget *parent, const char *name)
+ : KexiViewBase(mainWin, parent, name)
+ , KexiSearchAndReplaceViewInterface()
+ , m_internalView(0)
+ , m_actionClient(0)
+ , m_dataAwareObject(0)
+{
+}
+
+void KexiDataAwareView::init( QWidget* viewWidget, KexiSharedActionClient* actionClient,
+ KexiDataAwareObjectInterface* dataAwareObject, bool noDataAware )
+{
+ m_internalView = viewWidget;
+ m_actionClient = actionClient;
+ m_dataAwareObject = dataAwareObject;
+ setViewWidget(m_internalView, true);
+
+ if (!noDataAware) {
+ m_dataAwareObject->connectCellSelectedSignal(this, SLOT(slotCellSelected(int,int)));
+
+ //! before closing - we'are accepting editing
+ connect(this, SIGNAL(closing(bool&)), this, SLOT(slotClosing(bool&)));
+
+ //! updating actions on start/stop editing
+ m_dataAwareObject->connectRowEditStartedSignal(this, SLOT(slotUpdateRowActions(int)));
+ m_dataAwareObject->connectRowEditTerminatedSignal(this, SLOT(slotUpdateRowActions(int)));
+ m_dataAwareObject->connectReloadActionsSignal(this, SLOT(reloadActions()));
+ }
+
+ QVBoxLayout *box = new QVBoxLayout(this);
+ box->addWidget(m_internalView);
+
+ setMinimumSize(m_internalView->minimumSizeHint().width(),
+ m_internalView->minimumSizeHint().height());
+ resize( preferredSizeHint( m_internalView->sizeHint() ) );
+ setFocusProxy(m_internalView);
+
+ if (!noDataAware) {
+ initActions();
+ reloadActions();
+ }
+}
+
+void KexiDataAwareView::initActions()
+{
+ plugSharedAction("edit_delete_row", this, SLOT(deleteCurrentRow()));
+ m_actionClient->plugSharedAction(sharedAction("edit_delete_row")); //for proper shortcut
+
+ plugSharedAction("edit_delete", this, SLOT(deleteAndStartEditCurrentCell()));
+ m_actionClient->plugSharedAction(sharedAction("edit_delete")); //for proper shortcut
+
+ plugSharedAction("edit_edititem", this, SLOT(startEditOrToggleValue()));
+ m_actionClient->plugSharedAction(sharedAction("edit_edititem")); //for proper shortcut
+
+ plugSharedAction("data_save_row", this, SLOT(acceptRowEdit()));
+ m_actionClient->plugSharedAction(sharedAction("data_save_row")); //for proper shortcut
+
+ plugSharedAction("data_cancel_row_changes", this, SLOT(cancelRowEdit()));
+ m_actionClient->plugSharedAction(sharedAction("data_cancel_row_changes")); //for proper shortcut
+
+ if (m_dataAwareObject->isSortingEnabled()) {
+ plugSharedAction("data_sort_az", this, SLOT(sortAscending()));
+ plugSharedAction("data_sort_za", this, SLOT(sortDescending()));
+ }
+
+ m_actionClient->plugSharedAction(sharedAction("edit_insert_empty_row")); //for proper shortcut
+
+ setAvailable("data_sort_az", m_dataAwareObject->isSortingEnabled());
+ setAvailable("data_sort_za", m_dataAwareObject->isSortingEnabled());
+//! \todo plugSharedAction("data_filter", this, SLOT(???()));
+
+ plugSharedAction("data_go_to_first_record", this, SLOT(slotGoToFirstRow()));
+ plugSharedAction("data_go_to_previous_record", this, SLOT(slotGoToPreviusRow()));
+ plugSharedAction("data_go_to_next_record", this, SLOT(slotGoToNextRow()));
+ plugSharedAction("data_go_to_last_record", this, SLOT(slotGoToLastRow()));
+ plugSharedAction("data_go_to_new_record", this, SLOT(slotGoToNewRow()));
+
+//! \todo update availability
+ setAvailable("data_go_to_first_record", true);
+ setAvailable("data_go_to_previous_record", true);
+ setAvailable("data_go_to_next_record", true);
+ setAvailable("data_go_to_last_record", true);
+ setAvailable("data_go_to_new_record", true);
+
+ plugSharedAction("edit_copy", this, SLOT(copySelection()));
+ m_actionClient->plugSharedAction(sharedAction("edit_copy")); //for proper shortcut
+
+ plugSharedAction("edit_cut", this, SLOT(cutSelection()));
+ m_actionClient->plugSharedAction(sharedAction("edit_cut")); //for proper shortcut
+
+ plugSharedAction("edit_paste", this, SLOT(paste()));
+ m_actionClient->plugSharedAction(sharedAction("edit_paste")); //for proper shortcut
+
+// plugSharedAction("edit_find", this, SLOT(editFind()));
+// m_actionClient->plugSharedAction(sharedAction("edit_find")); //for proper shortcut
+
+// plugSharedAction("edit_findnext", this, SLOT(editFindNext()));
+// m_actionClient->plugSharedAction(sharedAction("edit_findnext")); //for proper shortcut
+
+// plugSharedAction("edit_findprevious", this, SLOT(editFindPrevious()));
+// m_actionClient->plugSharedAction(sharedAction("edit_findprev")); //for proper shortcut
+
+//! @todo plugSharedAction("edit_replace", this, SLOT(editReplace()));
+//! @todo m_actionClient->plugSharedAction(sharedAction("edit_replace")); //for proper shortcut
+
+// setAvailable("edit_find", true);
+// setAvailable("edit_findnext", true);
+// setAvailable("edit_findprevious", true);
+//! @todo setAvailable("edit_replace", true);
+}
+
+void KexiDataAwareView::slotUpdateRowActions(int row)
+{
+ const bool ro = m_dataAwareObject->isReadOnly();
+// const bool inserting = m_dataAwareObject->isInsertingEnabled();
+ const bool deleting = m_dataAwareObject->isDeleteEnabled();
+ const bool emptyInserting = m_dataAwareObject->isEmptyRowInsertingEnabled();
+ const bool editing = m_dataAwareObject->rowEditing();
+ const bool sorting = m_dataAwareObject->isSortingEnabled();
+ const int rows = m_dataAwareObject->rows();
+
+ setAvailable("edit_cut", !ro);
+ setAvailable("edit_paste", !ro);
+ setAvailable("edit_delete", !ro); // && !(inserting && row==rows));
+ setAvailable("edit_delete_row", !ro && !(deleting && row==rows));
+ setAvailable("edit_insert_empty_row", !ro && emptyInserting);
+ setAvailable("edit_clear_table", !ro && deleting && rows>0);
+ setAvailable("data_save_row", editing);
+ setAvailable("data_cancel_row_changes", editing);
+ setAvailable("data_sort_az", sorting);
+ setAvailable("data_sort_za", sorting);
+}
+
+QWidget* KexiDataAwareView::mainWidget()
+{
+ return m_internalView;
+}
+
+QSize KexiDataAwareView::minimumSizeHint() const
+{
+ return m_internalView ? m_internalView->minimumSizeHint() : QSize(0,0);//KexiViewBase::minimumSizeHint();
+}
+
+QSize KexiDataAwareView::sizeHint() const
+{
+ return m_internalView ? m_internalView->sizeHint() : QSize(0,0);//KexiViewBase::sizeHint();
+}
+
+void KexiDataAwareView::updateActions(bool activated)
+{
+ setAvailable("data_sort_az", m_dataAwareObject->isSortingEnabled());
+ setAvailable("data_sort_za", m_dataAwareObject->isSortingEnabled());
+ KexiViewBase::updateActions(activated);
+}
+
+void KexiDataAwareView::reloadActions()
+{
+// m_view->initActions(guiClient()->actionCollection());
+//warning FIXME Move this to the table part
+/*
+ kdDebug()<<"INIT ACTIONS***********************************************************************"<<endl;
+ new KAction(i18n("Filter"), "filter", 0, this, SLOT(filter()), actionCollection(), "tablepart_filter");
+ setXMLFile("kexidatatableui.rc");
+*/
+ m_dataAwareObject->contextMenu()->clear();
+
+ plugSharedAction("edit_cut", m_dataAwareObject->contextMenu());
+ plugSharedAction("edit_copy", m_dataAwareObject->contextMenu());
+ plugSharedAction("edit_paste", m_dataAwareObject->contextMenu());
+
+ bool separatorNeeded = true;
+
+ unplugSharedAction("edit_clear_table");
+ plugSharedAction("edit_clear_table", this, SLOT(deleteAllRows()));
+
+ if (m_dataAwareObject->isEmptyRowInsertingEnabled()) {
+ unplugSharedAction("edit_insert_empty_row");
+ plugSharedAction("edit_insert_empty_row", m_internalView, SLOT(insertEmptyRow()));
+ if (separatorNeeded)
+ m_dataAwareObject->contextMenu()->insertSeparator();
+ plugSharedAction("edit_insert_empty_row", m_dataAwareObject->contextMenu());
+ }
+ else {
+ unplugSharedAction("edit_insert_empty_row");
+ unplugSharedAction("edit_insert_empty_row", m_dataAwareObject->contextMenu());
+ }
+
+ if (m_dataAwareObject->isDeleteEnabled()) {
+ if (separatorNeeded)
+ m_dataAwareObject->contextMenu()->insertSeparator();
+ plugSharedAction("edit_delete", m_dataAwareObject->contextMenu());
+ plugSharedAction("edit_delete_row", m_dataAwareObject->contextMenu());
+ }
+ else {
+ unplugSharedAction("edit_delete_row", m_dataAwareObject->contextMenu());
+ unplugSharedAction("edit_delete_row", m_dataAwareObject->contextMenu());
+ }
+ //if (!m_view->isSortingEnabled()) {
+// unplugSharedAction("data_sort_az");
+// unplugSharedAction("data_sort_za");
+ //}
+ setAvailable("data_sort_az", m_dataAwareObject->isSortingEnabled());
+ setAvailable("data_sort_za", m_dataAwareObject->isSortingEnabled());
+
+ slotCellSelected( m_dataAwareObject->currentColumn(), m_dataAwareObject->currentRow() );
+}
+
+void KexiDataAwareView::slotCellSelected(int /*col*/, int row)
+{
+ slotUpdateRowActions(row);
+}
+
+void KexiDataAwareView::deleteAllRows()
+{
+ m_dataAwareObject->deleteAllRows(true/*ask*/, true/*repaint*/);
+}
+
+void KexiDataAwareView::deleteCurrentRow()
+{
+ m_dataAwareObject->deleteCurrentRow();
+}
+
+void KexiDataAwareView::deleteAndStartEditCurrentCell()
+{
+ m_dataAwareObject->deleteAndStartEditCurrentCell();
+}
+
+void KexiDataAwareView::startEditOrToggleValue()
+{
+ m_dataAwareObject->startEditOrToggleValue();
+}
+
+bool KexiDataAwareView::acceptRowEdit()
+{
+ return m_dataAwareObject->acceptRowEdit();
+}
+
+void KexiDataAwareView::slotClosing(bool& cancel)
+{
+ if (!acceptRowEdit())
+ cancel = true;
+}
+
+void KexiDataAwareView::cancelRowEdit()
+{
+ m_dataAwareObject->cancelRowEdit();
+}
+
+void KexiDataAwareView::sortAscending()
+{
+ m_dataAwareObject->sortAscending();
+}
+
+void KexiDataAwareView::sortDescending()
+{
+ m_dataAwareObject->sortDescending();
+}
+
+void KexiDataAwareView::copySelection()
+{
+ m_dataAwareObject->copySelection();
+}
+
+void KexiDataAwareView::cutSelection()
+{
+ m_dataAwareObject->cutSelection();
+}
+
+void KexiDataAwareView::paste()
+{
+ m_dataAwareObject->paste();
+}
+
+void KexiDataAwareView::slotGoToFirstRow() { m_dataAwareObject->selectFirstRow(); }
+void KexiDataAwareView::slotGoToPreviusRow() { m_dataAwareObject->selectPrevRow(); }
+void KexiDataAwareView::slotGoToNextRow() { m_dataAwareObject->selectNextRow(); }
+void KexiDataAwareView::slotGoToLastRow() { m_dataAwareObject->selectLastRow(); }
+void KexiDataAwareView::slotGoToNewRow() { m_dataAwareObject->addNewRecordRequested(); }
+
+bool KexiDataAwareView::setupFindAndReplace(QStringList& columnNames, QStringList& columnCaptions,
+ QString& currentColumnName)
+{
+ if (!dataAwareObject() || !dataAwareObject()->data())
+ return false;
+ KexiTableViewColumn::List columns( dataAwareObject()->data()->columns );
+ for (KexiTableViewColumn::ListIterator it(columns); it.current(); ++it) {
+ if (!it.current()->visible())
+ continue;
+ columnNames.append( it.current()->field()->name() );
+ columnCaptions.append( it.current()->captionAliasOrName() );
+ }
+
+ //update "look in" selection if there was any
+ const int currentColumnNumber = dataAwareObject()->currentColumn();
+ if (currentColumnNumber!=-1) {
+ KexiTableViewColumn *col = columns.at( currentColumnNumber );
+ if (col && col->field())
+ currentColumnName = col->field()->name();
+ }
+ return true;
+}
+
+tristate KexiDataAwareView::find(const QVariant& valueToFind,
+ const KexiSearchAndReplaceViewInterface::Options& options, bool next)
+{
+ if (!dataAwareObject() || !dataAwareObject()->data())
+ return cancelled;
+
+// const KexiDataAwareObjectInterface::FindAndReplaceOptions options(dlg->options());
+/* if (res == KexiFindDialog::Find) {*/
+// QVariant valueToFind(dlg->valueToFind());
+ return dataAwareObject()->find( valueToFind, options, next );
+/*
+//! @todo result...
+
+ }
+ else if (res == KexiFindDialog::Replace) {
+//! @todo
+ }
+ else if (res == KexiFindDialog::ReplaceAll) {
+//! @todo
+ }
+ */
+}
+
+tristate KexiDataAwareView::findNextAndReplace(const QVariant& valueToFind,
+ const QVariant& replacement,
+ const KexiSearchAndReplaceViewInterface::Options& options, bool replaceAll)
+{
+ if (!dataAwareObject() || !dataAwareObject()->data())
+ return cancelled;
+
+ return dataAwareObject()->findNextAndReplace(valueToFind, replacement, options, replaceAll);
+}
+
+/*
+void KexiDataAwareView::editFindNext()
+{
+ //! @todo reuse code from editFind()
+}
+
+void KexiDataAwareView::editFindPrevious()
+{
+ //! @todo reuse code from editFind()
+}
+
+void KexiDataAwareView::editReplace()
+{
+ //! @todo editReplace()
+ //! @todo reuse code from editFind()
+ // When ready, update KexiDataAwareView::initActions() and KexiMainWindowImpl
+}*/
+
+#include "kexidataawareview.moc"
diff --git a/kexi/widget/kexidataawareview.h b/kexi/widget/kexidataawareview.h
new file mode 100644
index 00000000..8f1d369f
--- /dev/null
+++ b/kexi/widget/kexidataawareview.h
@@ -0,0 +1,117 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-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 KEXIDATAAWAREVIEW_H
+#define KEXIDATAAWAREVIEW_H
+
+#include <kexiviewbase.h>
+#include <kexisearchandreplaceiface.h>
+
+class KexiDataAwareObjectInterface;
+class KexiSharedActionClient;
+
+/*! @short Provides a view displaying record-based data.
+
+ The KexiDataAwareView is used to implement differently-looking views
+ for displaying record-based data in a consistent way:
+ - tabular data views
+ - form data view
+
+ Action implementations like data editing and deleting are shared for different
+ view types to keep even better consistency.
+ The view also implements KexiSearchAndReplaceViewInterface to support search/replace features
+ used by shared KexiFindDialog.
+*/
+class KEXIEXTWIDGETS_EXPORT KexiDataAwareView : public KexiViewBase,
+ public KexiSearchAndReplaceViewInterface
+{
+ Q_OBJECT
+
+ public:
+ KexiDataAwareView(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0);
+
+ QWidget* mainWidget();
+
+ virtual QSize minimumSizeHint() const;
+
+ virtual QSize sizeHint() const;
+
+ KexiDataAwareObjectInterface* dataAwareObject() const { return m_dataAwareObject; }
+
+ /*! Sets up data for find/replace dialog, based on view's data model.
+ Implemented for KexiSearchAndReplaceViewInterface. */
+ virtual bool setupFindAndReplace(QStringList& columnNames, QStringList& columnCaptions,
+ QString& currentColumnName);
+
+ /*! Finds \a valueToFind within the view.
+ Implemented for KexiSearchAndReplaceViewInterface. */
+ virtual tristate find(const QVariant& valueToFind,
+ const KexiSearchAndReplaceViewInterface::Options& options, bool next);
+
+ /*! Finds \a valueToFind within the view and replaces with \a replacement.
+ Implemented for KexiSearchAndReplaceViewInterface. */
+ virtual tristate findNextAndReplace(const QVariant& valueToFind,
+ const QVariant& replacement,
+ const KexiSearchAndReplaceViewInterface::Options& options, bool replaceAll);
+
+ public slots:
+ void deleteAllRows();
+ void deleteCurrentRow();
+ void deleteAndStartEditCurrentCell();
+ void startEditOrToggleValue();
+ bool acceptRowEdit();
+ void cancelRowEdit();
+ void sortAscending();
+ void sortDescending();
+ void copySelection();
+ void cutSelection();
+ void paste();
+ void slotGoToFirstRow();
+ void slotGoToPreviusRow();
+ void slotGoToNextRow();
+ void slotGoToLastRow();
+ void slotGoToNewRow();
+/* void editFind();
+ void slotFind();
+ void editFindNext();
+ void editFindPrevious();
+ void editReplace();*/
+
+ protected slots:
+// void slotCellSelected(const QVariant& v); //!< @internal
+ void slotCellSelected(int col, int row);
+ void reloadActions();
+ void slotUpdateRowActions(int row);
+ void slotClosing(bool& cancel);
+
+ protected:
+ void init( QWidget* viewWidget, KexiSharedActionClient* actionClient,
+ KexiDataAwareObjectInterface* dataAwareObject,
+ // temporary, for KexiFormView in design mode
+ bool noDataAware = false
+ );
+ void initActions();
+ virtual void updateActions(bool activated);
+
+ QWidget* m_internalView;
+ KexiSharedActionClient* m_actionClient;
+ KexiDataAwareObjectInterface* m_dataAwareObject;
+};
+
+#endif
diff --git a/kexi/widget/kexidatasourcecombobox.cpp b/kexi/widget/kexidatasourcecombobox.cpp
new file mode 100644
index 00000000..77dc771f
--- /dev/null
+++ b/kexi/widget/kexidatasourcecombobox.cpp
@@ -0,0 +1,333 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 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.
+*/
+
+#include "kexidatasourcecombobox.h"
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klistbox.h>
+
+#include <kexi.h>
+#include <kexiproject.h>
+#include <keximainwindow.h>
+#include <kexipart.h>
+#include <kexipartmanager.h>
+#include <kexipartinfo.h>
+#include <kexipartitem.h>
+
+#include <kexidb/connection.h>
+
+#ifdef KEXI_SHOW_UNIMPLEMENTED
+#define ADD_DEFINEQUERY_ROW
+#endif
+
+//! @internal
+class KexiDataSourceComboBox::Private
+{
+ public:
+ Private()
+ : tablesCount(0)
+ , prevIndex(-1)
+ , showTables(true)
+ , showQueries(true)
+ {
+ }
+ int firstTableIndex() const {
+ int index = 1; //skip empty row
+#ifdef ADD_DEFINEQUERY_ROW
+ index++; /*skip 'define query' row*/
+#endif
+ return index;
+ }
+ int firstQueryIndex() const {
+ return firstTableIndex() + tablesCount;
+ }
+
+ QGuardedPtr<KexiProject> prj;
+ QPixmap tableIcon, queryIcon;
+ int tablesCount;
+ int prevIndex; //!< Used in slotActivated()
+ bool showTables : 1;
+ bool showQueries : 1;
+};
+
+//------------------------
+
+KexiDataSourceComboBox::KexiDataSourceComboBox(QWidget *parent, const char *name)
+ : KComboBox(true/*rw*/, parent, name)
+ , d(new Private())
+{
+ setInsertionPolicy(NoInsertion);
+ setCompletionMode(KGlobalSettings::CompletionPopupAuto);
+ setSizeLimit( 16 );
+ connect(this, SIGNAL(activated(int)), this, SLOT(slotActivated(int)));
+ connect(this, SIGNAL(returnPressed(const QString &)), this, SLOT(slotReturnPressed(const QString &)));
+
+ d->tableIcon = SmallIcon("table");
+ d->queryIcon = SmallIcon("query");
+}
+
+KexiDataSourceComboBox::~KexiDataSourceComboBox()
+{
+ delete d;
+}
+
+KexiProject* KexiDataSourceComboBox::project() const
+{
+ return d->prj;
+}
+
+void KexiDataSourceComboBox::setProject(KexiProject *prj, bool showTables, bool showQueries)
+{
+ if ((KexiProject*)d->prj == prj)
+ return;
+
+ if (d->prj) {
+ disconnect(d->prj, 0, this, 0);
+ }
+ d->prj = prj;
+ d->showTables = showTables;
+ d->showQueries = showQueries;
+ clear();
+ d->tablesCount = 0;
+ if (!d->prj)
+ return;
+
+ //needed for updating contents of the combo box
+ connect(d->prj, SIGNAL(newItemStored(KexiPart::Item&)),
+ this, SLOT(slotNewItemStored(KexiPart::Item&)));
+ connect(d->prj, SIGNAL(itemRemoved(const KexiPart::Item&)),
+ this, SLOT(slotItemRemoved(const KexiPart::Item&)));
+ connect(d->prj, SIGNAL(itemRenamed(const KexiPart::Item&, const QCString&)),
+ this, SLOT(slotItemRenamed(const KexiPart::Item&, const QCString&)));
+
+ KexiDB::Connection *conn = d->prj->dbConnection();
+ if (!conn)
+ return;
+
+ //special item: empty
+ insertItem("");
+#ifdef ADD_DEFINEQUERY_ROW
+ //special item: define query
+ insertItem(i18n("Define Query..."));
+#endif
+
+ KCompletion *comp = completionObject();
+
+ if (d->showTables) {
+ //tables
+ KexiPart::Info* partInfo = Kexi::partManager().infoForMimeType("kexi/table");
+ if (!partInfo)
+ return;
+ KexiPart::ItemList list;
+ prj->getSortedItems(list, partInfo);
+ list.sort();
+ d->tablesCount = 0;
+ for (KexiPart::ItemListIterator it(list); it.current(); ++it, d->tablesCount++) {
+ insertItem(d->tableIcon, it.current()->name()); //or caption()?
+ comp->addItem(it.current()->name());
+ }
+ }
+
+ if (d->showQueries) {
+ //queries
+ KexiPart::Info* partInfo = Kexi::partManager().infoForMimeType("kexi/query");
+ if (!partInfo)
+ return;
+ KexiPart::ItemList list;
+ prj->getSortedItems(list, partInfo);
+ list.sort();
+ for (KexiPart::ItemListIterator it(list); it.current(); ++it) {
+ insertItem(d->queryIcon, it.current()->name()); //or caption()?
+ comp->addItem(it.current()->name());
+ }
+ }
+// setCurrentText("");
+ setCurrentItem(0);
+}
+
+void KexiDataSourceComboBox::setDataSource(const QString& mimeType, const QString& name)
+{
+ if (name.isEmpty()) {
+ clearEdit();
+ setCurrentItem(0);
+ d->prevIndex = -1;
+ emit dataSourceChanged();
+ return;
+ }
+
+ QString mt(mimeType);
+ if (mimeType.isEmpty())
+ mt="kexi/table";
+ int i = findItem(mt, name);
+ if (i==-1) {
+ if (mimeType.isEmpty())
+ i = findItem("kexi/query", name);
+ if (i==-1) {
+ setCurrentItem(0);
+ return;
+ }
+ }
+ setCurrentItem(i);
+ slotActivated(i);
+}
+
+void KexiDataSourceComboBox::slotNewItemStored(KexiPart::Item& item)
+{
+ QString name(item.name());
+ //insert a new item, maintaining sort order and splitting to tables and queries
+ if (item.mimeType()=="kexi/table") {
+ int i = 1; /*skip empty row*/
+#ifdef ADD_DEFINEQUERY_ROW
+ i++; /*skip 'define query' row*/
+#endif
+ for (; i < d->firstQueryIndex() && name>=text(i); i++)
+ ;
+ insertItem(d->tableIcon, name, i);
+ completionObject()->addItem(name);
+ d->tablesCount++;
+ }
+ else if (item.mimeType()=="kexi/query") {
+ int i;
+ for (i=d->firstQueryIndex(); i<count() && name>=text(i); i++)
+ ;
+ insertItem(d->queryIcon, name, i);
+ completionObject()->addItem(name);
+ }
+}
+
+int KexiDataSourceComboBox::findItem(const QString& mimeType, const QString& name)
+{
+ int i, end;
+ if (mimeType=="kexi/table") {
+ i = 0;
+#ifdef ADD_DEFINEQUERY_ROW
+ i++; //skip 'define query'
+#endif
+ end = d->firstQueryIndex();
+ }
+ else if (mimeType=="kexi/query") {
+ i = d->firstQueryIndex();
+ end = count();
+ }
+ else
+ return -1;
+
+ QString nameString(name);
+
+ for (; i<end; i++)
+ if (text(i)==nameString)
+ return i;
+
+ return -1;
+}
+
+void KexiDataSourceComboBox::slotItemRemoved(const KexiPart::Item& item)
+{
+ const int i = findItem(item.mimeType(), item.name());
+ if (i==-1)
+ return;
+ removeItem(i);
+ completionObject()->removeItem(item.name());
+ if (item.mimeType()=="kexi/table")
+ d->tablesCount--;
+#if 0 //disabled because even invalid data source can be set
+ if (currentItem()==i) {
+ if (i==(count()-1))
+ setCurrentItem(i-1);
+ else
+ setCurrentItem(i);
+ }
+#endif
+}
+
+void KexiDataSourceComboBox::slotItemRenamed(const KexiPart::Item& item, const QCString& oldName)
+{
+ const int i = findItem(item.mimeType(), QString(oldName));
+ if (i==-1)
+ return;
+ changeItem(item.name(), i);
+ completionObject()->removeItem(QString(oldName));
+ completionObject()->addItem(item.name());
+ setCurrentText(oldName); //still keep old name
+}
+
+void KexiDataSourceComboBox::slotActivated( int index )
+{
+ if (index >= d->firstTableIndex() && index < count() && d->prevIndex!=currentItem()) {
+ d->prevIndex = currentItem();
+ emit dataSourceChanged();
+ }
+}
+
+QString KexiDataSourceComboBox::selectedMimeType() const
+{
+ if (selectedName().isEmpty())
+ return "";
+ const int index = currentItem();
+ if (index >= d->firstTableIndex() && index < (int)d->firstQueryIndex())
+ return "kexi/table";
+ else if (index >= (int)d->firstQueryIndex() && index < count())
+ return "kexi/query";
+ return "";
+}
+
+QString KexiDataSourceComboBox::selectedName() const
+{
+ if (isSelectionValid())
+ return text(currentItem());
+ return currentText();
+}
+
+bool KexiDataSourceComboBox::isSelectionValid() const
+{
+ const int index = currentItem();
+ return index >= d->firstTableIndex() && index < count() && text(index)==currentText();
+}
+
+void KexiDataSourceComboBox::slotReturnPressed(const QString & text)
+{
+ //text is available: select item for this text:
+ bool changed = false;
+ if (text.isEmpty() && 0!=currentItem()) {
+ setCurrentItem(0);
+ changed = true;
+ }
+ else {
+ QListBoxItem *item = listBox()->findItem( text, Qt::ExactMatch );
+ if (item) {
+ int index = listBox()->index( item );
+ //if (index < d->firstTableIndex())
+ if (index>=0 && index!=currentItem()) {
+ setCurrentItem( index );
+ changed = true;
+ }
+ }
+ }
+ if (changed)
+ emit dataSourceChanged();
+}
+
+void KexiDataSourceComboBox::focusOutEvent( QFocusEvent *e )
+{
+ KComboBox::focusOutEvent( e );
+ slotReturnPressed(currentText());
+}
+
+#include "kexidatasourcecombobox.moc"
diff --git a/kexi/widget/kexidatasourcecombobox.h b/kexi/widget/kexidatasourcecombobox.h
new file mode 100644
index 00000000..01a02d03
--- /dev/null
+++ b/kexi/widget/kexidatasourcecombobox.h
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 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 KEXIDATASOURCECOMBOBOX_H
+#define KEXIDATASOURCECOMBOBOX_H
+
+#include <kcombobox.h>
+
+class KexiProject;
+namespace KexiPart {
+ class Item;
+}
+
+/**
+ * A combo box listing availabe data sources (tables and queries)
+ * with icons. "Define query..." item can be also prepended.
+ */
+class KEXIEXTWIDGETS_EXPORT KexiDataSourceComboBox : public KComboBox
+{
+ Q_OBJECT
+
+ public:
+ KexiDataSourceComboBox(QWidget *parent, const char *name=0);
+ ~KexiDataSourceComboBox();
+
+ //! \return global project that is used to retrieve schema informationm for this combo box.
+ KexiProject* project() const;
+
+ //! \return name of selected table or query. Can return null string.
+ //! You should use isSelectionValid() to check validity of the input.
+ QString selectedMimeType() const;
+
+ //! \return name of selected table or query. Can return null string or nonexisting name,
+ //! so you should use isSelectionValid() to check validity of the input.
+ QString selectedName() const;
+
+ //! \return true if current selection is valid
+ bool isSelectionValid() const;
+
+ /*! \return index of item of mime type \a mimeType and name \a name.
+ Returs -1 of no such item exists. */
+ int findItem(const QString& mimeType, const QString& name);
+
+ public slots:
+ //! Sets global project that is used to retrieve schema informationm for this combo box.
+ //! Tables visibility can be set using \a showTables queries visibility using \a showQueries.
+ void setProject(KexiProject *prj, bool showTables = true, bool showQueries = true);
+
+ /*! Sets item for data source described by \a mimeType and \a name.
+ If \a mimeType is empty, either "kexi/table" and "kexi/query" are tried. */
+ void setDataSource(const QString& mimeType, const QString& name);
+
+ signals:
+ //! Emitted whenever data source changes.
+ //! Even setting invalid data source or clearing it will emit this signal.
+ void dataSourceChanged();
+
+ protected slots:
+ void slotNewItemStored(KexiPart::Item& item);
+ void slotItemRemoved(const KexiPart::Item& item);
+ void slotItemRenamed(const KexiPart::Item& item, const QCString& oldName);
+ void slotActivated( int index );
+ void slotReturnPressed(const QString & text);
+
+ protected:
+ virtual void focusOutEvent( QFocusEvent *e );
+
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kexi/widget/kexidatatable.cpp b/kexi/widget/kexidatatable.cpp
new file mode 100644
index 00000000..b9a0aeb2
--- /dev/null
+++ b/kexi/widget/kexidatatable.cpp
@@ -0,0 +1,82 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@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 <qlayout.h>
+#include <qlabel.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+
+#include <kexidb/cursor.h>
+
+#include "kexidatatableview.h"
+#include "kexidatatable.h"
+#include "kexidialogbase.h"
+
+KexiDataTable::KexiDataTable(KexiMainWindow *mainWin, QWidget *parent,
+ const char *name, bool dbAware)
+// : KexiViewBase(mainWin, parent, name)
+ : KexiDataAwareView( mainWin, parent, name )
+{
+ KexiTableView *view;
+ if (dbAware)
+ view = new KexiDataTableView(this,
+ QString("%1_datatableview").arg(name ? name : "KexiDataTableView").latin1());
+ else
+ view = new KexiTableView(0, this,
+ QString("%1_tableview").arg(name ? name : "KexiTableView").latin1());
+
+ KexiDataAwareView::init( view, view, view );
+}
+
+KexiDataTable::KexiDataTable(KexiMainWindow *mainWin, QWidget *parent,
+ KexiDB::Cursor *cursor, const char *name)
+ : KexiDataAwareView( mainWin, parent, name )
+{
+ KexiTableView *view = new KexiDataTableView(this, "view", cursor);
+ KexiDataAwareView::init( view, view, view );
+}
+
+KexiDataTable::~KexiDataTable()
+{
+}
+
+void
+KexiDataTable::setData(KexiDB::Cursor *c)
+{
+ if (!dynamic_cast<KexiDataTableView*>(mainWidget()))
+ return;
+ dynamic_cast<KexiDataTableView*>(mainWidget())->setData(c);
+}
+
+void KexiDataTable::filter()
+{
+}
+
+KexiTableView* KexiDataTable::tableView() const
+{
+ return dynamic_cast<KexiTableView*>(m_internalView);
+}
+
+#include "kexidatatable.moc"
diff --git a/kexi/widget/kexidatatable.h b/kexi/widget/kexidatatable.h
new file mode 100644
index 00000000..9fb73225
--- /dev/null
+++ b/kexi/widget/kexidatatable.h
@@ -0,0 +1,80 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@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 KEXIDATATABLE_H
+#define KEXIDATATABLE_H
+
+#include "kexidataawareview.h"
+
+class KexiMainWindow;
+class KexiDataTableView;
+class KexiTableView;
+class KexiTableViewData;
+class KPopupMenu;
+
+namespace KexiDB
+{
+ class Cursor;
+}
+
+/*! @short Provides a data-driven (record-based) tabular view.
+
+ The KexiDataTable can display data provided "by hand"
+ or from KexiDB-compatible database source.
+ @see KexiFormView
+*/
+class KEXIEXTWIDGETS_EXPORT KexiDataTable : public KexiDataAwareView
+{
+ Q_OBJECT
+
+ public:
+ /*! CTOR1: Creates, empty table view that can be initialized later
+ with setData().
+ If \a dbAware is true, table will be db-aware,
+ and KexiDataTableView is used internally.
+ Otherwise, table will be not-db-aware,
+ and KexiTableView is used internally. In the latter case,
+ data can be set by calling tableView()->setData(KexiTableViewData* data). */
+ KexiDataTable(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0,
+ bool dbAware = true);
+
+ /*! CTOR2: Creates db-aware, table view initialized with \a cursor.
+ KexiDataTableView is used internally. */
+ KexiDataTable(KexiMainWindow *mainWin, QWidget *parent,
+ KexiDB::Cursor *cursor, const char *name = 0);
+
+ virtual ~KexiDataTable();
+
+ KexiTableView* tableView() const;
+
+ public slots:
+ /*! Sets data. Only works for db-aware table. */
+ void setData(KexiDB::Cursor *cursor);
+
+ protected slots:
+//! @todo
+ void filter();
+
+ protected:
+ void init();
+};
+
+#endif
diff --git a/kexi/widget/kexidbconnectionwidget.cpp b/kexi/widget/kexidbconnectionwidget.cpp
new file mode 100644
index 00000000..5b1b4538
--- /dev/null
+++ b/kexi/widget/kexidbconnectionwidget.cpp
@@ -0,0 +1,407 @@
+/* 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 "kexidbconnectionwidget.h"
+#include "kexidbconnectionwidgetdetailsbase.h"
+
+#include <kexi.h>
+#include <kexiguimsghandler.h>
+#include <kexidb/connection.h>
+#include <kexidb/utils.h>
+#include "kexidbdrivercombobox.h"
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <knuminput.h>
+#include <kpassdlg.h>
+#include <kurlrequester.h>
+#include <ktextedit.h>
+#include <kprogress.h>
+
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qbuttongroup.h>
+#include <qwidgetstack.h>
+#include <qlayout.h>
+#include <qvbox.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qthread.h>
+#include <qradiobutton.h>
+
+//! Templorary hides db list
+//! @todo reenable this when implemented
+#define NO_LOAD_DB_LIST
+
+// @internal
+class KexiDBConnectionWidget::Private
+{
+ public:
+ Private()
+ : connectionOnly(false)
+ {
+ }
+
+ KPushButton *btnSaveChanges, *btnTestConnection;
+ bool connectionOnly : 1;
+};
+
+//---------
+
+KexiDBConnectionWidget::KexiDBConnectionWidget( QWidget* parent, const char* name )
+ : KexiDBConnectionWidgetBase( parent, name )
+ , d(new Private())
+{
+ iconLabel->setPixmap(DesktopIcon("network"));
+
+ QVBoxLayout *driversComboLyr = new QVBoxLayout(frmEngine);
+ m_driversCombo = new KexiDBDriverComboBox(frmEngine, Kexi::driverManager().driversInfo(),
+ KexiDBDriverComboBox::ShowServerDrivers);
+ lblEngine->setBuddy( m_driversCombo );
+ lblEngine->setFocusProxy( m_driversCombo );
+ driversComboLyr->addWidget( m_driversCombo );
+
+#ifdef NO_LOAD_DB_LIST
+ btnLoadDBList->hide();
+#endif
+ btnLoadDBList->setIconSet(SmallIconSet("reload"));
+ QToolTip::add(btnLoadDBList, i18n("Load database list from the server"));
+ QWhatsThis::add(btnLoadDBList,
+ i18n("Loads database list from the server, so you can select one using the \"Name\" combo box."));
+
+ QHBoxLayout *hbox = new QHBoxLayout(frmBottom);
+ hbox->addStretch(2);
+ d->btnSaveChanges = new KPushButton(KGuiItem(i18n("Save Changes"), "filesave",
+ i18n("Save all changes made to this connection information"),
+ i18n("Save all changes made to this connection information. You can later reuse this information.")),
+ frmBottom, "savechanges");
+ hbox->addWidget( d->btnSaveChanges );
+ hbox->addSpacing( KDialogBase::spacingHint() );
+ QWidget::setTabOrder(titleEdit, d->btnSaveChanges);
+ d->btnSaveChanges->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
+
+ d->btnTestConnection = new KPushButton(KGuiItem(i18n("&Test Connection"), "",
+ i18n("Test database connection"),
+ i18n("Tests database connection. You can ensure that valid connection information is provided.")),
+ frmBottom, "testConnection");
+ hbox->addWidget( d->btnTestConnection );
+ QWidget::setTabOrder(d->btnSaveChanges, d->btnTestConnection);
+ d->btnTestConnection->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
+
+ connect( locationBGrp, SIGNAL(clicked(int)), this, SLOT(slotLocationBGrpClicked(int)) );
+ connect( chkPortDefault, SIGNAL(toggled(bool)), this , SLOT(slotCBToggled(bool)) );
+ connect( btnLoadDBList, SIGNAL(clicked()), this, SIGNAL(loadDBList()) );
+ connect( d->btnSaveChanges, SIGNAL(clicked()), this, SIGNAL(saveChanges()) );
+}
+
+KexiDBConnectionWidget::~KexiDBConnectionWidget()
+{
+ delete d;
+}
+
+bool KexiDBConnectionWidget::connectionOnly() const
+{ return d->connectionOnly; }
+
+void KexiDBConnectionWidget::setDataInternal(const KexiProjectData& data, bool connectionOnly,
+ const QString& shortcutFileName)
+{
+ m_data = data;
+ d->connectionOnly = connectionOnly;
+
+ if (d->connectionOnly) {
+ nameLabel->hide();
+ nameCombo->hide();
+ btnLoadDBList->hide();
+ dbGroupBox->setTitle(i18n("Database Connection"));
+ }
+ else {
+ nameLabel->show();
+ nameCombo->show();
+#ifndef NO_LOAD_DB_LIST
+ btnLoadDBList->show();
+#endif
+ nameCombo->setCurrentText(m_data.databaseName());
+ dbGroupBox->setTitle(i18n("Database"));
+ }
+//! @todo what if there's no such driver name?
+ m_driversCombo->setDriverName(m_data.connectionData()->driverName);
+ hostEdit->setText(m_data.connectionData()->hostName);
+ locationBGrp->setButton( m_data.connectionData()->hostName.isEmpty() ? 0 : 1 );
+ slotLocationBGrpClicked( locationBGrp->selectedId() );
+ if (m_data.connectionData()->port!=0) {
+ chkPortDefault->setChecked(false);
+ customPortEdit->setValue(m_data.connectionData()->port);
+ }
+ else {
+ chkPortDefault->setChecked(true);
+/* @todo default port # instead of 0 */
+ customPortEdit->setValue(0);
+ }
+ userEdit->setText(m_data.connectionData()->userName);
+ passwordEdit->setText(m_data.connectionData()->password);
+ if (d->connectionOnly)
+ titleEdit->setText(m_data.connectionData()->caption);
+ else
+ titleEdit->setText(m_data.caption());
+
+ if (shortcutFileName.isEmpty()) {
+ d->btnSaveChanges->hide();
+// chkSavePassword->hide();
+ }
+ else {
+ if (!QFileInfo(shortcutFileName).isWritable()) {
+ d->btnSaveChanges->setEnabled(false);
+ }
+ }
+// chkSavePassword->setChecked(!m_data.connectionData()->password.isEmpty());
+ chkSavePassword->setChecked(m_data.connectionData()->savePassword);
+ adjustSize();
+}
+
+void KexiDBConnectionWidget::setData(const KexiProjectData& data, const QString& shortcutFileName)
+{
+ setDataInternal(data, false /*!connectionOnly*/, shortcutFileName);
+}
+
+void KexiDBConnectionWidget::setData(const KexiDB::ConnectionData& data, const QString& shortcutFileName)
+{
+ KexiProjectData pdata(data);
+ setDataInternal(pdata, true /*connectionOnly*/, shortcutFileName);
+}
+
+KPushButton* KexiDBConnectionWidget::saveChangesButton() const
+{
+ return d->btnSaveChanges;
+}
+
+KPushButton* KexiDBConnectionWidget::testConnectionButton() const
+{
+ return d->btnTestConnection;
+}
+
+KexiProjectData KexiDBConnectionWidget::data()
+{
+ return m_data;
+}
+
+void KexiDBConnectionWidget::slotLocationBGrpClicked(int id)
+{
+ if (id != 0 && id != 1) //only support local/remove radio buttons
+ return;
+ hostLbl->setEnabled(id==1);
+ hostEdit->setEnabled(id==1);
+}
+
+void KexiDBConnectionWidget::slotCBToggled(bool on)
+{
+ if (sender()==chkPortDefault) {
+ customPortEdit->setEnabled(!on);
+ }
+// else if (sender()==chkSocketDefault) {
+// customSocketEdit->setEnabled(!on);
+// }
+}
+
+//-----------
+
+KexiDBConnectionTabWidget::KexiDBConnectionTabWidget( QWidget* parent, const char* name )
+ : KTabWidget( parent, name )
+{
+ mainWidget = new KexiDBConnectionWidget( this, "mainWidget" );
+ mainWidget->layout()->setMargin(KDialog::marginHint());
+ addTab( mainWidget, i18n("Parameters") );
+
+// QVBox *page2 = new QVBox(this);
+// page2->setMargin(KDialog::marginHint());
+// page2->setSpacing(KDialog::spacingHint());
+// QLabel *lbl = new QLabel(i18n("&Description:"), page2);
+// m_descriptionEdit = new KTextEdit(page2);
+// lbl->setBuddy(m_descriptionEdit);
+ detailsWidget = new KexiDBConnectionWidgetDetailsBase(this, "detailsWidget");
+ addTab( detailsWidget, i18n("Details") );
+
+ connect( mainWidget->testConnectionButton(), SIGNAL(clicked()), this, SLOT(slotTestConnection()) );
+}
+
+KexiDBConnectionTabWidget::~KexiDBConnectionTabWidget()
+{
+}
+
+void KexiDBConnectionTabWidget::setData(const KexiProjectData& data, const QString& shortcutFileName)
+{
+ mainWidget->setData( data, shortcutFileName );
+ detailsWidget->chkUseSocket->setChecked( data.constConnectionData()->useLocalSocketFile );
+ detailsWidget->customSocketEdit->setURL( data.constConnectionData()->localSocketFileName );
+ detailsWidget->customSocketEdit->setEnabled( detailsWidget->chkUseSocket->isChecked() );
+ detailsWidget->chkSocketDefault->setChecked( data.constConnectionData()->localSocketFileName.isEmpty() );
+ detailsWidget->chkSocketDefault->setEnabled( detailsWidget->chkUseSocket->isChecked() );
+ detailsWidget->descriptionEdit->setText( data.description() );
+}
+
+void KexiDBConnectionTabWidget::setData(const KexiDB::ConnectionData& data,
+ const QString& shortcutFileName)
+{
+ mainWidget->setData( data, shortcutFileName );
+ detailsWidget->chkUseSocket->setChecked( data.useLocalSocketFile );
+ detailsWidget->customSocketEdit->setURL( data.localSocketFileName );
+ detailsWidget->customSocketEdit->setEnabled( detailsWidget->chkUseSocket->isChecked() );
+ detailsWidget->chkSocketDefault->setChecked( data.localSocketFileName.isEmpty() );
+ detailsWidget->chkSocketDefault->setEnabled( detailsWidget->chkUseSocket->isChecked() );
+ detailsWidget->descriptionEdit->setText( data.description );
+}
+
+KexiProjectData KexiDBConnectionTabWidget::currentProjectData()
+{
+ KexiProjectData data;
+
+//! @todo check if that's database of connection shortcut. Now we're assuming db shortcut only!
+
+ // collect data from the form's fields
+// if (d->isDatabaseShortcut) {
+ if (mainWidget->connectionOnly()) {
+ data.connectionData()->caption = mainWidget->titleEdit->text();
+ data.setCaption( QString::null );
+ data.connectionData()->description = detailsWidget->descriptionEdit->text();
+ data.setDatabaseName( QString::null );
+ }
+ else {
+ data.connectionData()->caption = QString::null; /* connection name is not specified... */
+ data.setCaption( mainWidget->titleEdit->text() );
+ data.setDescription( detailsWidget->descriptionEdit->text() );
+ data.setDatabaseName( mainWidget->nameCombo->currentText() );
+ }
+// }
+/* else {
+ data.setCaption( QString::null );
+ data.connectionData()->connName = config.readEntry("caption");
+ data.setDescription( QString::null );
+ data.connectionData()->description = config.readEntry("comment");
+ data.setDatabaseName( QString::null );
+ }*/
+ data.connectionData()->driverName = mainWidget->driversCombo()->selectedDriverName();
+
+/* if (data.connectionData()->driverName.isEmpty()) {
+ //ERR: "No valid "engine" field specified for %1 section" group
+ return false;
+ }*/
+ data.connectionData()->hostName =
+ (mainWidget->remotehostRBtn->isChecked()/*remote*/) ? mainWidget->hostEdit->text()
+ : QString::null;
+ data.connectionData()->port = mainWidget->chkPortDefault->isChecked()
+ ? 0 : mainWidget->customPortEdit->value();
+ data.connectionData()->localSocketFileName = detailsWidget->chkSocketDefault->isChecked()
+ ? QString::null : detailsWidget->customSocketEdit->url();
+ data.connectionData()->useLocalSocketFile = detailsWidget->chkUseSocket->isChecked();
+//UNSAFE!!!!
+ data.connectionData()->userName = mainWidget->userEdit->text();
+ data.connectionData()->password = mainWidget->passwordEdit->text();
+ data.connectionData()->savePassword = mainWidget->chkSavePassword->isChecked();
+/* @todo add "options=", eg. as string list? */
+ return data;
+}
+
+bool KexiDBConnectionTabWidget::savePasswordOptionSelected() const
+{
+ return mainWidget->chkSavePassword->isChecked();
+}
+
+
+
+
+void KexiDBConnectionTabWidget::slotTestConnection()
+{
+ KexiGUIMessageHandler msgHandler;
+ KexiDB::connectionTestDialog(this, *currentProjectData().connectionData(),
+ msgHandler);
+}
+
+//--------
+
+//! @todo set proper help ctxt ID
+
+KexiDBConnectionDialog::KexiDBConnectionDialog(const KexiProjectData& data,
+ const QString& shortcutFileName, const KGuiItem& acceptButtonGuiItem)
+ : KDialogBase(0, "dlg", true, i18n("Open Database"),
+ KDialogBase::User1|KDialogBase::Cancel|KDialogBase::Help,
+ KDialogBase::User1, false,
+ acceptButtonGuiItem.text().isEmpty()
+ ? KGuiItem(i18n("&Open"), "fileopen", i18n("Open Database Connection"))
+ : acceptButtonGuiItem
+ )
+{
+ tabWidget = new KexiDBConnectionTabWidget(this, "tabWidget");
+ tabWidget->setData(data, shortcutFileName);
+ init();
+}
+
+KexiDBConnectionDialog::KexiDBConnectionDialog(const KexiDB::ConnectionData& data,
+ const QString& shortcutFileName, const KGuiItem& acceptButtonGuiItem)
+ : KDialogBase(0, "dlg", true, i18n("Connect to a Database Server"),
+ KDialogBase::User1|KDialogBase::Cancel|KDialogBase::Help,
+ KDialogBase::User1, false,
+ acceptButtonGuiItem.text().isEmpty()
+ ? KGuiItem(i18n("&Open"), "fileopen", i18n("Open Database Connection"))
+ : acceptButtonGuiItem
+ )
+{
+ tabWidget = new KexiDBConnectionTabWidget(this, "tabWidget");
+ tabWidget->setData(data, shortcutFileName);
+ init();
+}
+
+KexiDBConnectionDialog::~KexiDBConnectionDialog()
+{
+}
+
+void KexiDBConnectionDialog::init()
+{
+ connect( this, SIGNAL(user1Clicked()), this, SLOT(accept()));
+ setMainWidget(tabWidget);
+ connect(tabWidget->mainWidget, SIGNAL(saveChanges()), this, SIGNAL(saveChanges()));
+ connect(tabWidget, SIGNAL(testConnection()), this, SIGNAL(testConnection()));
+
+ adjustSize();
+ resize(width(), tabWidget->height());
+ if (tabWidget->mainWidget->connectionOnly())
+ tabWidget->mainWidget->driversCombo()->setFocus();
+ else if (tabWidget->mainWidget->nameCombo->currentText().isEmpty())
+ tabWidget->mainWidget->nameCombo->setFocus();
+ else if (tabWidget->mainWidget->userEdit->text().isEmpty())
+ tabWidget->mainWidget->userEdit->setFocus();
+ else if (tabWidget->mainWidget->passwordEdit->text().isEmpty())
+ tabWidget->mainWidget->passwordEdit->setFocus();
+ else //back
+ tabWidget->mainWidget->nameCombo->setFocus();
+}
+
+KexiProjectData KexiDBConnectionDialog::currentProjectData()
+{ return tabWidget->currentProjectData(); }
+
+bool KexiDBConnectionDialog::savePasswordOptionSelected() const
+{ return tabWidget->savePasswordOptionSelected(); }
+
+KexiDBConnectionWidget* KexiDBConnectionDialog::mainWidget() const
+{ return tabWidget->mainWidget; }
+
+KexiDBConnectionWidgetDetailsBase* KexiDBConnectionDialog::detailsWidget() const
+{ return tabWidget->detailsWidget; }
+
+#include "kexidbconnectionwidget.moc"
+
diff --git a/kexi/widget/kexidbconnectionwidget.h b/kexi/widget/kexidbconnectionwidget.h
new file mode 100644
index 00000000..dd8559e5
--- /dev/null
+++ b/kexi/widget/kexidbconnectionwidget.h
@@ -0,0 +1,179 @@
+/* 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 KEXIDBCONNECTIONWIDGET_H
+#define KEXIDBCONNECTIONWIDGET_H
+
+#include "kexidbconnectionwidgetbase.h"
+
+#include <kexiprojectdata.h>
+
+#include <ktabwidget.h>
+#include <kdialogbase.h>
+
+class KTextEdit;
+class KPushButton;
+class KexiDBDriverComboBox;
+class KexiDBConnectionWidgetDetailsBase;
+class KexiDBConnectionTabWidget;
+
+class KEXIEXTWIDGETS_EXPORT KexiDBConnectionWidget : public KexiDBConnectionWidgetBase
+{
+ Q_OBJECT
+
+ public:
+ KexiDBConnectionWidget( QWidget* parent = 0, const char* name = 0 );
+ virtual ~KexiDBConnectionWidget();
+
+ /*! Sets project data \a data.
+ \a shortcutFileName is only used to check if the file is writable
+ (if no, "save changes" button will be disabled). */
+ void setData(const KexiProjectData& data, const QString& shortcutFileName = QString::null);
+
+ /*! Sets connection data \a data.
+ \a shortcutFileName is only used to check if the file is writable
+ (if no, "save changes" button will be disabled). */
+ void setData(const KexiDB::ConnectionData& data, const QString& shortcutFileName = QString::null);
+
+ KexiProjectData data();
+
+ //! \return a pointer to 'save changes' button. You can call hide() for this to hide it.
+ KPushButton* saveChangesButton() const;
+
+ //! \return a pointer to 'test connection' button. You can call hide() for this to hide it.
+ KPushButton* testConnectionButton() const;
+
+ KexiDBDriverComboBox *driversCombo() const { return m_driversCombo; }
+
+ //! \return true if only connection data is managed by this widget
+ bool connectionOnly() const;
+
+ signals:
+ //! emitted when data saving is needed
+ void saveChanges();
+
+ void loadDBList();
+
+ protected slots:
+ void slotLocationBGrpClicked(int id);
+ void slotCBToggled(bool on);
+ virtual void languageChange() { KexiDBConnectionWidgetBase::languageChange(); }
+
+ protected:
+ void setDataInternal(const KexiProjectData& data, bool connectionOnly,
+ const QString& shortcutFileName);
+
+ KexiProjectData m_data;
+ KexiDBDriverComboBox *m_driversCombo;
+
+ class Private;
+ Private *d;
+
+// friend class KexiDBConnectionTabWidget;
+};
+
+class KEXIEXTWIDGETS_EXPORT KexiDBConnectionTabWidget : public KTabWidget
+{
+ Q_OBJECT
+
+ public:
+ KexiDBConnectionTabWidget( QWidget* parent = 0, const char* name = 0 );
+ virtual ~KexiDBConnectionTabWidget();
+
+ /*! Sets connection data \a data.
+ \a shortcutFileName is only used to check if the file is writable
+ (if no, "save changes" button will be disabled). */
+ void setData(const KexiProjectData& data, const QString& shortcutFileName = QString::null);
+ void setData(const KexiDB::ConnectionData& data, const QString& shortcutFileName = QString::null);
+ KexiProjectData currentProjectData();
+
+ //! \return true if 'save password' option is selected
+ bool savePasswordOptionSelected() const;
+
+ signals:
+ //! emitted when test connection is needed
+ void testConnection();
+
+ protected slots:
+ void slotTestConnection();
+
+ protected:
+ KexiDBConnectionWidget *mainWidget;
+ KexiDBConnectionWidgetDetailsBase* detailsWidget;
+
+ friend class KexiDBConnectionDialog;
+};
+
+class KEXIEXTWIDGETS_EXPORT KexiDBConnectionDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ /*! Creates a new connection dialog for project data \a data.
+ Not only connection data is visible but also database name and and title.
+ \a shortcutFileName is only used to check if the shortcut file is writable
+ (if no, "save changes" button will be disabled).
+ The shortcut file is in .KEXIS format.
+ Connect to saveChanges() signal to react on saving changes.
+ If \a shortcutFileName is empty, the button will be hidden.
+ \a acceptButtonGuiItem allows to override default "Open" button's appearance. */
+ KexiDBConnectionDialog(const KexiProjectData& data,
+ const QString& shortcutFileName = QString::null,
+ const KGuiItem& acceptButtonGuiItem = KGuiItem(""));
+
+ /*! Creates a new connection dialog for connection data \a data.
+ Only connection data is visible: database name and and title fields are hidden.
+ \a shortcutFileName is only used to check if the shortcut file is writable
+ (if no, "save changes" button will be disabled).
+ The shortcut file is in .KEXIC format.
+ See above constructor for more details. */
+ KexiDBConnectionDialog(const KexiDB::ConnectionData& data,
+ const QString& shortcutFileName = QString::null,
+ const KGuiItem& acceptButtonGuiItem = KGuiItem(""));
+
+ ~KexiDBConnectionDialog();
+
+ /*! \return project data displayed within the dialog.
+ Information about database name and title can be empty if the dialog
+ contain only a connection data (if second constructor was used). */
+ KexiProjectData currentProjectData();
+
+ //! \return true if 'save password' option is selected
+ bool savePasswordOptionSelected() const;
+
+ KexiDBConnectionWidget *mainWidget() const;
+ KexiDBConnectionWidgetDetailsBase* detailsWidget() const;
+
+ signals:
+ //! emitted when data saving is needed
+ void saveChanges();
+
+ //! emitted when test connection is needed
+ void testConnection();
+
+ void loadDBList();
+
+ protected:
+ KexiDBConnectionTabWidget *tabWidget;
+
+ private:
+ void init();
+};
+
+#endif // KEXIDBCONNECTIONWIDGET_H
diff --git a/kexi/widget/kexidbconnectionwidgetbase.ui b/kexi/widget/kexidbconnectionwidgetbase.ui
new file mode 100644
index 00000000..70fcddca
--- /dev/null
+++ b/kexi/widget/kexidbconnectionwidgetbase.ui
@@ -0,0 +1,464 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KexiDBConnectionWidgetBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KexiDBConnectionWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>381</width>
+ <height>395</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="3" colspan="1">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>iconLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>48</width>
+ <height>48</height>
+ </size>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>331</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <spacer row="4" column="1">
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>4</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QButtonGroup" row="0" column="1">
+ <property name="name">
+ <cstring>locationBGrp</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Database Server</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="1" column="5" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>41</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KIntNumInput" row="3" column="4" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>customPortEdit</cstring>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>portLbl</cstring>
+ </property>
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="2" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>hostEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>localhostRBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Local server</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="3" column="6">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>90</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>hostLbl</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Hostname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>hostEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>chkPortDefault</cstring>
+ </property>
+ <property name="text">
+ <string>Default</string>
+ <comment>port: default</comment>
+ </property>
+ <property name="buttonGroupId">
+ <number>2</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="3" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>remotehostRBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Remote server</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lblEngine</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>TabFocus</enum>
+ </property>
+ <property name="text">
+ <string>&amp;Engine:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="QFrame" row="0" column="1" rowspan="1" colspan="6">
+ <property name="name">
+ <cstring>frmEngine</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="1">
+ <property name="name">
+ <cstring>authenticationGBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Authentication</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>userLbl</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Username:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>userEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>userEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>passwordLbl</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Password:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passwordEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>passwordEdit</cstring>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>chkSavePassword</cstring>
+ </property>
+ <property name="text">
+ <string>Save password in the shortcut file</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="1">
+ <property name="name">
+ <cstring>dbGroupBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Database</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>nameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameCombo</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>titleEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>titleLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Title (optional):</string>
+ </property>
+ <property name="alignment">
+ <set>AlignTop</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>titleEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="0" column="2">
+ <property name="name">
+ <cstring>btnLoadDBList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>nameCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="insertionPolicy">
+ <enum>NoInsertion</enum>
+ </property>
+ <property name="autoCompletion">
+ <bool>true</bool>
+ </property>
+ <property name="trapReturnKey">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer13</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>80</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>frmBottom</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>lblEngine</tabstop>
+ <tabstop>localhostRBtn</tabstop>
+ <tabstop>hostEdit</tabstop>
+ <tabstop>chkPortDefault</tabstop>
+ <tabstop>customPortEdit</tabstop>
+ <tabstop>userEdit</tabstop>
+ <tabstop>passwordEdit</tabstop>
+ <tabstop>chkSavePassword</tabstop>
+ <tabstop>nameCombo</tabstop>
+ <tabstop>btnLoadDBList</tabstop>
+ <tabstop>titleEdit</tabstop>
+ <tabstop>frmEngine</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kexi/widget/kexidbconnectionwidgetdetailsbase.ui b/kexi/widget/kexidbconnectionwidgetdetailsbase.ui
new file mode 100644
index 00000000..b729c9bc
--- /dev/null
+++ b/kexi/widget/kexidbconnectionwidgetdetailsbase.ui
@@ -0,0 +1,194 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KexiDBConnectionWidgetDetailsBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KexiDBConnectionWidgetDetailsBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>297</width>
+ <height>171</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>chkUseSocket</cstring>
+ </property>
+ <property name="text">
+ <string>Use socket &amp;file instead of TCP/IP port:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkSocketDefault</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Default</string>
+ <comment>socket: default</comment>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>customSocketEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer row="0" column="1">
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>129</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="KTextEdit" row="1" column="0">
+ <property name="name">
+ <cstring>descriptionEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Description:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>descriptionEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>chkSocketDefault</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>KexiDBConnectionWidgetDetailsBase</receiver>
+ <slot>slotCBToggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkUseSocket</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>KexiDBConnectionWidgetDetailsBase</receiver>
+ <slot>slotCBToggled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>customSocketEdit</tabstop>
+ <tabstop>descriptionEdit</tabstop>
+ <tabstop>chkUseSocket</tabstop>
+ <tabstop>chkSocketDefault</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">kexidbconnectionwidgetdetailsbase.ui.h</include>
+</includes>
+<slots>
+ <slot access="protected">slotCBToggled( bool on )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kexi/widget/kexidbconnectionwidgetdetailsbase.ui.h b/kexi/widget/kexidbconnectionwidgetdetailsbase.ui.h
new file mode 100644
index 00000000..90d6a4b8
--- /dev/null
+++ b/kexi/widget/kexidbconnectionwidgetdetailsbase.ui.h
@@ -0,0 +1,29 @@
+/* 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.
+ */
+
+void KexiDBConnectionWidgetDetailsBase::slotCBToggled( bool on )
+{
+ if (sender()==chkSocketDefault) {
+ customSocketEdit->setEnabled( !on );
+ }
+ else if (sender()==chkUseSocket) {
+ customSocketEdit->setEnabled( on && !chkSocketDefault->isChecked() );
+ chkSocketDefault->setEnabled( on );
+ }
+}
diff --git a/kexi/widget/kexidbdrivercombobox.cpp b/kexi/widget/kexidbdrivercombobox.cpp
new file mode 100644
index 00000000..e3431531
--- /dev/null
+++ b/kexi/widget/kexidbdrivercombobox.cpp
@@ -0,0 +1,92 @@
+/* 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.
+*/
+
+#include "kexidbdrivercombobox.h"
+
+#include <qlistbox.h>
+
+#include <kiconloader.h>
+
+KexiDBDriverComboBox::KexiDBDriverComboBox(QWidget* parent, const KexiDB::Driver::InfoMap& driversInfo,
+ Options options)
+ : KComboBox(parent, "KexiDBDriverComboBox")
+{
+ //retrieve list of drivers and sort it: file-based first, then server-based
+ QStringList captionsForFileBasedDrivers, captionsForServerBasedDrivers;
+ QMap<QString,QString> fileBasedDriversDict, serverBasedDriversDict; //a map from caption to name
+ foreach(KexiDB::Driver::InfoMap::ConstIterator, it, driversInfo) {
+ if (it.data().fileBased) {
+ captionsForFileBasedDrivers += it.data().caption;
+ fileBasedDriversDict[it.data().caption] = it.data().name.lower();
+ }
+ else {
+ captionsForServerBasedDrivers += it.data().caption;
+ serverBasedDriversDict[it.data().caption] = it.data().name.lower();
+ }
+ }
+ captionsForFileBasedDrivers.sort();
+ captionsForServerBasedDrivers.sort();
+ //insert file-based
+ if (options & ShowFileDrivers) {
+ foreach(QStringList::ConstIterator, it, captionsForFileBasedDrivers) {
+ const KexiDB::Driver::Info& info = driversInfo[ fileBasedDriversDict[ *it ] ];
+ //! @todo change this if better icon is available
+ insertItem( SmallIcon("gear"), info.caption );
+ m_driversMap.insert(info.caption, info.name.lower());
+ }
+ }
+ //insert server-based
+ if (options & ShowServerDrivers) {
+ foreach(QStringList::ConstIterator, it, captionsForServerBasedDrivers) {
+ const KexiDB::Driver::Info& info = driversInfo[ serverBasedDriversDict[ *it ] ];
+ //! @todo change this if better icon is available
+ insertItem( SmallIcon("gear"), info.caption );
+ m_driversMap.insert(info.caption, info.name.lower());
+ }
+ }
+// if (listBox())
+// listBox()->sort();
+
+ // Build the names list after sorting
+ for (int i=0; i<count(); i++)
+ m_driverNames += m_driversMap[ text(i) ];
+}
+
+KexiDBDriverComboBox::~KexiDBDriverComboBox()
+{
+}
+
+QString KexiDBDriverComboBox::selectedDriverName() const
+{
+ QMapConstIterator<QString,QString> it( m_driversMap.find( text( currentItem() ) ) );
+ if (it==m_driversMap.constEnd())
+ return QString::null;
+ return it.data();
+}
+
+void KexiDBDriverComboBox::setDriverName(const QString& driverName)
+{
+ int index = m_driverNames.findIndex( driverName.lower() );
+ if (index==-1) {
+ return;
+ }
+ setCurrentItem(index);
+}
+
+#include "kexidbdrivercombobox.moc"
diff --git a/kexi/widget/kexidbdrivercombobox.h b/kexi/widget/kexidbdrivercombobox.h
new file mode 100644
index 00000000..981b67c3
--- /dev/null
+++ b/kexi/widget/kexidbdrivercombobox.h
@@ -0,0 +1,102 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 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 KEXIDBDRIVERCOMBOBOX_H
+#define KEXIDBDRIVERCOMBOBOX_H
+
+#include <qwidget.h>
+#include <qmap.h>
+
+#include <kcombobox.h>
+
+#include <kexidb/driver.h>
+
+//! \brief Combo box widget for selecting a database driver
+/*! This widget provides a combobox for selecting a database driver.
+
+
+\b Usage: \n
+\code
+ KexiDB::DriverManager manager;
+ KexiDB::Driver::InfoMap drvs = manager.driversInfo();
+
+ KexiDBDriverComboBox* combo = new KexiDBDriverComboBox(drvs, true, 0);
+\endcode
+
+A more complete example can be found in
+<a href="http://websvn.kde.org/trunk/koffice/kexi/tests/widgets/kexidbdrivercombotest.cpp?&view=auto">koffice/kexi/tests/widgets/</a>.
+
+*/
+
+class KEXIEXTWIDGETS_EXPORT KexiDBDriverComboBox : public KComboBox
+{
+ Q_OBJECT
+
+ public:
+ enum Options {
+ ShowFileDrivers = 1,
+ ShowServerDrivers = 2,
+ ShowAll = ShowFileDrivers|ShowServerDrivers
+ };
+
+ /*! Constructs a KexiDBDriverComboBox object.
+
+ The combobox is populated with the names of the drivers in
+ \a driversInfo. A suitable value for \a driversInfo can be obtained
+ from KexiDB::DriverManager::driversInfo().
+
+ If \a includeFileBasedDrivers is set to false, then only those drivers
+ that are for database servers (those which have X-Kexi-DriverType=Network
+ in their .desktop file) are shown. */
+ KexiDBDriverComboBox(QWidget* parent, const KexiDB::Driver::InfoMap& driversInfo,
+ Options options = ShowAll );
+
+ ~KexiDBDriverComboBox();
+
+ /*! Gets a list of the names of all drivers.
+
+ Note that this returns just the names of those drivers that are in the
+ combobox: if the includeFileBasedDrivers argument to the constructor
+ was false, this won't include the file based drivers either.
+
+ \return a list of names of drivers that were found */
+ QStringList driverNames() const { return m_driverNames; }
+
+ /*! Get the name of the currrently selected driver. If the combobox is empty,
+ QString::null will be returned.
+
+ \return the name of the currently selected driver */
+ QString selectedDriverName() const;
+
+ /*! Set the currrently selected driver.
+
+ The combobox entry for \a driverName is selected. If \a driverName
+ is not listed in the combobox then there is no change. The search
+ is case insensitive.
+
+ */
+ void setDriverName(const QString& driverName);
+
+ protected:
+ QMap<QString,QString> m_driversMap;
+ QStringList m_driverNames;
+};
+
+#endif
+
diff --git a/kexi/widget/kexidswelcome.cpp b/kexi/widget/kexidswelcome.cpp
new file mode 100644
index 00000000..957132a1
--- /dev/null
+++ b/kexi/widget/kexidswelcome.cpp
@@ -0,0 +1,90 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+
+ 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 <qlayout.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kglobalsettings.h>
+#include <kstdguiitem.h>
+#include <kdeversion.h>
+
+#include "kexidatasourcewizard.h"
+#include "kexidswelcome.h"
+
+KexiDSWelcome::KexiDSWelcome(KexiDataSourceWizard *parent)
+ : QWidget(parent)
+{
+ m_wiz = parent;
+ KexiDSPixmap *pic = new KexiDSPixmap(this);
+
+ QLabel *lText = new QLabel(i18n("Kexi can help you with creation of %2 using data sources in almost no time with the \"%1 Wizard\""), this);
+ lText->setAlignment(AlignTop | AlignLeft | WordBreak);
+ QCheckBox *useWizard = new QCheckBox(i18n("Create %1 using the \"%1 Wizard\""), this);
+ connect(useWizard, SIGNAL(toggled(bool)), this, SLOT(setUseWizard(bool)));
+ useWizard->setChecked(true);
+
+ QSpacerItem *spacer = new QSpacerItem(320, 220);
+ QCheckBox *dontShow = new QCheckBox(i18n("Do not show this wizard again"), this);
+
+ QGridLayout *g = new QGridLayout(this);
+
+ g->addMultiCellWidget(pic, 0, 4, 0, 0);
+ g->addWidget(lText, 0, 1);
+ g->addWidget(useWizard, 2, 1);
+ g->addItem(spacer, 3, 1);
+ g->addWidget(dontShow, 4, 1);
+}
+
+void
+KexiDSWelcome::setUseWizard(bool use)
+{
+#if KDE_IS_VERSION(3,1,9) && !defined(Q_WS_WIN)
+ bool useIcons = KGlobalSettings::showIconsOnPushButtons();
+#else
+ bool useIcons = true;
+#endif
+ if(use)
+ {
+ KGuiItem forward = KStdGuiItem::forward(KStdGuiItem::UseRTL);
+
+ if(useIcons)
+ m_wiz->nextButton()->setIconSet( forward.iconSet() );
+
+ m_wiz->nextButton()->setText(i18n("&Next"));
+ }
+ else
+ {
+ if(useIcons)
+ m_wiz->nextButton()->setIconSet(SmallIconSet("apply"));
+
+ m_wiz->nextButton()->setText(i18n("&Finish"));
+ }
+
+ m_wiz->finishNext(!use);
+}
+
+KexiDSWelcome::~KexiDSWelcome()
+{
+}
+
+#include "kexidswelcome.moc"
+
diff --git a/kexi/widget/kexidswelcome.h b/kexi/widget/kexidswelcome.h
new file mode 100644
index 00000000..23cb64fe
--- /dev/null
+++ b/kexi/widget/kexidswelcome.h
@@ -0,0 +1,48 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+
+ 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 KEXIDSWELCOME_H
+#define KEXIDSWELCOME_H
+
+#include <qwidget.h>
+
+class KexiDataSourceWizard;
+
+/**
+ * This page is part of the KexiDataSourceWizard
+ * it is the greeting page per default, where people
+ * can choose whether they want to use the wizard or not.
+ */
+class KEXIEXTWIDGETS_EXPORT KexiDSWelcome : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KexiDSWelcome(KexiDataSourceWizard *parent);
+ ~KexiDSWelcome();
+
+ protected slots:
+ void setUseWizard(bool use);
+
+ private:
+ KexiDataSourceWizard *m_wiz;
+};
+
+#endif
+
diff --git a/kexi/widget/kexieditor.cpp b/kexi/widget/kexieditor.cpp
new file mode 100644
index 00000000..f482584e
--- /dev/null
+++ b/kexi/widget/kexieditor.cpp
@@ -0,0 +1,261 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ 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 "kexieditor.h"
+
+#include <keximainwindow.h>
+
+#include <qlayout.h>
+#include <qframe.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+//uncomment this to enable KTextEdit-based editor
+//#define KTEXTEDIT_BASED_SQL_EDITOR
+
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+# include <ktextedit.h>
+#else
+# include <ktexteditor/document.h>
+# include <ktexteditor/view.h>
+# include <ktexteditor/editorchooser.h>
+# include <ktexteditor/editinterface.h>
+# include <ktexteditor/viewcursorinterface.h>
+# include <ktexteditor/popupmenuinterface.h>
+# include <ktexteditor/undointerface.h>
+# include <ktexteditor/configinterface.h>
+# include <ktexteditor/highlightinginterface.h>
+#endif
+
+/** Used for the shared action framework to redirect shared actions like
+copy and paste to the editor. */
+class KexiEditorSharedActionConnector : public KexiSharedActionConnector
+{
+public:
+ KexiEditorSharedActionConnector( KexiActionProxy* proxy, QObject* obj )
+ : KexiSharedActionConnector( proxy, obj )
+ {
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ plugSharedAction("edit_cut", SLOT(cut()));
+ plugSharedAction("edit_copy", SLOT(copy()));
+ plugSharedAction("edit_paste", SLOT(paste()));
+ plugSharedAction("edit_clear", SLOT(clear()));
+ plugSharedAction("edit_undo", SLOT(undo()));
+ plugSharedAction("edit_redo", SLOT(redo()));
+ plugSharedAction("edit_select_all", SLOT(selectAll()));
+#else
+ QValueList<QCString> actions;
+ actions << "edit_cut" << "edit_copy" << "edit_paste" << "edit_clear"
+ << "edit_undo" << "edit_redo" << "edit_select_all";
+ plugSharedActionsToExternalGUI(actions, dynamic_cast<KXMLGUIClient*>(obj));
+#endif
+ }
+};
+
+//! @internal
+class KexiEditorPrivate {
+ public:
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ KTextEdit *view;
+#else
+ KTextEditor::Document *doc;
+ KTextEditor::View *view;
+#endif
+};
+
+KexiEditor::KexiEditor(KexiMainWindow *mainWin, QWidget *parent, const char *name)
+ : KexiViewBase(mainWin, parent, name)
+ , d(new KexiEditorPrivate())
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ d->view = new KTextEdit( "", QString::null, this, "kexi_editor" );
+ //adjust font
+ connect(d->view, SIGNAL(textChanged()), this, SIGNAL(textChanged()));
+ QFont f("Courier");
+ f.setStyleStrategy(QFont::PreferAntialias);
+ f.setPointSize(d->view->font().pointSize());
+ d->view->setFont( f );
+ d->view->setCheckSpellingEnabled(false);
+#else
+ QFrame *fr = new QFrame(this);
+ fr->setFrameStyle(QFrame::Sunken|QFrame::WinPanel);
+ layout->addWidget(fr);
+ layout = new QVBoxLayout(fr);
+ layout->setMargin( 2 );
+
+ d->doc = KTextEditor::EditorChooser::createDocument(fr);
+ if (!d->doc)
+ return;
+ d->view = d->doc->createView(fr, 0L);
+
+ KTextEditor::PopupMenuInterface *popupInt = dynamic_cast<KTextEditor::PopupMenuInterface*>( d->view );
+ if(popupInt) {
+ QPopupMenu *pop = (QPopupMenu*) mainWin->factory()->container("edit", mainWin);
+ if(pop) {
+ //plugSharedAction("edit_undo", pop);
+ popupInt->installPopup(pop);
+ }
+ }
+
+ connect(d->doc, SIGNAL(textChanged()), this, SIGNAL(textChanged()));
+#endif
+ KexiEditorSharedActionConnector c(this, d->view);
+ d->view->installEventFilter(this);
+
+ layout->addWidget(d->view);
+ setViewWidget(d->view, true/*focus*/);
+ d->view->show();
+}
+
+KexiEditor::~KexiEditor()
+{
+ delete d;
+}
+
+void KexiEditor::updateActions(bool activated)
+{
+ KexiViewBase::updateActions(activated);
+}
+
+bool KexiEditor::isAdvancedEditor()
+{
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ return false;
+#else
+ return true;
+#endif
+}
+
+QString KexiEditor::text()
+{
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ return d->view->text();
+#else
+ if (!d->doc)
+ return QString::null;
+ KTextEditor::EditInterface *eIface = KTextEditor::editInterface(d->doc);
+ return eIface->text();
+#endif
+}
+
+void KexiEditor::setText(const QString &text)
+{
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ const bool was_dirty = m_parentView ? m_parentView->dirty() : dirty();
+ d->view->setText(text);
+ setDirty(was_dirty);
+#else
+ if (!d->doc)
+ return;
+ const bool was_dirty = dirty();
+ KTextEditor::EditInterface *eIface = KTextEditor::editInterface(d->doc);
+ eIface->setText(text);
+ KTextEditor::UndoInterface *undoIface = KTextEditor::undoInterface(d->doc);
+ undoIface->clearUndo();
+ undoIface->clearRedo();
+ setDirty(was_dirty);
+#endif
+}
+
+void KexiEditor::setHighlightMode(const QString& highlightmodename)
+{
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+#else
+ KTextEditor::HighlightingInterface *hl = KTextEditor::highlightingInterface( d->doc );
+ for(uint i = 0; i < hl->hlModeCount(); i++) {
+ //kdDebug() << "hlmode("<<i<<"): " << hl->hlModeName(i) << endl;
+ if (hl->hlModeName(i).contains(highlightmodename, false)) {
+ hl->setHlMode(i);
+ return;
+ }
+ }
+ hl->setHlMode(0); // 0=None, don't highlight anything.
+#endif
+}
+
+void KexiEditor::slotConfigureEditor()
+{
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ //TODO show errormessage?
+#else
+ KTextEditor::ConfigInterface *config = KTextEditor::configInterface( d->doc );
+ if (config)
+ config->configDialog();
+#endif
+}
+
+void KexiEditor::jump(int character)
+{
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ const int numRows = d->view->paragraphs();
+ int row = 0, col = 0;
+ for (int ch = 0; row < numRows; row++) {
+ const int rowLen = d->view->paragraphLength(row)+1;
+ if ((ch + rowLen) > character) {
+ col = character-ch;
+ break;
+ }
+ ch += rowLen;
+ }
+ d->view->setCursorPosition(row, col);
+#else
+ if (!d->doc)
+ return;
+ KTextEditor::EditInterface *ei = KTextEditor::editInterface(d->doc);
+ const int numRows = ei->numLines();
+ int row = 0, col = 0;
+ for (int ch = 0; row < numRows; row++) {
+ const int rowLen = ei->lineLength(row)+1;
+ if ((ch + rowLen) > character) {
+ col = character-ch;
+ break;
+ }
+ ch += rowLen;
+ }
+ KTextEditor::ViewCursorInterface *ci = KTextEditor::viewCursorInterface(d->view);
+ ci->setCursorPositionReal(row, col);
+#endif
+}
+
+void KexiEditor::setCursorPosition(int line, int col)
+{
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ d->view->setCursorPosition(line, col);
+#else
+ KTextEditor::ViewCursorInterface *ci = KTextEditor::viewCursorInterface( d->view );
+ ci->setCursorPosition(line, col);
+#endif
+}
+
+void KexiEditor::clearUndoRedo()
+{
+#ifdef KTEXTEDIT_BASED_SQL_EDITOR
+ //TODO how to remove undo/redo from a KTextEdit?
+#else
+ KTextEditor::UndoInterface* u = KTextEditor::undoInterface( d->doc );
+ u->clearUndo();
+ u->clearRedo();
+#endif
+}
+
+#include "kexieditor.moc"
+
diff --git a/kexi/widget/kexieditor.h b/kexi/widget/kexieditor.h
new file mode 100644
index 00000000..fce5f45b
--- /dev/null
+++ b/kexi/widget/kexieditor.h
@@ -0,0 +1,120 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ 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 KEXIEDITOR_H
+#define KEXIEDITOR_H
+
+#include <qwidget.h>
+#include "kexiviewbase.h"
+
+class KTextEdit;
+class KexiEditorPrivate;
+
+namespace KTextEditor
+{
+ class Document;
+ class View;
+}
+
+//! An text editor view that uses both KTextEditor and KTextEdit
+/*! It is used for SQL and script editor. */
+class KEXIEXTWIDGETS_EXPORT KexiEditor : public KexiViewBase
+{
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * \param mainWin The \a KexiMainWindow instance this KexiEditor
+ * belongs too.
+ * \param parent The parent \a QWidget this KexiEditor is child
+ * of. You don't need to free the KexiEditor cause Qt
+ * will handle that for us.
+ * \param name The name this KexiEditor has. Used only for debugging.
+ */
+ KexiEditor(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiEditor();
+
+ /**
+ * \return true if internally the KTextEditor::EditorChooser got
+ * used else, if a simple KTextEdit is used, false is returned.
+ */
+ static bool isAdvancedEditor();
+
+ /**
+ * \return the text displayed in the editor-widget.
+ */
+ QString text();
+
+ /**
+ * Set the highlight-mode to \p highlightmodename . If
+ * \a isAdvancedEditor returns false (KTextEdit is used
+ * rather then KTextEditor), then the method just does
+ * nothing. The \p highlightmodename could be any kind
+ * of string like e.g. "python", "kjs" or "sql"
+ * KTextEditor supports.
+ */
+ void setHighlightMode(const QString& highlightmodename);
+
+ /**
+ * Find row and column for this \p character and jump to the
+ * position.
+ */
+ void jump(int character);
+
+ /**
+ * Set the cursor position to \p line and \p col .
+ */
+ void setCursorPosition(int line, int col);
+
+ /**
+ * Clear all remembered undo/redo-actions. Only
+ * avaiable if \a isAdvancedEditor returns true.
+ */
+ void clearUndoRedo();
+
+ public slots:
+ /*! Sets editor's text to \a text. 'Dirty' flag remains unchanged.
+ Undo/redo buffer is cleared.*/
+ void setText(const QString &text);
+ /*! Display the configuration-dialog. Only avaiable if isAdvancedEditor() returns true. */
+ void slotConfigureEditor();
+
+ protected:
+ /*! Update the actions. This call is redirected to \a KexiViewBase::updateActions */
+ virtual void updateActions(bool activated);
+
+ signals:
+ /*! Emitted if the text displayed in the editor changed. */
+ void textChanged();
+
+ private:
+ /*! Private d-pointer class. */
+ KexiEditorPrivate *d;
+};
+
+#endif
diff --git a/kexi/widget/kexifieldcombobox.cpp b/kexi/widget/kexifieldcombobox.cpp
new file mode 100644
index 00000000..c0355e9c
--- /dev/null
+++ b/kexi/widget/kexifieldcombobox.cpp
@@ -0,0 +1,250 @@
+/* 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 "kexifieldcombobox.h"
+
+#include <qheader.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qstyle.h>
+#include <qlistbox.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kdeversion.h>
+#include <kconfig.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+
+#include <kexidb/tableschema.h>
+#include <kexidb/queryschema.h>
+#include <kexidb/utils.h>
+#include <kexiutils/utils.h>
+#include <kexidragobjects.h>
+#include <kexiproject.h>
+
+//! @internal
+class KexiFieldComboBox::Private
+{
+ public:
+ Private()
+// : schema(0)
+ : keyIcon( SmallIcon("key") )
+ , noIcon( KexiUtils::emptyIcon(KIcon::Small) )
+ , table(true)
+ {
+ }
+ ~Private()
+ {
+// delete schema;
+ }
+ QGuardedPtr<KexiProject> prj;
+// KexiDB::TableOrQuerySchema* schema;
+ QPixmap keyIcon, noIcon;
+ QString tableOrQueryName;
+ QString fieldOrExpression;
+ QMap<QString, QString> captions;
+ bool table : 1;
+};
+
+//------------------------
+
+KexiFieldComboBox::KexiFieldComboBox(QWidget *parent, const char *name)
+ : KComboBox(true/*rw*/, parent, name)
+ , d(new Private())
+{
+ setInsertionPolicy(NoInsertion);
+ setCompletionMode(KGlobalSettings::CompletionPopupAuto);
+ setSizeLimit( 16 );
+ connect(this, SIGNAL(activated(int)), this, SLOT(slotActivated(int)));
+ connect(this, SIGNAL(returnPressed(const QString &)), this, SLOT(slotReturnPressed(const QString &)));
+
+// setAcceptDrops(true);
+// viewport()->setAcceptDrops(true);
+}
+
+KexiFieldComboBox::~KexiFieldComboBox()
+{
+ delete d;
+}
+
+void KexiFieldComboBox::setProject(KexiProject *prj)
+{
+ if ((KexiProject*)d->prj==prj)
+ return;
+ d->prj = prj;
+ setTableOrQuery("", true);
+}
+
+KexiProject* KexiFieldComboBox::project() const
+{
+ return d->prj;
+}
+
+void KexiFieldComboBox::setTableOrQuery(const QString& name, bool table)
+{
+ d->tableOrQueryName = name;
+ d->table = table;
+ clear();
+ d->captions.clear();
+ insertItem("");
+// delete d->schema;
+ if (d->tableOrQueryName.isEmpty() || !d->prj)
+ return;
+
+ KexiDB::TableOrQuerySchema tableOrQuery(d->prj->dbConnection(), d->tableOrQueryName.latin1(), d->table);
+ if (!tableOrQuery.table() && !tableOrQuery.query())
+ return;
+
+// bool hasPKeys = true; //t->hasPrimaryKeys();
+ KexiDB::QueryColumnInfo::Vector columns = tableOrQuery.columns();
+ const int count = columns.count();
+ for(int i=0; i < count; i++)
+ {
+ KexiDB::QueryColumnInfo *colinfo = columns[i];
+ insertItem(
+ (colinfo && (colinfo->field->isPrimaryKey() || colinfo->field->isUniqueKey()))
+ ? d->keyIcon
+ : d->noIcon
+ , colinfo->aliasOrName());
+ completionObject()->addItem(colinfo->aliasOrName());
+ //store user-friendly caption (used by fieldOrExpressionCaption())
+ d->captions.insert( colinfo->aliasOrName(), colinfo->captionOrAliasOrName() );
+ }
+
+ //update selection
+ setFieldOrExpression(d->fieldOrExpression);
+}
+
+QString KexiFieldComboBox::tableOrQueryName() const
+{
+ return d->tableOrQueryName;
+}
+
+bool KexiFieldComboBox::isTableAssigned() const
+{
+ return d->table;
+}
+
+void KexiFieldComboBox::setFieldOrExpression(const QString& string)
+{
+ const QString name(string); //string.stripWhiteSpace().lower());
+ const int pos = name.find('.');
+ if (pos==-1) {
+ d->fieldOrExpression = name;
+ }
+ else {
+ QString objectName = name.left(pos);
+ if (d->tableOrQueryName!=objectName) {
+ d->fieldOrExpression = name;
+ setCurrentItem(0);
+ setCurrentText(name);
+//! @todo show error
+ kexiwarn << "KexiFieldComboBox::setField(): invalid table/query name in '" << name << "'" << endl;
+ return;
+ }
+ d->fieldOrExpression = name.mid(pos+1);
+ }
+
+ QListBoxItem *item = listBox()->findItem(d->fieldOrExpression);
+ if (!item) {
+ setCurrentItem(0);
+ setCurrentText(d->fieldOrExpression);
+ //todo: show 'the item doesn't match' info?
+ return;
+ }
+ setCurrentItem( listBox()->index(item) );
+}
+
+void KexiFieldComboBox::setFieldOrExpression(int index)
+{
+ index++; //skip 1st empty item
+ if (index>=count()) {
+ kexiwarn << QString("KexiFieldComboBox::setFieldOrExpression(int index): index %1 "
+ "out of range (0..%2)").arg(index).arg(count()-1) << endl;
+ index = -1;
+ }
+ if (index<=0) {
+ setCurrentItem(0);
+ d->fieldOrExpression = QString::null;
+ }
+ else {
+ setCurrentItem(index);
+ d->fieldOrExpression = currentText();
+ }
+}
+
+QString KexiFieldComboBox::fieldOrExpression() const
+{
+ return d->fieldOrExpression;
+}
+
+int KexiFieldComboBox::indexOfField() const
+{
+ KexiDB::TableOrQuerySchema tableOrQuery(d->prj->dbConnection(), d->tableOrQueryName.latin1(), d->table);
+ if (!tableOrQuery.table() && !tableOrQuery.query())
+ return -1;
+
+ return currentItem()>0 ? (currentItem()-1) : -1;
+}
+
+QString KexiFieldComboBox::fieldOrExpressionCaption() const
+{
+ return d->captions[ d->fieldOrExpression ];
+}
+
+void KexiFieldComboBox::slotActivated(int i)
+{
+ d->fieldOrExpression = text(i);
+ emit selected();
+}
+
+void KexiFieldComboBox::slotReturnPressed(const QString & text)
+{
+ //text is available: select item for this text:
+ int index;
+ if (text.isEmpty()) {
+ index = 0;
+ }
+ else {
+ QListBoxItem *item = listBox()->findItem( text, Qt::ExactMatch );
+ if (!item)
+ return;
+ index = listBox()->index( item );
+ if (index < 1)
+ return;
+ }
+ setCurrentItem( index );
+ slotActivated( index );
+}
+
+void KexiFieldComboBox::focusOutEvent( QFocusEvent *e )
+{
+ KComboBox::focusOutEvent( e );
+ // accept changes if the focus is moved
+ if (!KexiUtils::hasParent(this, focusWidget())) //(a check needed because drop-down listbox also causes a focusout)
+ slotReturnPressed(currentText());
+}
+
+#include "kexifieldcombobox.moc"
diff --git a/kexi/widget/kexifieldcombobox.h b/kexi/widget/kexifieldcombobox.h
new file mode 100644
index 00000000..238168d2
--- /dev/null
+++ b/kexi/widget/kexifieldcombobox.h
@@ -0,0 +1,82 @@
+/* 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 KEXIFIELDCOMBOBOX_H
+#define KEXIFIELDCOMBOBOX_H
+
+#include <qpixmap.h>
+#include <kcombobox.h>
+
+namespace KexiDB {
+ class TableOrQuerySchema;
+}
+class KexiProject;
+
+/*! This widget provides a list of fields from a table or query
+ within a combobox, so user can pick one of them.
+*/
+class KEXIEXTWIDGETS_EXPORT KexiFieldComboBox : public KComboBox
+{
+ Q_OBJECT
+
+ public:
+ KexiFieldComboBox(QWidget *parent, const char *name = 0);
+ virtual ~KexiFieldComboBox();
+
+// /*! Sets table or query schema \a schema.
+// The schema object will be owned by the KexiFieldComboBox object. */
+// void setSchema(KexiDB::TableOrQuerySchema* schema);
+
+// KexiDB::TableOrQuerySchema* schema() const { return m_schema; }
+
+ public slots:
+ //! \return global project that is used to retrieve schema informationm for this combo box.
+ KexiProject* project() const;
+
+ //! Sets global project that is used to retrieve schema informationm for this combo box.
+ void setProject(KexiProject *prj);
+
+ void setTableOrQuery(const QString& name, bool table);
+ QString tableOrQueryName() const;
+ bool isTableAssigned() const;
+ void setFieldOrExpression(const QString& string);
+ void setFieldOrExpression(int index);
+ QString fieldOrExpression() const;
+ QString fieldOrExpressionCaption() const;
+
+ /*! \return index of selected table or query field.
+ -1 is returned if there is nothing selected or expression is selected
+ of project is not assigned or table or query is not assigned. */
+ int indexOfField() const;
+
+ signals:
+ void selected();
+
+ protected slots:
+ void slotActivated(int);
+ void slotReturnPressed(const QString & text);
+
+ protected:
+ virtual void focusOutEvent( QFocusEvent *e );
+
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kexi/widget/kexifieldlistview.cpp b/kexi/widget/kexifieldlistview.cpp
new file mode 100644
index 00000000..84b577a6
--- /dev/null
+++ b/kexi/widget/kexifieldlistview.cpp
@@ -0,0 +1,180 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003-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 "kexifieldlistview.h"
+
+#include <qheader.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qstyle.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kdeversion.h>
+#include <kconfig.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+
+#include <kexidb/tableschema.h>
+#include <kexidb/queryschema.h>
+#include <kexidb/utils.h>
+#include <kexidragobjects.h>
+#include <kexiutils/utils.h>
+
+KexiFieldListView::KexiFieldListView(QWidget *parent, const char *name, int options)
+ : KListView(parent, name)
+ , m_schema(0)
+ , m_keyIcon(SmallIcon("key"))
+ , m_noIcon(KexiUtils::emptyIcon(KIcon::Small))
+ , m_options(options)
+ , m_allColumnsItem(0)
+{
+ setAcceptDrops(true);
+ viewport()->setAcceptDrops(true);
+ setDropVisualizer(false);
+ setDropHighlighter(true);
+ setAllColumnsShowFocus(true);
+ addColumn(i18n("Field Name"));
+ if (m_options & ShowDataTypes)
+ addColumn(i18n("Data Type"));
+ if (m_options & AllowMultiSelection)
+ setSelectionMode(QListView::Extended);
+ setResizeMode(QListView::LastColumn);
+// header()->hide();
+ setSorting(-1, true); // disable sorting
+ setDragEnabled(true);
+
+ connect(this, SIGNAL(doubleClicked(QListViewItem*, const QPoint &, int)),
+ this, SLOT(slotDoubleClicked(QListViewItem*)));
+}
+
+KexiFieldListView::~KexiFieldListView()
+{
+ delete m_schema;
+}
+
+void KexiFieldListView::setSchema(KexiDB::TableOrQuerySchema* schema)
+{
+ if (schema && m_schema == schema)
+ return;
+ m_allColumnsItem = 0;
+ clear();
+ delete m_schema;
+ m_schema = schema;
+ if (!m_schema)
+ return;
+
+ int order=0;
+ bool hasPKeys = true; //t->hasPrimaryKeys();
+ KListViewItem *item = 0;
+ KexiDB::QueryColumnInfo::Vector columns = m_schema->columns(true /*unique*/);
+ const int count = columns.count();
+ for(int i=-1; i < count; i++)
+ {
+ KexiDB::QueryColumnInfo *colinfo = 0;
+ if (i==-1) {
+ if (! (m_options & ShowAsterisk))
+ continue;
+ item = new KListViewItem(this, item, i18n("* (All Columns)"));
+ m_allColumnsItem = item;
+ }
+ else {
+ colinfo = columns[i];
+ item = new KListViewItem(this, item, colinfo->aliasOrName());
+ if (m_options & ShowDataTypes)
+ item->setText(1, colinfo->field->typeName());
+ }
+ if(colinfo && (colinfo->field->isPrimaryKey() || colinfo->field->isUniqueKey()))
+ item->setPixmap(0, m_keyIcon);
+ else if (hasPKeys) {
+ item->setPixmap(0, m_noIcon);
+ }
+ order++;
+ }
+
+ setCurrentItem(firstChild());
+}
+
+#if 0
+QSize KexiFieldListView::sizeHint()
+{
+ QFontMetrics fm(font());
+
+ kdDebug() << m_table->name() << " cw=" << columnWidth(1) + fm.width("i") << ", " << fm.width(m_table->name()+" ") << endl;
+
+ QSize s(
+ QMAX( columnWidth(1) + fm.width("i"), fm.width(m_table->name()+" ")),
+ childCount()*firstChild()->totalHeight() + 4 );
+// QSize s( columnWidth(1), childCount()*firstChild()->totalHeight() + 3*firstChild()->totalHeight()/10);
+ return s;
+}
+
+void KexiFieldListView::setReadOnly(bool b)
+{
+ setAcceptDrops(!b);
+ viewport()->setAcceptDrops(!b);
+}
+#endif
+
+QDragObject* KexiFieldListView::dragObject()
+{
+ if (!schema())
+ return 0;
+ const QStringList selectedFields( selectedFieldNames() );
+ return new KexiFieldDrag(m_schema->table() ? "kexi/table" : "kexi/query",
+ m_schema->name(), selectedFields, this, "KexiFieldDrag");
+/* if (selectedItem()) {
+ KexiFieldDrag *drag = new KexiFieldDrag("kexi/table", m_schema->name(),
+ selectedItem()->text(1), this, "KexiFieldDrag");
+ return drag;
+ }*/
+}
+
+QStringList KexiFieldListView::selectedFieldNames() const
+{
+ if (!schema())
+ return QStringList();
+ QStringList selectedFields;
+ for (QListViewItem *item = firstChild(); item; item = item->nextSibling()) {
+ if (item->isSelected()) {
+//! @todo what about query fields/aliases? it.current()->text(0) can be not enough
+ if (item == m_allColumnsItem && m_allColumnsItem)
+ selectedFields.append("*");
+ else
+ selectedFields.append(item->text(0));
+ }
+ }
+ return selectedFields;
+}
+
+void KexiFieldListView::slotDoubleClicked(QListViewItem* item)
+{
+ if (schema() && item) {
+ //! @todo what about query fields/aliases? it.current()->text(0) can be not enough
+ emit fieldDoubleClicked(schema()->table() ? "kexi/table" : "kexi/query",
+ schema()->name(), item->text(0));
+ }
+}
+
+#include "kexifieldlistview.moc"
diff --git a/kexi/widget/kexifieldlistview.h b/kexi/widget/kexifieldlistview.h
new file mode 100644
index 00000000..fcbe1c5f
--- /dev/null
+++ b/kexi/widget/kexifieldlistview.h
@@ -0,0 +1,82 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003-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 KEXIFIELDLISTVIEW_H
+#define KEXIFIELDLISTVIEW_H
+
+#include <qframe.h>
+#include <qpixmap.h>
+#include <klistview.h>
+
+class KListViewItem;
+
+namespace KexiDB {
+ class TableOrQuerySchema;
+}
+
+/*! This widget provides a list of fields from a table or query.
+*/
+class KEXIEXTWIDGETS_EXPORT KexiFieldListView : public KListView
+{
+ Q_OBJECT
+
+ public:
+ //! Flags used to alter list's behaviour and appearance
+ enum Options {
+ ShowDataTypes = 1, //!< if set, 'data type' column is added
+ ShowAsterisk = 2, //!< if set, asterisk ('*') item is prepended to the list
+ AllowMultiSelection = 4 //!< if set, multiple selection is allowed
+ };
+
+ KexiFieldListView(QWidget *parent, const char *name = 0,
+ int options = ShowDataTypes | AllowMultiSelection );
+ virtual ~KexiFieldListView();
+
+ /*! Sets table or query schema \a schema.
+ The schema object will be owned by the KexiFieldListView object. */
+ void setSchema(KexiDB::TableOrQuerySchema* schema);
+
+ /*! \return table or query schema schema set for this widget. */
+ KexiDB::TableOrQuerySchema* schema() const { return m_schema; }
+
+ /*! \return list of selected field names. */
+ QStringList selectedFieldNames() const;
+
+// void setReadOnly(bool);
+// virtual QSize sizeHint();
+
+ signals:
+ /*! Emitted when a field is double clicked */
+ void fieldDoubleClicked(const QString& sourceMimeType, const QString& sourceName,
+ const QString& fieldName);
+
+ protected slots:
+ void slotDoubleClicked(QListViewItem* item);
+
+ protected:
+ virtual QDragObject *dragObject();
+
+ KexiDB::TableOrQuerySchema* m_schema;
+ QPixmap m_keyIcon; //!< a small "primary key" icon for 0-th column
+ QPixmap m_noIcon; //!< blank icon of the same size as m_keyIcon
+ int m_options;
+ KListViewItem *m_allColumnsItem;
+};
+
+#endif
diff --git a/kexi/widget/kexifilterdlg.cpp b/kexi/widget/kexifilterdlg.cpp
new file mode 100644
index 00000000..6f847e61
--- /dev/null
+++ b/kexi/widget/kexifilterdlg.cpp
@@ -0,0 +1,149 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+
+ 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 <qlistview.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qheader.h>
+#include <qstringlist.h>
+
+#include "kexiproject.h"
+#include "kexiprojecthandler.h"
+#include "kexiprojecthandleritem.h"
+#include "kexidataprovider.h"
+#include "kexifilterdlg.h"
+#include "kexiquerydesignersqleditor.h"
+
+KexiFilterDlg::KexiFilterDlg(KexiProject *project, QWidget *parent, const char *name)
+ : QDialog(parent, name)
+{
+ m_project = project;
+
+ QHBoxLayout *lbraces = new QHBoxLayout(0, 0, 4);
+
+ QPushButton *bsBO = createMiniButton("[");
+ QPushButton *bBO = createMiniButton("(");
+ QPushButton *bBC = createMiniButton(")");
+ QPushButton *bsBC = createMiniButton("]");
+ lbraces->addWidget(bsBO);
+ lbraces->addWidget(bBO);
+ lbraces->addWidget(bBC);
+ lbraces->addWidget(bsBC);
+
+ QHBoxLayout *lcond = new QHBoxLayout(0, 0, 4);
+ QPushButton *blt = createMiniButton("<");
+ QPushButton *beq = createMiniButton("=");
+ QPushButton *bgt = createMiniButton(">");
+ QPushButton *bp = createMiniButton("%");
+ lcond->addWidget(blt);
+ lcond->addWidget(beq);
+ lcond->addWidget(bgt);
+ lcond->addWidget(bp);
+
+ QHBoxLayout *lbool = new QHBoxLayout(0, 0, 4);
+ QPushButton *bAnd = new QPushButton("AND", this);
+ bAnd->setFlat(true);
+ QPushButton *bOr = new QPushButton("OR", this);
+ bOr->setFlat(true);
+ QPushButton *bLike = new QPushButton("LIKE", this);
+ bLike->setFlat(true);
+ lbool->addWidget(bLike);
+ lbool->addWidget(bAnd);
+ lbool->addWidget(bOr);
+
+ m_catalog = new QListView(this);
+ m_catalog->addColumn("a");
+ m_catalog->header()->hide();
+
+ KexiQueryDesignerSQLEditor *e = new KexiQueryDesignerSQLEditor(this);
+
+ setupCatalog(QString("kexi/table"));
+
+ QGridLayout *g = new QGridLayout(this);
+ g->setSpacing(6);
+ g->addMultiCellWidget(e, 0, 0, 0, 2);
+ g->addItem(lbraces, 1, 0);
+ g->addItem(lcond, 1, 1);
+ g->addItem(lbool, 1, 2);
+ g->addMultiCellWidget(m_catalog, 2, 2, 0, 2);
+}
+
+QPushButton*
+KexiFilterDlg::createMiniButton(const QString &text)
+{
+ QPushButton *p = new QPushButton(text, this);
+ p->setFlat(true);
+ p->setMaximumSize(QSize(20, 300));
+
+ return p;
+}
+
+void
+KexiFilterDlg::setupCatalog(const QStringList &mimes)
+{
+ m_catalog->clear();
+ m_catalog->setRootIsDecorated(true);
+ QStringList::ConstIterator it, end( mimes.constEnd() );
+ for( it = mimes.constBegin(); it != end; ++it)
+ {
+ KexiProjectHandler *h = m_project->handlerForMime(*it);
+ if(h)
+ {
+ QListViewItem *base = new QListViewItem(m_catalog, h->name());
+ base->setPixmap(0, h->groupPixmap());
+
+ QDictIterator<KexiProjectHandlerItem> iit(*h->items()); // See QDictIterator
+ for(; iit.current(); ++iit )
+ {
+ QListViewItem *bi = new QListViewItem(base, iit.current()->name());
+ bi->setPixmap(0, h->itemPixmap());
+
+ KexiDataProvider *prov=KEXIDATAPROVIDER(h);
+ if(prov)
+ {
+ QStringList fields = prov->fields(0, iit.current()->identifier());
+ QStringList::ConstIterator fit, end( fields.constEnd() );
+ for( fit = fields.constBegin(); fit != end; ++fit)
+ {
+ QListViewItem *bif = new QListViewItem(bi, (*fit));
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+KexiFilterDlg::setupCatalog(const QString &mime)
+{
+ QStringList l;
+ l.append(mime);
+ setupCatalog(l);
+}
+
+void
+KexiFilterDlg::insert(QListViewItem *)
+{
+}
+
+KexiFilterDlg::~KexiFilterDlg()
+{
+}
+
+#include "kexifilterdlg.moc"
diff --git a/kexi/widget/kexifilterdlg.h b/kexi/widget/kexifilterdlg.h
new file mode 100644
index 00000000..e5544888
--- /dev/null
+++ b/kexi/widget/kexifilterdlg.h
@@ -0,0 +1,50 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+
+ 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 KEXIFILTERDLG_H
+#define KEXIFILTERDLG_H
+
+#include <qdialog.h>
+
+class QPushButton;
+class QListView;
+class QListViewItem;
+class KexiProject;
+
+class KEXIEXTWIDGETS_EXPORT KexiFilterDlg : public QDialog
+{
+ Q_OBJECT
+
+ public:
+ KexiFilterDlg(KexiProject *p, QWidget *parent=0, const char *name=0);
+ ~KexiFilterDlg();
+
+ QPushButton *createMiniButton(const QString &text);
+ void setupCatalog(const QStringList &mimes);
+ void setupCatalog(const QString &mime);
+
+ protected slots:
+ void insert(QListViewItem *);
+
+ protected:
+ QListView *m_catalog;
+ KexiProject *m_project;
+};
+
+#endif
diff --git a/kexi/widget/kexiprjtypeselector.cpp b/kexi/widget/kexiprjtypeselector.cpp
new file mode 100644
index 00000000..71dafc6c
--- /dev/null
+++ b/kexi/widget/kexiprjtypeselector.cpp
@@ -0,0 +1,44 @@
+/* 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 "kexiprjtypeselector.h"
+#include <qlabel.h>
+#include <kiconloader.h>
+#include <kmimetype.h>
+#include <kexidb/driver.h>
+
+KexiPrjTypeSelector::KexiPrjTypeSelector( QWidget* parent )
+ : KexiPrjTypeSelectorBase( parent, "KexiPrjTypeSelector" )
+{
+ QString none;
+ icon_file->setPixmap(
+ KGlobal::iconLoader()->loadIcon( KMimeType::mimeType(
+ KexiDB::Driver::defaultFileBasedDriverMimeType() )->icon(none,0), KIcon::Desktop, 48
+ )
+ );
+ icon_file->setFixedSize(icon_file->pixmap()->size()/2);
+ icon_server->setPixmap( DesktopIcon("network", 48) );
+ icon_server->setFixedSize(icon_server->pixmap()->size()/2);
+}
+
+KexiPrjTypeSelector::~KexiPrjTypeSelector()
+{
+}
+
+#include "kexiprjtypeselector.moc"
diff --git a/kexi/widget/kexiprjtypeselector.h b/kexi/widget/kexiprjtypeselector.h
new file mode 100644
index 00000000..efb8a294
--- /dev/null
+++ b/kexi/widget/kexiprjtypeselector.h
@@ -0,0 +1,38 @@
+/* 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 KEXIPRJTYPESELECTOR_H
+#define KEXIPRJTYPESELECTOR_H
+
+#include "kexiprjtypeselectorbase.h"
+
+//! @short A simple widget with radio buttons with "show file/server-based projects" options
+class KEXIEXTWIDGETS_EXPORT KexiPrjTypeSelector : public KexiPrjTypeSelectorBase
+{
+ Q_OBJECT
+
+ public:
+ KexiPrjTypeSelector( QWidget* parent = 0 );
+ ~KexiPrjTypeSelector();
+
+ protected slots:
+ virtual void languageChange() { KexiPrjTypeSelectorBase::languageChange(); }
+};
+
+#endif // KEXIPRJTYPESELECTOR_H
diff --git a/kexi/widget/kexiprjtypeselectorbase.ui b/kexi/widget/kexiprjtypeselectorbase.ui
new file mode 100644
index 00000000..a8031909
--- /dev/null
+++ b/kexi/widget/kexiprjtypeselectorbase.ui
@@ -0,0 +1,162 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KexiPrjTypeSelectorBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KexiPrjTypeSelectorBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>254</width>
+ <height>61</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <property name="selectedId" stdset="0">
+ <number>1</number>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QRadioButton" row="1" column="1">
+ <property name="name">
+ <cstring>option_server</cstring>
+ </property>
+ <property name="text">
+ <string>Projects stored on a database server</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>2</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="1">
+ <property name="name">
+ <cstring>option_file</cstring>
+ </property>
+ <property name="text">
+ <string>Projects stored in a file</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>icon_file</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>icon_server</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ </widget>
+ <widget class="QFrame" row="2" column="1">
+ <property name="name">
+ <cstring>frame_server</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonGroup</sender>
+ <signal>clicked(int)</signal>
+ <receiver>KexiPrjTypeSelectorBase</receiver>
+ <slot>slotSelectionChanged(int)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>option_file</tabstop>
+ <tabstop>option_server</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">kexiprjtypeselectorbase.ui.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="non virtual">slotSelectionChanged( int id )</slot>
+</slots>
+<functions>
+ <function access="private" specifier="non virtual">init()</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kexi/widget/kexiprjtypeselectorbase.ui.h b/kexi/widget/kexiprjtypeselectorbase.ui.h
new file mode 100644
index 00000000..03f1301f
--- /dev/null
+++ b/kexi/widget/kexiprjtypeselectorbase.ui.h
@@ -0,0 +1,23 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you want to add, delete, or rename functions or slots, use
+** Qt Designer to update this file, preserving your code.
+**
+** You should not define a constructor or destructor in this file.
+** Instead, write your code in functions called init() and destroy().
+** These will automatically be called by the form's constructor and
+** destructor.
+*****************************************************************************/
+
+
+void KexiPrjTypeSelectorBase::init()
+{
+ slotSelectionChanged( 1 );
+}
+
+
+void KexiPrjTypeSelectorBase::slotSelectionChanged( int id )
+{
+ frame_server->setEnabled(id==2);
+}
diff --git a/kexi/widget/kexipropertyeditorview.cpp b/kexi/widget/kexipropertyeditorview.cpp
new file mode 100644
index 00000000..5225a7af
--- /dev/null
+++ b/kexi/widget/kexipropertyeditorview.cpp
@@ -0,0 +1,223 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-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 "kexipropertyeditorview.h"
+#include "keximainwindow.h"
+#include <koproperty/set.h>
+#include <koproperty/editor.h>
+#include <koproperty/property.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+
+KexiObjectInfoLabel::KexiObjectInfoLabel(QWidget* parent, const char* name)
+ : QWidget(parent, name)
+{
+ QHBoxLayout *hlyr = new QHBoxLayout(this);
+ m_objectIconLabel = new QLabel(this);
+ m_objectIconLabel->setMargin(2);
+ setFixedHeight( IconSize(KIcon::Small) + 2 + 2 );
+ hlyr->addWidget(m_objectIconLabel);
+ m_objectNameLabel = new QLabel(this);
+ m_objectNameLabel->setMargin(2);
+ m_objectNameLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+ hlyr->addWidget(m_objectNameLabel);
+}
+
+KexiObjectInfoLabel::~KexiObjectInfoLabel()
+{
+}
+
+void KexiObjectInfoLabel::setObjectClassIcon(const QString& name)
+{
+ m_classIcon = name;
+ if (m_classIcon.isEmpty())
+ m_objectIconLabel->setFixedWidth( 0 );
+ else
+ m_objectIconLabel->setFixedWidth( IconSize(KIcon::Small) + 2 + 2 );
+ m_objectIconLabel->setPixmap( SmallIcon(name) );
+}
+
+void KexiObjectInfoLabel::setObjectClassName(const QString& name)
+{
+ m_className = name;
+ updateName();
+}
+
+void KexiObjectInfoLabel::setObjectName(const QString& name)
+{
+ m_objectName = name;
+ updateName();
+}
+
+void KexiObjectInfoLabel::updateName()
+{
+ QString txt( m_className );
+ if (txt.isEmpty())
+ txt = m_objectName;
+ else if (!m_objectName.isEmpty())
+ txt += QString(" \"%1\"").arg(m_objectName);
+ m_objectNameLabel->setText(txt);
+}
+
+void KexiObjectInfoLabel::setBuddy( QWidget * buddy )
+{
+ m_objectNameLabel->setBuddy(buddy);
+}
+
+//------------------------------
+
+//! @internal
+class KexiPropertyEditorView::Private
+{
+ public:
+ Private()
+ {
+ }
+ KoProperty::Editor *editor;
+// QLabel *objectIcon;
+// QString iconName;
+// QLabel *objectClassName;
+ KexiObjectInfoLabel *objectInfoLabel;
+};
+
+//------------------------------
+
+KexiPropertyEditorView::KexiPropertyEditorView(KexiMainWindow *mainWin, QWidget* parent)
+ : QWidget(parent, "KexiPropertyEditorView")
+ , d(new Private())
+{
+ setCaption(i18n("Properties"));
+ //TODO: set a nice icon
+ setIcon(*mainWin->icon());
+
+ QVBoxLayout *lyr = new QVBoxLayout(this);
+
+ //add object class info
+ d->objectInfoLabel = new KexiObjectInfoLabel(this, "KexiObjectInfoLabel");
+ lyr->addWidget(d->objectInfoLabel);
+
+ /*
+ QHBoxLayout *vlyr = new QHBoxLayout(lyr);
+ d->objectIcon = new QLabel(this);
+ d->objectIcon->setMargin(2);
+ d->objectIcon->setFixedHeight( IconSize(KIcon::Small) + 2 + 2 );
+ vlyr->addWidget(d->objectIcon);
+ d->objectClassName = new QLabel(this);
+ d->objectClassName->setMargin(2);
+ d->objectClassName->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+ vlyr->addWidget(d->objectClassName);*/
+
+ d->editor = new KoProperty::Editor(this, true /*AutoSync*/, "propeditor");
+ lyr->addWidget(d->editor);
+ setFocusProxy(d->editor);
+ d->objectInfoLabel->setBuddy(d->editor);
+ setFocusPolicy(WheelFocus);
+
+ connect(d->editor, SIGNAL(propertySetChanged(KoProperty::Set*)),
+ this, SLOT(slotPropertySetChanged(KoProperty::Set*)));
+
+// d->iconName = "dummy";
+ slotPropertySetChanged(0);
+}
+
+KexiPropertyEditorView::~KexiPropertyEditorView()
+{
+ delete d;
+}
+
+QSize KexiPropertyEditorView::sizeHint() const
+{
+ return QSize(200,200);//m_editor->sizeHint();
+}
+
+QSize KexiPropertyEditorView::minimumSizeHint() const
+{
+ return QSize(200,200);//m_editor->sizeHint();
+}
+
+/*void KexiPropertyEditorView::setGeometry ( const QRect &r )
+{
+ QWidget::setGeometry(r);
+}
+
+void KexiPropertyEditorView::resize ( int w, int h )
+{
+ QWidget::resize( w, h );
+}*/
+
+KoProperty::Editor *KexiPropertyEditorView::editor() const
+{
+ return d->editor;
+}
+
+/*! Updates \a infoLabel widget by reusing properties provided by property set \a set.
+ Read documentation of KexiPropertyEditorView class for information about accepted properties.
+ If \a set is 0 and \a textToDisplayForNullSet string is not empty, this string is displayed
+ (without icon or any other additional part).
+ If \a set is 0 and \a textToDisplayForNullSet string is empty, the \a infoLabel widget becomes
+ hidden.
+*/
+void KexiPropertyEditorView::updateInfoLabelForPropertySet(KexiObjectInfoLabel *infoLabel,
+ KoProperty::Set* set, const QString& textToDisplayForNullSet)
+{
+ QString className, iconName, objectName;
+ if (set) {
+ if (set->contains("this:classString"))
+ className = (*set)["this:classString"].value().toString();
+ if (set->contains("this:iconName"))
+ iconName = (*set)["this:iconName"].value().toString();
+ const bool useCaptionAsObjectName = set->contains("this:useCaptionAsObjectName")
+ && (*set)["this:useCaptionAsObjectName"].value().toBool();
+ if (set->contains(useCaptionAsObjectName ? "caption" : "name"))
+ objectName = (*set)[useCaptionAsObjectName ? "caption" : "name"].value().toString();
+ }
+ if (!set || objectName.isEmpty()) {
+ objectName = textToDisplayForNullSet;
+ className = QString::null;
+ iconName = QString::null;
+ }
+
+ if (className.isEmpty() && objectName.isEmpty())
+ infoLabel->hide();
+ else
+ infoLabel->show();
+
+ if (infoLabel->objectClassName() == className
+ && infoLabel->objectClassIcon() == iconName
+ && infoLabel->objectName() == objectName)
+ return;
+
+ infoLabel->setObjectClassIcon(iconName);
+ infoLabel->setObjectClassName(className);
+ infoLabel->setObjectName(objectName);
+}
+
+void KexiPropertyEditorView::slotPropertySetChanged(KoProperty::Set* set)
+{
+ //update information about selected object
+ updateInfoLabelForPropertySet(d->objectInfoLabel, set);
+ d->editor->setEnabled(set);
+}
+
+#include "kexipropertyeditorview.moc"
diff --git a/kexi/widget/kexipropertyeditorview.h b/kexi/widget/kexipropertyeditorview.h
new file mode 100644
index 00000000..77dab6c8
--- /dev/null
+++ b/kexi/widget/kexipropertyeditorview.h
@@ -0,0 +1,117 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-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 KEXIPROPERTYEDITORVIEW_H
+#define KEXIPROPERTYEDITORVIEW_H
+
+//#include "kexiviewbase.h"
+#include <qwidget.h>
+
+class QLabel;
+class KexiMainWindow;
+
+namespace KoProperty {
+ class Editor;
+ class Set;
+}
+
+//! @short Helper class displaying small icon with class name and object name
+/*! The info label is displayed in a form:
+ <i>[ObjectClassIcon] ClassName "ObjectName"</i>
+
+ The <i>ObjectClassIcon</i> is optional. If "ClassName" is empty, the information
+ is displayed as:
+ <i>[ObjectClassIcon] ObjectName</i>
+
+ Example uses:
+ - [button_icon] Button "quit"
+ - [label_icon] Label "welcome"
+*/
+class KEXIEXTWIDGETS_EXPORT KexiObjectInfoLabel : public QWidget
+{
+ public:
+ KexiObjectInfoLabel(QWidget* parent, const char* name = 0);
+ ~KexiObjectInfoLabel();
+
+ void setObjectClassIcon(const QString& name);
+ QString objectClassIcon() const { return m_classIcon; }
+ void setObjectClassName(const QString& name);
+ QString objectClassName() const { return m_className; }
+ void setObjectName(const QString& name);
+ QString objectName() const { return m_objectName; }
+ void setBuddy( QWidget * buddy );
+ protected:
+ void updateName();
+
+ QString m_className;
+ QString m_classIcon, m_objectName;
+ QLabel *m_objectIconLabel, *m_objectNameLabel;
+};
+
+//! @short The container (acts as a dock window) for KexiPropertyEditor.
+/*! The widget displays KexiObjectInfoLabel on its top, to show user what
+ object the properties belong to. Read KexiObjectInfoLabel documentation for
+ the description what information is displayed.
+
+ There are properties obtained from KexiMainWindow's current property set
+ that help to customize displaying this information:
+ - "this:classString property" of type string describes object's class name
+ - "this:iconName" property of type string describes class name
+ - "name" or "caption" property of type string describes object's name
+ - "this:useCaptionAsObjectName" property of type boolean forces displaying "caption"
+ property instead of "name" - this can be usable when we know that "caption" properties
+ are available for a given type of objects (this is the case for Table Designer fields)
+*/
+class KEXIEXTWIDGETS_EXPORT KexiPropertyEditorView : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KexiPropertyEditorView(KexiMainWindow *mainWin, QWidget* parent);
+ virtual ~KexiPropertyEditorView();
+
+ /*! Helper function. Updates \a infoLabel widget by reusing properties provided
+ by property set \a set.
+ Read documentation of KexiPropertyEditorView class for information about accepted properties.
+ If \a set is 0 and \a textToDisplayForNullSet string is not empty, this string is displayed
+ (without icon or any other additional part).
+ If \a set is 0 and \a textToDisplayForNullSet string is empty, the \a infoLabel widget becomes
+ hidden. */
+ static void updateInfoLabelForPropertySet(
+ KexiObjectInfoLabel *infoLabel, KoProperty::Set* set,
+ const QString& textToDisplayForNullSet = QString::null);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+ KoProperty::Editor *editor() const;
+
+// public slots:
+// virtual void setGeometry( const QRect &r );
+// virtual void resize( int w, int h );
+
+ protected slots:
+ void slotPropertySetChanged(KoProperty::Set* );
+
+ protected:
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kexi/widget/kexiquerydesignersqleditor.cpp b/kexi/widget/kexiquerydesignersqleditor.cpp
new file mode 100644
index 00000000..3ff57989
--- /dev/null
+++ b/kexi/widget/kexiquerydesignersqleditor.cpp
@@ -0,0 +1,35 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ 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 "kexiquerydesignersqleditor.h"
+
+KexiQueryDesignerSQLEditor::KexiQueryDesignerSQLEditor(
+ KexiMainWindow *mainWin, QWidget *parent, const char *name)
+ : KexiEditor(mainWin, parent, name)
+{
+ setHighlightMode("sql");
+}
+
+KexiQueryDesignerSQLEditor::~KexiQueryDesignerSQLEditor()
+{
+}
+
+#include "kexiquerydesignersqleditor.moc"
diff --git a/kexi/widget/kexiquerydesignersqleditor.h b/kexi/widget/kexiquerydesignersqleditor.h
new file mode 100644
index 00000000..a34dbc7a
--- /dev/null
+++ b/kexi/widget/kexiquerydesignersqleditor.h
@@ -0,0 +1,39 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ 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 KEXIQUERYDESIGNERSQLEDITOR_H
+#define KEXIQUERYDESIGNERSQLEDITOR_H
+
+#include "kexieditor.h"
+
+//! Text editor for entering query statements.
+/*! The KexiQueryDesignerSQLEditor class embeds text editor
+ for entering query statements. */
+class KEXIEXTWIDGETS_EXPORT KexiQueryDesignerSQLEditor : public KexiEditor
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryDesignerSQLEditor(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0);
+ virtual ~KexiQueryDesignerSQLEditor();
+};
+
+#endif
diff --git a/kexi/widget/kexiqueryparameters.cpp b/kexi/widget/kexiqueryparameters.cpp
new file mode 100644
index 00000000..449c265c
--- /dev/null
+++ b/kexi/widget/kexiqueryparameters.cpp
@@ -0,0 +1,139 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 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.
+*/
+
+#include "kexiqueryparameters.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <knumvalidator.h>
+
+#include <kexidb/queryschemaparameter.h>
+#include <kexidb/utils.h>
+#include "utils/kexidatetimeformatter.h"
+
+//static
+QValueList<QVariant> KexiQueryParameters::getParameters(QWidget *parent,
+ const KexiDB::Driver &driver, KexiDB::QuerySchema& querySchema, bool &ok)
+{
+ Q_UNUSED(driver);
+ ok = false;
+ const KexiDB::QuerySchemaParameterList params( querySchema.parameters() );
+ QValueList<QVariant> values;
+ const QString caption( i18n("Enter Query Parameter Value", "Enter Parameter Value") );
+ foreach(KexiDB::QuerySchemaParameterListConstIterator, it, params) {
+ switch ((*it).type) {
+ case KexiDB::Field::Byte:
+ case KexiDB::Field::ShortInteger:
+ case KexiDB::Field::Integer:
+ case KexiDB::Field::BigInteger: {
+//! @todo problem for ranges in case of BigInteger - will disappear when we remove use of KInputDialog
+ int minValue, maxValue;
+//! @todo add support for unsigned parameter here
+ KexiDB::getLimitsForType((*it).type, minValue, maxValue);
+ const int result = KInputDialog::getInteger(
+ caption, (*it).message, 0, minValue, maxValue, 1/*step*/, 10/*base*/, &ok, parent);
+ if (!ok)
+ return QValueList<QVariant>(); //cancelled
+ values.append(result);
+ break;
+ }
+ case KexiDB::Field::Boolean: {
+ QStringList list;
+ list << i18n("Boolean True - Yes", "Yes") << i18n("Boolean False - No", "No");
+ const QString result = KInputDialog::getItem(
+ caption, (*it).message, list, 0/*current*/, false /*!editable*/, &ok, parent);
+ if (!ok || result.isEmpty())
+ return QValueList<QVariant>(); //cancelled
+ values.append( QVariant( result==list.first(), 1 ) );
+ break;
+ }
+ case KexiDB::Field::Date: {
+ KexiDateFormatter df;
+ const QString result = KInputDialog::getText(
+ caption, (*it).message, QString::null, &ok, parent, 0/*name*/,
+//! @todo add validator
+ 0/*validator*/, df.inputMask() );
+ if (!ok)
+ return QValueList<QVariant>(); //cancelled
+ values.append( df.stringToDate(result) );
+ break;
+ }
+ case KexiDB::Field::DateTime: {
+ KexiDateFormatter df;
+ KexiTimeFormatter tf;
+ const QString result = KInputDialog::getText(
+ caption, (*it).message, QString::null, &ok, parent, 0/*name*/,
+//! @todo add validator
+ 0/*validator*/, dateTimeInputMask(df, tf) );
+ if (!ok)
+ return QValueList<QVariant>(); //cancelled
+ values.append( stringToDateTime(df, tf, result) );
+ break;
+ }
+ case KexiDB::Field::Time: {
+ KexiTimeFormatter tf;
+ const QString result = KInputDialog::getText(
+ caption, (*it).message, QString::null, &ok, parent, 0/*name*/,
+//! @todo add validator
+ 0/*validator*/, tf.inputMask() );
+ if (!ok)
+ return QValueList<QVariant>(); //cancelled
+ values.append( tf.stringToTime(result) );
+ break;
+ }
+ case KexiDB::Field::Float:
+ case KexiDB::Field::Double: {
+ // KInputDialog::getDouble() does not work well, use getText and double validator
+ KDoubleValidator validator(0);
+ const QString textResult = KInputDialog::getText( caption, (*it).message, QString::null, &ok,
+ parent, 0, &validator);
+ if (!ok || textResult.isEmpty())
+ return QValueList<QVariant>(); //cancelled
+//! @todo this value will be still rounded: consider storing them as a decimal type
+//! (e.g. using a special Q_LLONG+decimalplace class)
+ const double result = textResult.toDouble(&ok); //this is also good for float (to avoid rounding)
+ if (!ok)
+ return QValueList<QVariant>();
+ values.append( result );
+ break;
+ }
+ case KexiDB::Field::Text:
+ case KexiDB::Field::LongText: {
+ const QString result = KInputDialog::getText(
+ caption, (*it).message, QString::null, &ok, parent);
+ if (!ok)
+ return QValueList<QVariant>(); //cancelled
+ values.append( result );
+ break;
+ }
+ case KexiDB::Field::BLOB: {
+//! @todo BLOB input unsupported
+ values.append( QByteArray() );
+ }
+ default:
+ kexiwarn << "KexiQueryParameters::getParameters() unsupported type " << KexiDB::Field::typeName((*it).type)
+ << " for parameter \"" << (*it).message << "\" - aborting query execution!" << endl;
+ return QValueList<QVariant>();
+ }
+ }
+ ok = true;
+ return values;
+}
+
diff --git a/kexi/widget/kexiqueryparameters.h b/kexi/widget/kexiqueryparameters.h
new file mode 100644
index 00000000..40251b71
--- /dev/null
+++ b/kexi/widget/kexiqueryparameters.h
@@ -0,0 +1,44 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 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 KEXIQUERYPARAMETERS_H
+#define KEXIQUERYPARAMETERS_H
+
+#include <kexidb/queryschema.h>
+
+//! @short Utilities providing GUI for getting query parameters
+class KEXIEXTWIDGETS_EXPORT KexiQueryParameters
+{
+ public:
+ /*! Asks for query parameters using KInputDialog, one dialog per query parameter
+ (see @ref KexiDB::QuerySchema::parameters()). The type of each dialog depends
+ on the type of query parameter.
+ \return list of values obtained from the user
+ \a ok is set to true on success and to false on failure. */
+ //! @todo do not use KInputDialog - switch to more powerful custom dialog
+ //! @todo offer option to display one dialog (form) with all the parameters
+ //! @todo support more types (using validators)
+ //! @todo support defaults
+ //! @todo support validation rules, e.g. min/max value, unsigned
+ //! @todo support Enum type (list of strings, need support for keys and user-visible strings)
+ static QValueList<QVariant> getParameters(QWidget *parent, const KexiDB::Driver &driver,
+ KexiDB::QuerySchema& querySchema, bool &ok);
+};
+
+#endif // KEXIDBCONNECTIONWIDGET_H
diff --git a/kexi/widget/kexiscrollview.cpp b/kexi/widget/kexiscrollview.cpp
new file mode 100644
index 00000000..1cf47378
--- /dev/null
+++ b/kexi/widget/kexiscrollview.cpp
@@ -0,0 +1,407 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004 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.
+*/
+#include "kexiscrollview.h"
+
+#include <qcursor.h>
+#include <qobjectlist.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <kstaticdeleter.h>
+#include <klocale.h>
+
+#include <utils/kexirecordnavigator.h>
+#include <core/kexi.h>
+#include <kexiutils/utils.h>
+
+//! @internal
+class KexiScrollViewData
+{
+ public:
+ QPixmap horizontalOuterAreaPixmapBuffer;
+ QPixmap verticalOuterAreaPixmapBuffer;
+};
+
+// @todo warning: not reentrant!
+static KStaticDeleter<KexiScrollViewData> KexiScrollView_data_deleter;
+KexiScrollViewData* KexiScrollView_data = 0;
+
+KexiScrollView::KexiScrollView(QWidget *parent, bool preview)
+ : QScrollView(parent, "kexiscrollview", WStaticContents)
+ , m_widget(0)
+ , m_helpFont(font())
+ , m_preview(preview)
+ , m_scrollViewNavPanel(0)
+{
+ setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
+ viewport()->setPaletteBackgroundColor(colorGroup().mid());
+ QColor fc = palette().active().foreground(),
+ bc = viewport()->paletteBackgroundColor();
+ m_helpColor = KexiUtils::blendedColors(fc, bc, 1, 2);
+// m_helpColor = QColor((fc.red()+bc.red()*2)/3, (fc.green()+bc.green()*2)/3,
+// (fc.blue()+bc.blue()*2)/3);
+ m_helpFont.setPointSize( m_helpFont.pointSize() * 3 );
+
+ setFocusPolicy(WheelFocus);
+
+ //initial resize mode is always manual;
+ //will be changed on show(), if needed
+ setResizePolicy(Manual);
+
+ viewport()->setMouseTracking(true);
+ m_resizing = false;
+ m_enableResizing = true;
+ m_snapToGrid = false;
+ m_gridSize = 0;
+ m_outerAreaVisible = true;
+
+ connect(&m_delayedResize, SIGNAL(timeout()), this, SLOT(refreshContentsSize()));
+ m_smodeSet = false;
+ if (m_preview) {
+ refreshContentsSizeLater(true, true);
+//! @todo allow to hide navigator
+ updateScrollBars();
+ m_scrollViewNavPanel = new KexiRecordNavigator(this, leftMargin(), "nav");
+ m_scrollViewNavPanel->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Preferred);
+ }
+}
+
+KexiScrollView::~KexiScrollView()
+{
+}
+
+void
+KexiScrollView::setWidget(QWidget *w)
+{
+ addChild(w);
+ m_widget = w;
+}
+
+void
+KexiScrollView::setRecordNavigatorVisible(bool visible)
+{
+ if(/*m_scrollViewNavPanel->isVisible() &&*/ !visible)
+ m_scrollViewNavPanel->hide();
+ else if(visible) {
+ m_scrollViewNavPanel->show();
+ updateNavPanelGeometry();
+ }
+}
+
+void
+KexiScrollView::setSnapToGrid(bool enable, int gridSize)
+{
+ m_snapToGrid = enable;
+ if(enable) {
+ m_gridSize = gridSize;
+ }
+}
+
+void
+KexiScrollView::refreshContentsSizeLater(bool horizontal, bool vertical)
+{
+ Q_UNUSED( horizontal );
+ Q_UNUSED( vertical );
+
+ if (!m_smodeSet) {
+ m_smodeSet = true;
+ m_vsmode = vScrollBarMode();
+ m_hsmode = hScrollBarMode();
+ }
+// if (vertical)
+ setVScrollBarMode(QScrollView::AlwaysOff);
+ //if (horizontal)
+ setHScrollBarMode(QScrollView::AlwaysOff);
+ updateScrollBars();
+ m_delayedResize.start( 100, true );
+}
+
+void
+KexiScrollView::refreshContentsSize()
+{
+ if(!m_widget)
+ return;
+ if (m_preview) {
+ resizeContents(m_widget->width(), m_widget->height());
+// kdDebug() << "KexiScrollView::refreshContentsSize(): ( "
+ // << m_widget->width() <<", "<< m_widget->height() << endl;
+ setVScrollBarMode(m_vsmode);
+ setHScrollBarMode(m_hsmode);
+ m_smodeSet = false;
+ updateScrollBars();
+ }
+ else {
+ // Ensure there is always space to resize Form
+ int w = contentsWidth(), h = contentsHeight();
+ bool change = false;
+ const int delta_x = QMAX( (KexiScrollView_data ?
+ KexiScrollView_data->verticalOuterAreaPixmapBuffer.width() : 0), 300);
+ const int delta_y = QMAX( (KexiScrollView_data ?
+ KexiScrollView_data->horizontalOuterAreaPixmapBuffer.height() : 0), 300);
+ if((m_widget->width() + delta_x * 2 / 3) > w) {
+ w = m_widget->width() + delta_x;
+ change = true;
+ }
+ else if((w - m_widget->width()) > delta_x) {
+ w = m_widget->width() + delta_x;
+ change = true;
+ }
+ if((m_widget->height() + delta_y * 2 / 3) > h) {
+ h = m_widget->height() + delta_y;
+ change = true;
+ }
+ else if((h - m_widget->height()) > delta_y) {
+ h = m_widget->height() + delta_y;
+ change = true;
+ }
+ if (change) {
+ repaint();
+ viewport()->repaint();
+ repaintContents();
+ updateContents(0, 0, 2000,2000);
+ clipper()->repaint();
+
+ resizeContents(w, h);
+ }
+// kdDebug() << "KexiScrollView::refreshContentsSize(): ( "
+ // << contentsWidth() <<", "<< contentsHeight() << endl;
+ updateScrollBars();
+ setVScrollBarMode(Auto);
+ setHScrollBarMode(Auto);
+ }
+ updateContents();
+ updateScrollBars();
+}
+
+void
+KexiScrollView::updateNavPanelGeometry()
+{
+ if (m_scrollViewNavPanel)
+ m_scrollViewNavPanel->updateGeometry(leftMargin());
+}
+
+void
+KexiScrollView::contentsMousePressEvent(QMouseEvent *ev)
+{
+ if(!m_widget)
+ return;
+
+ QRect r3(0, 0, m_widget->width() + 4, m_widget->height() + 4);
+ if(!r3.contains(ev->pos())) // clicked outside form
+ //m_form->resetSelection();
+ emit outerAreaClicked();
+
+ if(!m_enableResizing)
+ return;
+
+ QRect r(m_widget->width(), 0, 4, m_widget->height() + 4); // right limit
+ QRect r2(0, m_widget->height(), m_widget->width() + 4, 4); // bottom limit
+ if(r.contains(ev->pos()) || r2.contains(ev->pos()))
+ {
+ m_resizing = true;
+ emit resizingStarted();
+ }
+}
+
+void
+KexiScrollView::contentsMouseReleaseEvent(QMouseEvent *)
+{
+ if(m_resizing) {
+ m_resizing = false;
+ emit resizingEnded();
+ }
+
+ unsetCursor();
+}
+
+void
+KexiScrollView::contentsMouseMoveEvent(QMouseEvent *ev)
+{
+ if(!m_widget || !m_enableResizing)
+ return;
+
+ if(m_resizing) // resize widget
+ {
+ int tmpx = ev->x(), tmpy = ev->y();
+ const int exceeds_x = (tmpx - contentsX() + 5) - clipper()->width();
+ const int exceeds_y = (tmpy - contentsY() + 5) - clipper()->height();
+ if (exceeds_x > 0)
+ tmpx -= exceeds_x;
+ if (exceeds_y > 0)
+ tmpy -= exceeds_y;
+ if ((tmpx - contentsX()) < 0)
+ tmpx = contentsX();
+ if ((tmpy - contentsY()) < 0)
+ tmpy = contentsY();
+
+ // we look for the max widget right() (or bottom()), which would be the limit for form resizing (not to hide widgets)
+ QObjectList *list = m_widget->queryList("QWidget", 0, true, false /* not recursive*/);
+ for(QObject *o = list->first(); o; o = list->next())
+ {
+ QWidget *w = (QWidget*)o;
+ tmpx = QMAX(tmpx, (w->geometry().right() + 10));
+ tmpy = QMAX(tmpy, (w->geometry().bottom() + 10));
+ }
+ delete list;
+
+ int neww = -1, newh;
+ if(cursor().shape() == QCursor::SizeHorCursor)
+ {
+ if(m_snapToGrid)
+ neww = int( float(tmpx) / float(m_gridSize) + 0.5 ) * m_gridSize;
+ else
+ neww = tmpx;
+ newh = m_widget->height();
+ }
+ else if(cursor().shape() == QCursor::SizeVerCursor)
+ {
+ neww = m_widget->width();
+ if(m_snapToGrid)
+ newh = int( float(tmpy) / float(m_gridSize) + 0.5 ) * m_gridSize;
+ else
+ newh = tmpy;
+ }
+ else if(cursor().shape() == QCursor::SizeFDiagCursor)
+ {
+ if(m_snapToGrid) {
+ neww = int( float(tmpx) / float(m_gridSize) + 0.5 ) * m_gridSize;
+ newh = int( float(tmpy) / float(m_gridSize) + 0.5 ) * m_gridSize;
+ } else {
+ neww = tmpx;
+ newh = tmpy;
+ }
+ }
+ //needs update?
+ if (neww!=-1 && m_widget->size() != QSize(neww, newh)) {
+ m_widget->resize( neww, newh );
+ refreshContentsSize();
+ updateContents();
+ }
+ }
+ else // update mouse cursor
+ {
+ QPoint p = ev->pos();
+ QRect r(m_widget->width(), 0, 4, m_widget->height()); // right
+ QRect r2(0, m_widget->height(), m_widget->width(), 4); // bottom
+ QRect r3(m_widget->width(), m_widget->height(), 4, 4); // bottom-right corner
+
+ if(r.contains(p))
+ setCursor(QCursor::SizeHorCursor);
+ else if(r2.contains(p))
+ setCursor(QCursor::SizeVerCursor);
+ else if(r3.contains(p))
+ setCursor(QCursor::SizeFDiagCursor);
+ else
+ unsetCursor();
+ }
+}
+
+void
+KexiScrollView::setupPixmapBuffer(QPixmap& pixmap, const QString& text, int lines)
+{
+ Q_UNUSED( lines );
+
+ QFontMetrics fm(m_helpFont);
+ const int flags = Qt::AlignCenter|Qt::AlignTop;
+ QRect rect(fm.boundingRect(0,0,1000,1000,flags,text));
+ const int txtw = rect.width(), txth = rect.height();//fm.width(text), txth = fm.height()*lines;
+ pixmap = QPixmap(txtw, txth);
+ if (!pixmap.isNull()) {
+ //create pixmap once
+ pixmap.fill( viewport()->paletteBackgroundColor() );
+ QPainter pb(&pixmap, this);
+ pb.setPen(m_helpColor);
+ pb.setFont(m_helpFont);
+ pb.drawText(0, 0, txtw, txth, Qt::AlignCenter|Qt::AlignTop, text);
+ }
+}
+
+void
+KexiScrollView::drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph )
+{
+ QScrollView::drawContents(p, clipx, clipy, clipw, cliph);
+ if (m_widget) {
+ if(m_preview || !m_outerAreaVisible)
+ return;
+
+ //draw right and bottom borders
+ const int wx = childX(m_widget);
+ const int wy = childY(m_widget);
+ p->setPen(palette().active().foreground());
+ p->drawLine(wx+m_widget->width(), wy, wx+m_widget->width(), wy+m_widget->height());
+ p->drawLine(wx, wy+m_widget->height(), wx+m_widget->width(), wy+m_widget->height());
+//kdDebug() << "KexiScrollView::drawContents() " << wy+m_widget->height() << endl;
+
+ if (!KexiScrollView_data) {
+ KexiScrollView_data_deleter.setObject( KexiScrollView_data, new KexiScrollViewData() );
+
+ //create flicker-less buffer
+ setupPixmapBuffer( KexiScrollView_data->horizontalOuterAreaPixmapBuffer, i18n("Outer Area"), 1 );
+ setupPixmapBuffer( KexiScrollView_data->verticalOuterAreaPixmapBuffer, i18n("Outer\nArea"), 2 );
+ }
+ if (!KexiScrollView_data->horizontalOuterAreaPixmapBuffer.isNull()
+ && !KexiScrollView_data->verticalOuterAreaPixmapBuffer.isNull()
+ && !m_delayedResize.isActive() /* only draw text if there's not pending delayed resize*/)
+ {
+ if (m_widget->height()>(KexiScrollView_data->verticalOuterAreaPixmapBuffer.height()+20)) {
+ p->drawPixmap(
+ QMAX( m_widget->width(), KexiScrollView_data->verticalOuterAreaPixmapBuffer.width() + 20 ) + 20,
+ QMAX( (m_widget->height() - KexiScrollView_data->verticalOuterAreaPixmapBuffer.height())/2, 20 ),
+ KexiScrollView_data->verticalOuterAreaPixmapBuffer
+ );
+ }
+ p->drawPixmap(
+ QMAX( (m_widget->width() - KexiScrollView_data->horizontalOuterAreaPixmapBuffer.width())/2, 20 ),
+ QMAX( m_widget->height(), KexiScrollView_data->horizontalOuterAreaPixmapBuffer.height() + 20 ) + 20,
+ KexiScrollView_data->horizontalOuterAreaPixmapBuffer
+ );
+ }
+ }
+}
+
+void
+KexiScrollView::leaveEvent( QEvent *e )
+{
+ QWidget::leaveEvent(e);
+ m_widget->update(); //update form elements on too fast mouse move
+}
+
+void
+KexiScrollView::setHBarGeometry( QScrollBar & hbar, int x, int y, int w, int h )
+{
+/*todo*/
+// kdDebug(44021)<<"KexiScrollView::setHBarGeometry"<<endl;
+ if (m_scrollViewNavPanel && m_scrollViewNavPanel->isVisible()) {
+ m_scrollViewNavPanel->setHBarGeometry( hbar, x, y, w, h );
+ }
+ else {
+ hbar.setGeometry( x, y, w, h );
+ }
+}
+
+KexiRecordNavigator*
+KexiScrollView::recordNavigator() const
+{
+ return m_scrollViewNavPanel;
+}
+
+#include "kexiscrollview.moc"
+
diff --git a/kexi/widget/kexiscrollview.h b/kexi/widget/kexiscrollview.h
new file mode 100644
index 00000000..c313f2d7
--- /dev/null
+++ b/kexi/widget/kexiscrollview.h
@@ -0,0 +1,93 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004 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 KEXISCROLLVIEW_H
+#define KEXISCROLLVIEW_H
+
+#include <qscrollview.h>
+#include <qtimer.h>
+
+class QColor;
+class QFont;
+class KexiRecordNavigator;
+
+//! The scrollview which includes KexiDBForm
+/*! It allows to resize its m_widget, following snapToGrid setting.
+ Its contents is resized so the widget can always be resized. */
+class KEXIEXTWIDGETS_EXPORT KexiScrollView : public QScrollView
+{
+ Q_OBJECT
+
+ public:
+ KexiScrollView(QWidget *parent, bool preview);
+ virtual ~KexiScrollView();
+
+ void setWidget(QWidget *w);
+ void setSnapToGrid(bool enable, int gridSize=10);
+
+ void setResizingEnabled(bool enabled) { m_enableResizing = enabled; }
+ void setRecordNavigatorVisible(bool visible);
+
+ void setOuterAreaIndicatorVisible(bool visible) { m_outerAreaVisible = visible; }
+
+ void refreshContentsSizeLater(bool horizontal, bool vertical);
+ void updateNavPanelGeometry();
+
+ KexiRecordNavigator* recordNavigator() const;
+
+ inline bool preview() const { return m_preview; }
+
+ public slots:
+ /*! Make sure there is a 300px margin around the form contents to allow resizing. */
+ virtual void refreshContentsSize();
+
+ signals:
+ void outerAreaClicked();
+ void resizingStarted();
+ void resizingEnded();
+
+ protected:
+ virtual void contentsMousePressEvent(QMouseEvent * ev);
+ virtual void contentsMouseReleaseEvent(QMouseEvent * ev);
+ virtual void contentsMouseMoveEvent(QMouseEvent * ev);
+ virtual void drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph );
+ virtual void leaveEvent( QEvent *e );
+ virtual void setHBarGeometry( QScrollBar & hbar, int x, int y, int w, int h );
+ void setupPixmapBuffer(QPixmap& pixmap, const QString& text, int lines);
+
+ bool m_resizing;
+ bool m_enableResizing;
+ QWidget *m_widget;
+
+ int m_gridSize;
+ QFont m_helpFont;
+ QColor m_helpColor;
+ QTimer m_delayedResize;
+ //! for refreshContentsSizeLater()
+ QScrollView::ScrollBarMode m_vsmode, m_hsmode;
+ bool m_snapToGrid : 1;
+ bool m_preview : 1;
+ bool m_smodeSet : 1;
+ bool m_outerAreaVisible : 1;
+ KexiRecordNavigator* m_scrollViewNavPanel;
+};
+
+#endif
+
diff --git a/kexi/widget/kexisectionheader.cpp b/kexi/widget/kexisectionheader.cpp
new file mode 100644
index 00000000..42c5031e
--- /dev/null
+++ b/kexi/widget/kexisectionheader.cpp
@@ -0,0 +1,162 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 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.
+*/
+
+#include "kexisectionheader.h"
+#include "kexiviewbase.h"
+#include <kexiutils/utils.h>
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qtooltip.h>
+
+#include <kiconloader.h>
+#include <kpushbutton.h>
+
+class KexiSectionHeader::BoxLayout : public QBoxLayout
+{
+ public:
+ BoxLayout( KexiSectionHeader* parent, Direction d, int margin = 0,
+ int spacing = -1, const char * name = 0 );
+ virtual void addItem( QLayoutItem * item );
+ QGuardedPtr<KexiViewBase> view;
+};
+
+//==========================
+
+//! @internal
+class KexiSectionHeaderPrivate
+{
+ public:
+ KexiSectionHeaderPrivate()
+ {
+ }
+
+ Qt::Orientation orientation;
+ QLabel *lbl;
+ KexiSectionHeader::BoxLayout *lyr;
+ QHBox *lbl_b;
+};
+
+//==========================
+
+KexiSectionHeader::KexiSectionHeader(const QString &caption, Orientation o, QWidget* parent )
+ : QWidget(parent, "KexiSectionHeader")
+ , d( new KexiSectionHeaderPrivate() )
+{
+ d->orientation = o;
+ d->lyr = new BoxLayout( this, d->orientation==Vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight );
+ d->lyr->setAutoAdd(true);
+ d->lbl_b = new QHBox(this);
+ d->lbl = new QLabel(QString(" ")+caption, d->lbl_b);
+ d->lbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+ d->lbl->setFocusPolicy(StrongFocus);
+ d->lbl->installEventFilter(this);
+ installEventFilter(this);
+ setCaption(caption);
+}
+
+KexiSectionHeader::~KexiSectionHeader()
+{
+ delete d;
+}
+
+void KexiSectionHeader::addButton(const QString& icon, const QString& toolTip,
+ const QObject * receiver, const char * member)
+{
+ KPushButton *btn = new KPushButton(d->lbl_b);
+ btn->setFlat(true);
+ btn->setFocusPolicy(NoFocus);
+ btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ if (receiver && member) {
+ connect(btn, SIGNAL(clicked()), receiver, member);
+ }
+
+ if (!icon.isEmpty()) {
+ QIconSet iset = SmallIconSet(icon);
+ btn->setIconSet( iset );
+ QFontMetrics fm(d->lbl->font());
+ btn->setMaximumHeight( QMAX(fm.height(), 16) );
+ }
+ if (!toolTip.isEmpty()) {
+ QToolTip::add(btn, toolTip);
+ }
+}
+
+bool KexiSectionHeader::eventFilter( QObject *o, QEvent *e )
+{
+ if (o == d->lbl && e->type()==QEvent::MouseButtonRelease) {//|| e->type()==QEvent::FocusOut) {// && o->inherits("QWidget")) {
+ if (d->lyr->view)
+ d->lyr->view->setFocus();
+// if (KexiUtils::hasParent( this, static_cast<QWidget*>(o))) {
+// d->lbl->setPaletteBackgroundColor( e->type()==QEvent::FocusIn ? red : blue);
+// }
+ }
+ return QWidget::eventFilter(o,e);
+}
+
+void KexiSectionHeader::slotFocus(bool in)
+{
+ in = in || focusWidget()==this;
+ d->lbl->setPaletteBackgroundColor(
+ in ? palette().active().color(QColorGroup::Highlight) : palette().active().color(QColorGroup::Background) );
+ d->lbl->setPaletteForegroundColor(
+ in ? palette().active().color(QColorGroup::HighlightedText) : palette().active().color(QColorGroup::Foreground) );
+}
+
+QSize KexiSectionHeader::sizeHint() const
+{
+ if (!d->lyr->view)
+ return QWidget::sizeHint();
+ QSize s = d->lyr->view->sizeHint();
+ return QSize(s.width(), d->lbl->sizeHint().height() + s.height());
+}
+
+/*void KexiSectionHeader::setFocus()
+{
+ if (d->lyr->view)
+ d->lyr->view->setFocus();
+ else
+ QWidget::setFocus();
+}*/
+
+//======================
+
+KexiSectionHeader::BoxLayout::BoxLayout( KexiSectionHeader* parent, Direction d, int margin, int spacing, const char * name )
+ : QBoxLayout(parent, d, margin, spacing, name )
+{
+}
+
+void KexiSectionHeader::BoxLayout::addItem( QLayoutItem * item )
+{
+ QBoxLayout::addItem( item );
+ if (item->widget()) {
+ item->widget()->installEventFilter( mainWidget() );
+ if (item->widget()->inherits("KexiViewBase")) {
+ view = static_cast<KexiViewBase*>(item->widget());
+ KexiSectionHeader *sh = static_cast<KexiSectionHeader*>(mainWidget());
+ connect(view,SIGNAL(focus(bool)),sh,SLOT(slotFocus(bool)));
+ sh->d->lbl->setBuddy(item->widget());
+ }
+ }
+}
+
+
+#include "kexisectionheader.moc"
+
diff --git a/kexi/widget/kexisectionheader.h b/kexi/widget/kexisectionheader.h
new file mode 100644
index 00000000..b842f6ff
--- /dev/null
+++ b/kexi/widget/kexisectionheader.h
@@ -0,0 +1,54 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 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 KEXISECTIONHEADER_H
+#define KEXISECTIONHEADER_H
+
+#include <qwidget.h>
+
+class KexiSectionHeaderPrivate;
+
+class KEXIEXTWIDGETS_EXPORT KexiSectionHeader : public QWidget
+{
+ Q_OBJECT
+ public:
+ class BoxLayout;
+
+ KexiSectionHeader(const QString &caption, Orientation o,
+ QWidget* parent = 0 );
+
+ virtual ~KexiSectionHeader();
+
+ void addButton(const QString& icon, const QString& toolTip,
+ const QObject * receiver, const char * member);
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ virtual QSize sizeHint() const;
+
+ public slots:
+ void slotFocus(bool in);
+
+ protected:
+ KexiSectionHeaderPrivate *d;
+ friend class BoxLayout;
+};
+
+#endif
+
diff --git a/kexi/widget/kexismalltoolbutton.cpp b/kexi/widget/kexismalltoolbutton.cpp
new file mode 100644
index 00000000..085d4847
--- /dev/null
+++ b/kexi/widget/kexismalltoolbutton.cpp
@@ -0,0 +1,133 @@
+/* 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 "kexismalltoolbutton.h"
+
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qstyle.h>
+
+#include <kiconloader.h>
+#include <kglobalsettings.h>
+
+#include <core/kexi.h>
+
+KexiSmallToolButton::KexiSmallToolButton(QWidget* parent, const QString& text,
+ const QString& icon, const char* name)
+ : QToolButton(parent, name)
+{
+ init();
+ update(text, SmallIconSet(icon));
+}
+
+KexiSmallToolButton::KexiSmallToolButton(QWidget* parent, const QString& text,
+ const QIconSet& iconSet, const char* name)
+ : QToolButton(parent, name)
+{
+ init();
+ update(text, iconSet);
+}
+
+KexiSmallToolButton::KexiSmallToolButton(QWidget* parent, KAction* action)
+ : QToolButton(parent, action->name())
+ , m_action(action)
+{
+ init();
+ connect(this, SIGNAL(clicked()), action, SLOT(activate()));
+ connect(action, SIGNAL(enabled(bool)), this, SLOT(setEnabled(bool)));
+ updateAction();
+}
+
+KexiSmallToolButton::~KexiSmallToolButton()
+{
+}
+
+void KexiSmallToolButton::updateAction()
+{
+ if (!m_action)
+ return;
+ update(m_action->text(), m_action->iconSet(KIcon::Small));
+ setAccel(m_action->shortcut());
+ QToolTip::add(this, m_action->toolTip());
+ QWhatsThis::add(this, m_action->whatsThis());
+}
+
+void KexiSmallToolButton::init()
+{
+ setPaletteBackgroundColor(palette().active().background());
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ QFont f(KGlobalSettings::toolBarFont());
+ f.setPixelSize(Kexi::smallFont().pixelSize());
+ setFont(f);
+ setAutoRaise(true);
+}
+
+void KexiSmallToolButton::update(const QString& text, const QIconSet& iconSet, bool tipToo)
+{
+ int width = 0;
+ if (text.isEmpty()) {
+ width = 10;
+ setUsesTextLabel(false);
+ }
+ else {
+ width += QFontMetrics(font()).width(text+" ");
+ setUsesTextLabel(true);
+ setTextPosition(QToolButton::Right);
+ QToolButton::setTextLabel(text, tipToo);
+ }
+ if (!iconSet.isNull()) {
+ width += IconSize(KIcon::Small);
+ QToolButton::setIconSet(iconSet);
+ }
+ setFixedWidth( width );
+}
+
+void KexiSmallToolButton::setIconSet( const QIconSet& iconSet )
+{
+ update(textLabel(), iconSet);
+}
+
+void KexiSmallToolButton::setIconSet( const QString& icon )
+{
+ setIconSet( SmallIconSet(icon) );
+}
+
+void KexiSmallToolButton::setTextLabel( const QString & newLabel, bool tipToo )
+{
+ Q_UNUSED( tipToo );
+
+ update(newLabel, iconSet());
+}
+
+void KexiSmallToolButton::drawButton( QPainter *_painter )
+{
+ QToolButton::drawButton(_painter);
+ if (QToolButton::popup()) {
+ QStyle::SFlags arrowFlags = QStyle::Style_Default;
+ if (isDown())
+ arrowFlags |= QStyle::Style_Down;
+ if (isEnabled())
+ arrowFlags |= QStyle::Style_Enabled;
+ style().drawPrimitive(QStyle::PE_ArrowDown, _painter,
+ QRect(width()-7, height()-7, 5, 5), colorGroup(),
+ arrowFlags, QStyleOption() );
+ }
+}
+
+#include "kexismalltoolbutton.moc"
diff --git a/kexi/widget/kexismalltoolbutton.h b/kexi/widget/kexismalltoolbutton.h
new file mode 100644
index 00000000..59af7cfa
--- /dev/null
+++ b/kexi/widget/kexismalltoolbutton.h
@@ -0,0 +1,59 @@
+/* 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 KEXISMALLTOOLBUTTON_H
+#define KEXISMALLTOOLBUTTON_H
+
+#include <qtoolbutton.h>
+#include <kaction.h>
+
+class QIconSet;
+
+//! A small tool button with icon and optional text
+class KEXIEXTWIDGETS_EXPORT KexiSmallToolButton : public QToolButton
+{
+ Q_OBJECT
+
+ public:
+ KexiSmallToolButton(QWidget* parent, const QString& text,
+ const QString& icon = QString::null, const char* name = 0);
+
+ KexiSmallToolButton(QWidget* parent, const QString& text,
+ const QIconSet& iconSet, const char* name = 0);
+
+ KexiSmallToolButton(QWidget* parent, KAction *action);
+
+ virtual ~KexiSmallToolButton();
+
+ void updateAction();
+
+ virtual void setIconSet( const QIconSet& iconSet );
+ virtual void setIconSet( const QString& icon );
+ virtual void setTextLabel( const QString & newLabel, bool tipToo );
+ virtual void setTextLabel( const QString & newLabel ) { setTextLabel(newLabel, false); }
+
+ protected:
+ void update(const QString& text, const QIconSet& iconSet, bool tipToo = false);
+ void init();
+ virtual void drawButton( QPainter *_painter );
+
+ QGuardedPtr<KAction> m_action;
+};
+
+#endif
diff --git a/kexi/widget/pixmapcollection.cpp b/kexi/widget/pixmapcollection.cpp
new file mode 100644
index 00000000..7f9718d6
--- /dev/null
+++ b/kexi/widget/pixmapcollection.cpp
@@ -0,0 +1,440 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 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 <qpixmap.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qstringlist.h>
+#include <qtoolbutton.h>
+#include <qdom.h>
+
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kfiledialog.h>
+#include <kcombobox.h>
+#include <kicondialog.h>
+#include <klineedit.h>
+#include <kicontheme.h>
+#include <kpixmapio.h>
+#include <kpopupmenu.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "pixmapcollection.h"
+
+/// Pixmap Collection
+PixmapCollection::PixmapCollection(const QString &collectionName, QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+ m_name = collectionName;
+}
+
+QString
+PixmapCollection::addPixmapPath(const KURL &url)
+{
+ QString name = url.filename();
+ while(m_pixmaps.contains(name))
+ {
+ bool ok;
+ int num = name.right(1).toInt(&ok, 10);
+ if(ok)
+ name = name.left(name.length()-1) + QString::number(num+1);
+ else
+ name += "2";
+ }
+
+ m_pixmaps.insert(name, qMakePair(url.path(), 0));
+ return name;
+}
+
+QString
+PixmapCollection::addPixmapName(const QString &icon, int size)
+{
+ QString name = icon;
+ while(m_pixmaps.contains(name))
+ {
+ bool ok;
+ int num = name.right(1).toInt(&ok, 10);
+ if(ok)
+ name = name.left(name.length()-1) + QString::number(num+1);
+ else
+ name += "2";
+ }
+
+ m_pixmaps.insert(name, qMakePair(icon, size));
+ return name;
+}
+
+void
+PixmapCollection::removePixmap(const QString &name)
+{
+ m_pixmaps.remove(name);
+}
+
+QPixmap
+PixmapCollection::getPixmap(const QString &name)
+{
+ if(!m_pixmaps.contains(name))
+ {
+ kdDebug() << " The icon " << name << " you requested is not in the collection" << endl;
+ return QPixmap();
+ }
+
+ if(m_pixmaps[name].second != 0)
+ {
+ return kapp->iconLoader()->loadIcon(m_pixmaps[name].first, KIcon::NoGroup, m_pixmaps[name].second);
+ }
+ else
+ return QPixmap(m_pixmaps[name].first);
+}
+
+bool
+PixmapCollection::contains(const QString &name)
+{
+ return m_pixmaps.contains(name);
+}
+
+void
+PixmapCollection::save(QDomNode parentNode)
+{
+ if(m_pixmaps.isEmpty())
+ return;
+
+ QDomDocument domDoc = parentNode.ownerDocument();
+ QDomElement collection = domDoc.createElement("collection");
+ parentNode.appendChild(collection);
+
+ PixmapMap::ConstIterator it;
+ PixmapMap::ConstIterator endIt = m_pixmaps.constEnd();
+ for(it = m_pixmaps.constBegin(); it != endIt; ++it)
+ {
+ QDomElement item = domDoc.createElement("pixmap");
+ collection.appendChild(item);
+ item.setAttribute("name", it.key());
+ if(it.data().second != 0)
+ item.setAttribute("size", QString::number(it.data().second));
+
+ QString text = it.data().first;
+ QDomText textNode = domDoc.createTextNode(text);
+ item.appendChild(textNode);
+ }
+}
+
+void
+PixmapCollection::load(QDomNode node)
+{
+ QDomDocument domDoc = node.ownerDocument();
+ for(QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling())
+ {
+ QDomElement el = n.toElement();
+ QPair<QString, int> pair = qMakePair(el.text(), el.attribute("size").toInt());
+ m_pixmaps[el.attribute("name")] = pair;
+ }
+}
+
+//// A dialog to load a KDE icon by its name
+LoadIconDialog::LoadIconDialog(QWidget *parent)
+: KDialogBase(parent, "loadicon_dialog", true, i18n("Load KDE Icon by Name"), Ok|Cancel, Ok, false)
+{
+ QFrame *frame = makeMainWidget();
+ QGridLayout *l = new QGridLayout(frame, 2, 3, 0, 6);
+
+ // Name input
+ QLabel *name = new QLabel(i18n("&Name:"), frame);
+ l->addWidget(name, 0, 0);
+ name->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ m_nameInput = new KLineEdit("kexi", frame);
+ l->addWidget(m_nameInput, 0, 1);
+ name->setBuddy(m_nameInput);
+
+ // Choose size
+ QLabel *size = new QLabel(i18n("&Size:"), frame);
+ l->addWidget(size, 1, 0);
+ size->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+
+ KComboBox *combo = new KComboBox(frame);
+ l->addWidget(combo, 1, 1);
+ size->setBuddy(combo);
+ QStringList list;
+ list << i18n("Small") << i18n("Medium") << i18n("Large") << i18n("Huge");
+ combo->insertStringList(list);
+ combo->setCurrentItem(2);
+ connect(combo, SIGNAL(activated(int)), this, SLOT(changeIconSize(int)));
+
+
+ // Icon chooser button
+ m_button = new KIconButton(frame);
+ m_button->setIcon("kexi");
+ m_button->setIconSize(KIcon::SizeMedium);
+ l->addMultiCellWidget(m_button, 0, 1, 2, 2);
+ connect(m_button, SIGNAL(iconChanged(QString)), this, SLOT(updateIconName(QString)));
+ connect(m_nameInput, SIGNAL(textChanged(const QString &)), this, SLOT(setIcon(const QString &)));
+}
+
+void
+LoadIconDialog::updateIconName(QString icon)
+{
+ m_nameInput->setText(icon);
+}
+
+void
+LoadIconDialog::setIcon(const QString &icon)
+{
+ m_button->setIcon(icon);
+}
+
+void
+LoadIconDialog::changeIconSize(int index)
+{
+ int size = KIcon::SizeMedium;
+ switch(index)
+ {
+ case 0: size = KIcon::SizeSmall; break;
+ //case 1: size = KIcon::SizeSmallMedium; break;
+ case 1: size = KIcon::SizeMedium; break;
+ case 2: size = KIcon::SizeLarge; break;
+#if !defined(Q_WS_WIN) && KDE_IS_VERSION(3,1,9)
+ case 3: size = KIcon::SizeHuge; break;
+#endif
+ default:;
+ }
+
+ m_button->setIconSize(size);
+}
+
+int LoadIconDialog::iconSize()
+{
+ return m_button->iconSize();
+}
+
+QString LoadIconDialog::iconName()
+{
+ return m_button->icon();
+}
+
+/// Pixmap Collection Editor Dialog
+PixmapCollectionEditor::PixmapCollectionEditor(PixmapCollection *collection, QWidget *parent)
+: KDialogBase(parent, "pixcollection_dialog", true,
+ i18n("Edit Pixmap Collection: %1").arg(collection->collectionName()), Close, Close, false)
+{
+ m_collection = collection;
+ QFrame *frame = makeMainWidget();
+ QHBoxLayout *l = new QHBoxLayout(frame, 0, 6);
+ setInitialSize(QSize(400, 200), true);
+
+ //// Setup the icon toolbar /////////////////
+ QVBoxLayout *vlayout = new QVBoxLayout(l, 3);
+ QToolButton *newItemPath = new QToolButton(frame);
+ newItemPath->setIconSet(BarIconSet("fileopen"));
+ newItemPath->setTextLabel(i18n("&Add File"), true);
+ vlayout->addWidget(newItemPath);
+ m_buttons.insert(BNewItemPath, newItemPath);
+ connect(newItemPath, SIGNAL(clicked()), this, SLOT(newItemByPath()));
+
+ QToolButton *newItemName = new QToolButton(frame);
+ newItemName->setIconSet(BarIconSet("icons"));
+ newItemName->setTextLabel(i18n("&Add an Icon"), true);
+ vlayout->addWidget(newItemName);
+ m_buttons.insert(BNewItemName, newItemName);
+ connect(newItemName, SIGNAL(clicked()), this, SLOT(newItemByName()));
+
+ QToolButton *delItem = new QToolButton(frame);
+ delItem->setIconSet(BarIconSet("edit_remove"));
+ delItem->setTextLabel(i18n("&Remove Selected Item"), true);
+ vlayout->addWidget(delItem);
+ m_buttons.insert(BDelItem, delItem);
+ connect(delItem, SIGNAL(clicked()), this, SLOT(removeItem()));
+ vlayout->addStretch();
+
+ // Setup the iconView
+ m_iconView = new KIconView(frame, "pixcollection_iconView");
+ m_iconView->resize(100,100);
+ m_iconView->setArrangement(QIconView::LeftToRight);
+ m_iconView->setAutoArrange(true);
+ m_iconView->setMode(KIconView::Select);
+ l->addWidget(m_iconView);
+ connect(m_iconView, SIGNAL(contextMenuRequested(QIconViewItem*, const QPoint&)), this, SLOT(displayMenu(QIconViewItem*, const QPoint&)));
+ connect(m_iconView, SIGNAL(itemRenamed(QIconViewItem*, const QString &)), this, SLOT(renameCollectionItem(QIconViewItem*, const QString&)));
+
+ PixmapMap::ConstIterator it;
+ PixmapMap::ConstIterator endIt = collection->m_pixmaps.end();
+ for(it = collection->m_pixmaps.constBegin(); it != endIt; ++it)
+ createIconViewItem(it.key());
+}
+
+void
+PixmapCollectionEditor::newItemByName()
+{
+ LoadIconDialog d(parentWidget());
+ if(d.exec()== QDialog::Accepted)
+ {
+ if(d.iconName().isEmpty())
+ return;
+
+ QString name = m_collection->addPixmapName(d.iconName(), d.iconSize());
+ createIconViewItem(name);
+ }
+}
+
+void
+PixmapCollectionEditor::newItemByPath()
+{
+ KURL url = KFileDialog::getImageOpenURL("::kexi", parentWidget());
+ if(url.isEmpty())
+ return;
+ QString name = m_collection->addPixmapPath(url);
+ createIconViewItem(name);
+}
+
+void
+PixmapCollectionEditor::removeItem()
+{
+ QIconViewItem *item = m_iconView->currentItem();
+ if( !item )
+ return;
+
+ int confirm = KMessageBox::questionYesNo(parentWidget(), QString("<qt>")+
+ i18n("Do you want to remove item \"%1\" from collection \"%2\"?")
+ .arg(item->text()).arg(m_collection->collectionName()) + "</qt>");
+ if(confirm == KMessageBox::No)
+ return;
+
+ m_collection->removePixmap(item->text());
+ delete item;
+}
+
+void
+PixmapCollectionEditor::renameItem()
+{
+ if(m_iconView->currentItem())
+ m_iconView->currentItem()->rename();
+}
+
+void
+PixmapCollectionEditor::createIconViewItem(const QString &name)
+{
+ PixmapIconViewItem *item = new PixmapIconViewItem(m_iconView, name, getPixmap(name));
+ item->setRenameEnabled(true);
+}
+
+QPixmap
+PixmapCollectionEditor::getPixmap(const QString &name)
+{
+ QPixmap pixmap = m_collection->getPixmap(name);
+ if((pixmap.width() <= 48) && (pixmap.height() <= 48))
+ return pixmap;
+
+ KPixmapIO io;
+ QImage image = io.convertToImage(pixmap);
+ pixmap = io.convertToPixmap(image.scale(48, 48, QImage::ScaleMin));
+ return pixmap;
+}
+
+void
+PixmapCollectionEditor::renameCollectionItem(QIconViewItem *it, const QString &name)
+{
+ PixmapIconViewItem *item = static_cast<PixmapIconViewItem*>(it);
+ if(!m_collection->m_pixmaps.contains(item->name()))
+ return;
+
+ // We just rename the collection item
+ QPair<QString, int> pair = m_collection->m_pixmaps[item->name()];
+ m_collection->m_pixmaps.remove(item->name());
+ m_collection->m_pixmaps[name] = pair;
+ item->setName(name);
+}
+
+void
+PixmapCollectionEditor::displayMenu(QIconViewItem *it, const QPoint &p)
+{
+ if(!it) return;
+ KPopupMenu *menu = new KPopupMenu();
+ menu->insertItem(SmallIconSet("edit"), i18n("Rename Item"), this, SLOT(renameItem()));
+ menu->insertItem(SmallIconSet("remove"), i18n("Remove Item"), this, SLOT(removeItem()));
+ menu->exec(p);
+}
+
+//// A Dialog to choose a pixmap from the PixmapCollection
+PixmapCollectionChooser::PixmapCollectionChooser(PixmapCollection *collection, const QString &selectedItem, QWidget *parent)
+: KDialogBase(parent, "pixchoose_dialog", true, i18n("Select Pixmap From %1").arg(collection->collectionName()),
+ User1|Ok|Cancel, Ok, false, KGuiItem(i18n("Edit Collection...")))
+{
+ m_collection = collection;
+ setInitialSize(QSize(400, 200), true);
+
+ m_iconView = new KIconView(this, "pixchooser_iconView");
+ setMainWidget(m_iconView);
+ m_iconView->setArrangement(QIconView::LeftToRight);
+ m_iconView->setAutoArrange(true);
+ m_iconView->setMode(KIconView::Select);
+
+ PixmapMap::ConstIterator it;
+ PixmapMap::ConstIterator endIt = collection->m_pixmaps.constEnd();
+ for(it = collection->m_pixmaps.constBegin(); it != endIt; ++it)
+ new PixmapIconViewItem(m_iconView, it.key(), getPixmap(it.key()));
+
+ QIconViewItem *item = m_iconView->findItem(selectedItem, Qt::ExactMatch);
+ if(item && !selectedItem.isEmpty())
+ m_iconView->setCurrentItem(item);
+}
+
+QPixmap
+PixmapCollectionChooser::pixmap()
+{
+ if(! m_iconView->currentItem())
+ return QPixmap();
+ QString name = m_iconView->currentItem()->text();
+ return m_collection->getPixmap(name);
+}
+
+QString
+PixmapCollectionChooser::pixmapName()
+{
+ return m_iconView->currentItem() ? m_iconView->currentItem()->text() : QString("");
+}
+
+QPixmap
+PixmapCollectionChooser::getPixmap(const QString &name)
+{
+ QPixmap pixmap = m_collection->getPixmap(name);
+ if((pixmap.width() <= 48) && (pixmap.height() <= 48))
+ return pixmap;
+
+ // We scale the pixmap down to 48x48 to fit in the iconView
+ KPixmapIO io;
+ QImage image = io.convertToImage(pixmap);
+ pixmap = io.convertToPixmap(image.scale(48, 48, QImage::ScaleMin));
+ return pixmap;
+}
+
+void
+PixmapCollectionChooser::slotUser1()
+{
+ PixmapCollectionEditor dialog(m_collection, parentWidget());
+ dialog.exec();
+
+ m_iconView->clear();
+ PixmapMap::ConstIterator it;
+ PixmapMap::ConstIterator endIt = m_collection->m_pixmaps.constEnd();
+ for(it = m_collection->m_pixmaps.constBegin(); it != endIt; ++it)
+ new PixmapIconViewItem(m_iconView, it.key(), getPixmap(it.key()));
+}
+
+#include "pixmapcollection.moc"
diff --git a/kexi/widget/pixmapcollection.h b/kexi/widget/pixmapcollection.h
new file mode 100644
index 00000000..a46a2043
--- /dev/null
+++ b/kexi/widget/pixmapcollection.h
@@ -0,0 +1,162 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 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 KFORMDESIGNERPIXMAPCOLLECTION_H
+#define KFORMDESIGNERPIXMAPCOLLECTION_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qpair.h>
+#include <qintdict.h>
+#include <qtoolbutton.h>
+
+#include <kicontheme.h>
+#include <kdialogbase.h>
+#include <kiconview.h>
+#include <kurl.h>
+
+class QPixmap;
+class KIconView;
+class KIconButton;
+class KLineEdit;
+class QDomNode;
+
+typedef QMap<QString, QPair<QString, int> > PixmapMap;
+
+//! A class that store pixmaps (by path or by name for KDE icons)
+class KEXIEXTWIDGETS_EXPORT PixmapCollection : public QObject
+{
+ Q_OBJECT
+
+ public:
+ PixmapCollection(const QString &collectionName, QObject *parent = 0, const char *name = 0);
+ ~PixmapCollection() {;}
+
+ QString addPixmapPath(const KURL &url);
+ QString addPixmapName(const QString &name, int size = KIcon::SizeMedium);
+ void removePixmap(const QString &name);
+
+ bool contains(const QString &name);
+ QPixmap getPixmap(const QString &name);
+
+ void save(QDomNode parentNode);
+ void load(QDomNode node);
+
+ QString collectionName() {return m_name; }
+
+ signals:
+ void itemRenamed(const QString &oldName, const QString &newName);
+ void itemRemoved(const QString &name);
+
+ protected:
+ QString m_name;
+ PixmapMap m_pixmaps;
+
+ friend class PixmapCollectionEditor;
+ friend class PixmapCollectionChooser;
+};
+
+//! A dialog to edit the contents of a PixmapCollection
+class KEXIEXTWIDGETS_EXPORT PixmapCollectionEditor : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ PixmapCollectionEditor(PixmapCollection *collection, QWidget *parent = 0);
+ ~PixmapCollectionEditor() {;}
+
+ protected:
+ QPixmap getPixmap(const QString &name);
+ void createIconViewItem(const QString &name);
+
+ protected slots:
+ void newItemByPath();
+ void newItemByName();
+ void removeItem();
+ void renameItem();
+ void renameCollectionItem(QIconViewItem *item, const QString &name);
+ void displayMenu(QIconViewItem *item, const QPoint &p);
+
+ private:
+ enum { BNewItemPath = 101, BNewItemName, BDelItem};
+ KIconView *m_iconView;
+ QIntDict<QToolButton> m_buttons;
+ PixmapCollection *m_collection;
+};
+
+//! A dialog to choose an icon in a PixmapCollection
+class KEXIEXTWIDGETS_EXPORT PixmapCollectionChooser : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ PixmapCollectionChooser(PixmapCollection *collection, const QString &selectedItem, QWidget *parent = 0);
+ ~PixmapCollectionChooser() {;}
+
+ QPixmap pixmap();
+ QString pixmapName();
+
+ protected:
+ QPixmap getPixmap(const QString &name);
+ protected slots:
+ virtual void slotUser1();
+
+ private:
+ PixmapCollection *m_collection;
+ KIconView *m_iconView;
+};
+
+//! A simple dialog to choose a KDE icon
+class KEXIEXTWIDGETS_EXPORT LoadIconDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ LoadIconDialog(QWidget *parent = 0);
+ ~LoadIconDialog() {;}
+
+ int iconSize();
+ QString iconName();
+
+ protected slots:
+ void changeIconSize(int);
+ void updateIconName(QString);
+ void setIcon(const QString &);
+
+ private:
+ KLineEdit *m_nameInput;
+ KIconButton *m_button;
+};
+
+//! A Special KIconViewItem that holds the name of its associated pixmap (to allow renaming)
+class KEXIEXTWIDGETS_EXPORT PixmapIconViewItem : public KIconViewItem
+{
+ public:
+ PixmapIconViewItem(KIconView *parent, const QString &text, const QPixmap &icon)
+ : KIconViewItem(parent, text, icon) { m_name = text; }
+ ~PixmapIconViewItem() {;}
+
+ void setName(const QString &name) { m_name = name; }
+ QString name() { return m_name;}
+
+ private:
+ QString m_name;
+};
+
+#endif
diff --git a/kexi/widget/relations/Makefile.am b/kexi/widget/relations/Makefile.am
new file mode 100644
index 00000000..f09e939a
--- /dev/null
+++ b/kexi/widget/relations/Makefile.am
@@ -0,0 +1,38 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+lib_LTLIBRARIES = libkexirelationsview.la
+
+libkexirelationsview_la_SOURCES = kexirelationview.cpp kexirelationviewconnection.cpp \
+ kexirelationviewtable.cpp kexirelationwidget.cpp
+
+libkexirelationsview_la_LDFLAGS = $(all_libraries) $(VER_INFO) -Wnounresolved
+libkexirelationsview_la_LIBADD = ../../core/libkexicore.la
+
+libkexirelationsview_la_METASOURCES = AUTO
+
+SUBDIRS = .
+
+# kde_appsdir Where your application's menu entry (.desktop) should go to.
+# kde_icondir Where your icon should go to - better use KDE_ICON.
+# kde_sounddir Where your sounds should go to.
+# kde_htmldir Where your docs should go to. (contains lang subdirs)
+# kde_datadir Where you install application data. (Use a subdir)
+# kde_locale Where translation files should go to. (contains lang subdirs)
+# kde_cgidir Where cgi-bin executables should go to.
+# kde_confdir Where config files should go to (system-wide ones with default values).
+# kde_mimedir Where mimetypes .desktop files should go to.
+# kde_servicesdir Where services .desktop files should go to.
+# kde_servicetypesdir Where servicetypes .desktop files should go to.
+# kde_toolbardir Where general toolbar icons should go to (deprecated, use KDE_ICON).
+# kde_wallpaperdir Where general wallpapers should go to.
+# kde_templatesdir Where templates for the "New" menu (Konqueror/KDesktop) should go to.
+# kde_bindir Where executables should go to. Use bin_PROGRAMS or bin_SCRIPTS.
+# kde_libdir Where shared libraries should go to. Use lib_LTLIBRARIES.
+# kde_moduledir Where modules (e.g. parts) should go to. Use kde_module_LTLIBRARIES.
+# kde_styledir Where Qt/KDE widget styles should go to (new in KDE 3).
+# kde_designerdir Where Qt Designer plugins should go to (new in KDE 3).
+
+# set the include path for X, qt and KDE
+INCLUDES= -I$(top_srcdir)/kexi $(LIB_KEXI_KMDI_INCLUDES) \
+ -I$(top_srcdir)/kexi/core $(all_includes)
+
diff --git a/kexi/widget/relations/kexirelationview.cpp b/kexi/widget/relations/kexirelationview.cpp
new file mode 100644
index 00000000..9d68a755
--- /dev/null
+++ b/kexi/widget/relations/kexirelationview.cpp
@@ -0,0 +1,639 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 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 <kdebug.h>
+
+#include <qstringlist.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qheader.h>
+#include <qevent.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qlineedit.h>
+#include <qpopupmenu.h>
+
+#include <klocale.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kglobalsettings.h>
+#include <kmessagebox.h>
+
+#include <kexidb/tableschema.h>
+#include <kexidb/indexschema.h>
+#include <kexidb/utils.h>
+
+#include "kexirelationview.h"
+#include "kexirelationviewtable.h"
+#include "kexirelationviewconnection.h"
+#include <kexi.h>
+
+KexiRelationView::KexiRelationView(QWidget *parent, const char *name)
+ : QScrollView(parent, name, WStaticContents)
+{
+// m_relation=relation;
+// m_relation->incUsageCount();
+ m_selectedConnection = 0;
+ m_readOnly=false;
+ m_focusedTableView = 0;
+ setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
+
+// connect(relation, SIGNAL(relationListUpdated(QObject *)), this, SLOT(slotListUpdate(QObject *)));
+
+ viewport()->setPaletteBackgroundColor(colorGroup().mid());
+ setFocusPolicy(WheelFocus);
+ setResizePolicy(Manual);
+/*MOVED TO KexiRelationDialog
+ //actions
+ m_tableQueryPopup = new KPopupMenu(this, "m_popup");
+ m_tableQueryPopup->insertTitle(i18n("Table"));
+ m_connectionPopup = new KPopupMenu(this, "m_connectionPopup");
+ m_connectionPopup->insertTitle(i18n("Relation"));
+ m_areaPopup = new KPopupMenu(this, "m_areaPopup");
+
+ plugSharedAction("edit_delete", i18n("Hide Table"), m_tableQueryPopup);
+ plugSharedAction("edit_delete",m_connectionPopup);
+ plugSharedAction("edit_delete",this, SLOT(removeSelectedObject()));
+*/
+#if 0
+ m_removeSelectedTableQueryAction = new KAction(i18n("&Hide Selected Table/Query"), "editdelete", "",
+ this, SLOT(removeSelectedTableQuery()), parent->actionCollection(), "relationsview_removeSelectedTableQuery");
+ m_removeSelectedConnectionAction = new KAction(i18n("&Remove Selected Relationship"), "button_cancel", "",
+ this, SLOT(removeSelectedConnection()), parent->actionCollection(), "relationsview_removeSelectedConnection");
+ m_openSelectedTableQueryAction = new KAction(i18n("&Open Selected Table/Query"), "", "",
+ this, SLOT(openSelectedTableQuery()), 0/*parent->actionCollection()*/, "relationsview_openSelectedTableQuery");
+#endif
+
+// invalidateActions();
+
+#if 0
+
+
+ m_popup = new KPopupMenu(this, "m_popup");
+ m_openSelectedTableQueryAction->plug( m_popup );
+ m_removeSelectedTableQueryAction->plug( m_popup );
+ m_removeSelectedConnectionAction->plug( m_popup );
+
+ invalidateActions();
+#endif
+
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, true);
+}
+
+KexiRelationView::~KexiRelationView()
+{
+}
+
+/*KexiRelationViewTableContainer*
+KexiRelationView::containerForTable(KexiDB::TableSchema* tableSchema)
+{
+ if (!tableSchema)
+ return 0;
+ for (TablesDictIterator it(m_tables); it.current(); ++it) {
+ if (it.current()->schema()->table()==tableSchema)
+ return it.current();
+ }
+ return 0;
+}*/
+
+KexiRelationViewTableContainer *
+KexiRelationView::tableContainer(KexiDB::TableSchema *t) const
+{
+ return t ? m_tables.find(t->name()) : 0;
+}
+
+KexiRelationViewTableContainer*
+KexiRelationView::addTableContainer(KexiDB::TableSchema *t, const QRect &rect)
+{
+ if(!t)
+ return 0;
+
+ kdDebug() << "KexiRelationView::addTable(): " << t->name() << ", " << viewport() << endl;
+
+ KexiRelationViewTableContainer* c = tableContainer(t);
+ if (c) {
+ kdWarning() << "KexiRelationView::addTable(): table already added" << endl;
+ return c;
+ }
+
+ c = new KexiRelationViewTableContainer(this,
+/*! @todo what about query? */
+ new KexiDB::TableOrQuerySchema(t)
+ );
+ connect(c, SIGNAL(endDrag()), this, SLOT(slotTableViewEndDrag()));
+ connect(c, SIGNAL(gotFocus()), this, SLOT(slotTableViewGotFocus()));
+// connect(c, SIGNAL(headerContextMenuRequest(const QPoint&)),
+// this, SLOT(tableHeaderContextMenuRequest(const QPoint&)));
+ connect(c, SIGNAL(contextMenuRequest(const QPoint&)),
+ this, SIGNAL(tableContextMenuRequest(const QPoint&)));
+
+ addChild(c, 100,100);
+ if (rect.isValid()) {//predefined size
+ QSize finalSize = c->size().expandedTo( c->sizeHint() );
+ QRect r = rect;
+ r.setSize( finalSize + QSize(0,10) );
+ moveChild( c, rect.left(), rect.top() );
+ //we're doing this instead of setGeometry(rect)
+ //because the geomenty might be saved on other system with bigger fonts :)
+ c->resize(c->sizeHint());
+// c->setGeometry(r);
+//TODO
+
+// moveChild( c, rect.left(), rect.top() ); // setGeometry(rect);
+// c->resize( finalSize );
+// c->updateGeometry();
+ }
+ c->show();
+ updateGeometry();
+ if (!rect.isValid()) {
+ c->updateGeometry();
+ c->resize(c->sizeHint());
+ }
+ int x, y;
+
+ if(m_tables.count() > 0)
+ {
+ int place = -10;
+ QDictIterator<KexiRelationViewTableContainer> it(m_tables);
+ for(; it.current(); ++it)
+ {
+ int right = (*it)->x() + (*it)->width();
+ if(right > place)
+ place = right;
+ }
+
+ x = place + 30;
+ }
+ else
+ {
+ x = 5;
+ }
+
+ y = 5;
+ QPoint p = viewportToContents(QPoint(x, y));
+ recalculateSize(p.x() + c->width(), p.y() + c->height());
+ if (!rect.isValid()) {
+ moveChild(c, x, y);
+ }
+
+ m_tables.insert(t->name(), c);
+
+ connect(c, SIGNAL(moved(KexiRelationViewTableContainer *)), this,
+ SLOT(containerMoved(KexiRelationViewTableContainer *)));
+
+ if (hasFocus()) //ok?
+ c->setFocus();
+
+ return c;
+}
+
+void
+KexiRelationView::addConnection(const SourceConnection& _conn)
+{
+ SourceConnection conn = _conn;
+ kdDebug() << "KexiRelationView::addConnection()" << endl;
+
+ KexiRelationViewTableContainer *master = m_tables[conn.masterTable];
+ KexiRelationViewTableContainer *details = m_tables[conn.detailsTable];
+ if (!master || !details)
+ return;
+
+/*! @todo what about query? */
+ KexiDB::TableSchema *masterTable = master->schema()->table();
+/*! @todo what about query? */
+ KexiDB::TableSchema *detailsTable = details->schema()->table();
+ if (!masterTable || !detailsTable)
+ return;
+
+ // ok, but we need to know where is the 'master' and where is the 'details' side:
+ KexiDB::Field *masterFld = masterTable->field(conn.masterField);
+ KexiDB::Field *detailsFld = detailsTable->field(conn.detailsField);
+ if (!masterFld || !detailsFld)
+ return;
+
+ if (!masterFld->isUniqueKey()) {
+ if (detailsFld->isUniqueKey()) {
+ //SWAP:
+ KexiDB::Field *tmpFld = masterFld;
+ masterFld = detailsFld;
+ detailsFld = tmpFld;
+ KexiDB::TableSchema *tmpTable = masterTable;
+ masterTable = detailsTable;
+ detailsTable = tmpTable;
+ KexiRelationViewTableContainer *tmp = master;
+ master = details;
+ details = tmp;
+ QString tmp_masterTable = conn.masterTable;
+ conn.masterTable = conn.detailsTable;
+ conn.detailsTable = tmp_masterTable;
+ QString tmp_masterField = conn.masterField;
+ conn.masterField = conn.detailsField;
+ conn.detailsField = tmp_masterField;
+ }
+ }
+
+// kdDebug() << "KexiRelationView::addConnection(): finalSRC = " << m_tables[conn.srcTable] << endl;
+
+ KexiRelationViewConnection *connView = new KexiRelationViewConnection(master, details, conn, this);
+ m_connectionViews.append(connView);
+ updateContents(connView->connectionRect());
+
+/*js: will be moved up to relation/query part as this is only visual class
+ KexiDB::TableSchema *mtable = m_conn->tableSchema(conn.srcTable);
+ KexiDB::TableSchema *ftable = m_conn->tableSchema(conn.rcvTable);
+ KexiDB::IndexSchema *forign = new KexiDB::IndexSchema(ftable);
+
+ forign->addField(mtable->field(conn.srcField));
+ new KexiDB::Reference(forign, mtable->primaryKey());
+*/
+#if 0
+ if(!interactive)
+ {
+ kdDebug() << "KexiRelationView::addConnection: adding self" << endl;
+ RelationList l = m_relation->projectRelations();
+ l.append(conn);
+ m_relation->updateRelationList(this, l);
+ }
+#endif
+}
+
+void
+KexiRelationView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ KexiRelationViewConnection *cview;
+// p->translate(0, (double)contentsY());
+
+ QRect clipping(cx, cy, cw, ch);
+ for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
+ {
+ if(clipping.intersects(cview->oldRect() | cview->connectionRect()))
+ cview->drawConnection(p);
+ }
+}
+
+void
+KexiRelationView::slotTableScrolling(const QString& table)
+{
+ KexiRelationViewTableContainer *c = m_tables[table];
+
+ if(c)
+ containerMoved(c);
+}
+
+void
+KexiRelationView::containerMoved(KexiRelationViewTableContainer *c)
+{
+ KexiRelationViewConnection *cview;
+ QRect r;
+ for (ConnectionListIterator it(m_connectionViews); ((cview=it.current())); ++it) {
+//! @todo optimize
+ if(cview->masterTable() == c || cview->detailsTable() == c
+ || cview->connectionRect().intersects(r))
+ {
+ r |= cview->oldRect();
+ kdDebug() << r << endl;
+ r |= cview->connectionRect();
+ kdDebug() << r << endl;
+ }
+// updateContents(cview->oldRect());
+// updateContents(cview->connectionRect());
+// }
+ }
+//! @todo optimize!
+//didn't work well: updateContents(r);
+ updateContents();
+
+// QRect w(c->x() - 5, c->y() - 5, c->width() + 5, c->height() + 5);
+// updateContents(w);
+
+ QPoint p = viewportToContents(QPoint(c->x(), c->y()));
+ recalculateSize(p.x() + c->width(), p.y() + c->height());
+
+ emit tablePositionChanged(c);
+}
+
+void
+KexiRelationView::setReadOnly(bool b)
+{
+ m_readOnly=b;
+//TODO
+// invalidateActions();
+/* TableList::Iterator it, end( m_tables.end() );
+ for ( it=m_tables.begin(); it != end; ++it)
+ {
+// (*it)->setReadOnly(b);
+#ifndef Q_WS_WIN
+ #warning readonly needed
+#endif
+ }*/
+}
+
+void
+KexiRelationView::slotListUpdate(QObject *)
+{
+#if 0
+ if(s != this)
+ {
+ m_connectionViews.clear();
+ RelationList rl = m_relation->projectRelations();
+ if(!rl.isEmpty())
+ {
+ RelationList::ConstIterator it, end( rl.constEnd() );
+ for( it = rl.begin(); it != end; ++it)
+ {
+ addConnection((*it), true);
+ }
+ }
+ }
+
+ updateContents();
+#endif
+}
+
+void
+KexiRelationView::contentsMousePressEvent(QMouseEvent *ev)
+{
+ KexiRelationViewConnection *cview;
+ for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
+ {
+ if(!cview->matchesPoint(ev->pos(), 3))
+ continue;
+ clearSelection();
+ setFocus();
+ cview->setSelected(true);
+ updateContents(cview->connectionRect());
+ m_selectedConnection = cview;
+ emit connectionViewGotFocus();
+// invalidateActions();
+
+ if(ev->button() == RightButton) {//show popup
+ kdDebug() << "KexiRelationView::contentsMousePressEvent(): context" << endl;
+// QPopupMenu m;
+// m_removeSelectedTableQueryAction->plug( &m );
+// m_removeSelectedConnectionAction->plug( &m );
+ emit connectionContextMenuRequest( ev->globalPos() );
+// executePopup( ev->globalPos() );
+ }
+ return;
+ }
+ //connection not found
+ clearSelection();
+// invalidateActions();
+ if(ev->button() == RightButton) {//show popup on view background area
+// QPopupMenu m;
+// m_removeSelectedConnectionAction->plug( &m );
+ emit emptyAreaContextMenuRequest( ev->globalPos() );
+// executePopup(ev->globalPos());
+ }
+ else {
+ emit emptyAreaGotFocus();
+ }
+ setFocus();
+// QScrollView::contentsMousePressEvent(ev);
+}
+
+void KexiRelationView::clearSelection()
+{
+ if (m_focusedTableView) {
+ m_focusedTableView->unsetFocus();
+ m_focusedTableView = 0;
+// setFocus();
+// invalidateActions();
+ }
+ if (m_selectedConnection) {
+ m_selectedConnection->setSelected(false);
+ updateContents(m_selectedConnection->connectionRect());
+ m_selectedConnection = 0;
+// invalidateActions();
+ }
+}
+
+void
+KexiRelationView::keyPressEvent(QKeyEvent *ev)
+{
+ kdDebug() << "KexiRelationView::keyPressEvent()" << endl;
+
+ if (ev->key()==KGlobalSettings::contextMenuKey()) {
+ if (m_selectedConnection) {
+ emit connectionContextMenuRequest(
+ mapToGlobal(m_selectedConnection->connectionRect().center()) );
+ }
+// m_popup->exec( mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() ) );
+// executePopup();
+ }
+ else {
+ if(ev->key() == Key_Delete)
+ removeSelectedObject();
+ }
+}
+
+void
+KexiRelationView::recalculateSize(int width, int height)
+{
+ kdDebug() << "recalculateSize(" << width << ", " << height << ")" << endl;
+ int newW = contentsWidth(), newH = contentsHeight();
+ kdDebug() << "contentsSize(" << newW << ", " << newH << ")" << endl;
+
+ if(newW < width)
+ newW = width;
+
+ if(newH < height)
+ newH = height;
+
+ resizeContents(newW, newH);
+}
+
+/*! Resizes contents to size exactly enough to fit tableViews.
+ Executed on every tableView's drop event.
+*/
+void
+KexiRelationView::stretchExpandSize()
+{
+ int max_x=-1, max_y=-1;
+ QDictIterator<KexiRelationViewTableContainer> it(m_tables);
+ for (;it.current(); ++it) {
+ if (it.current()->right()>max_x)
+ max_x = it.current()->right();
+ if (it.current()->bottom()>max_y)
+ max_y = it.current()->bottom();
+ }
+ QPoint p = viewportToContents(QPoint(max_x, max_y) + QPoint(3,3)); //3 pixels margin
+ resizeContents(p.x(), p.y());
+}
+
+void KexiRelationView::slotTableViewEndDrag()
+{
+ kdDebug() << "END DRAG!" <<endl;
+ stretchExpandSize();
+
+}
+
+void
+KexiRelationView::removeSelectedObject()
+{
+ if (m_selectedConnection) {
+ removeConnection(m_selectedConnection);
+
+#if 0
+ RelationList l = m_relation->projectRelations();
+ RelationList nl;
+ for(RelationList::Iterator it = l.begin(); it != l.end(); ++it)
+ {
+ if((*it).srcTable == m_selectedConnection->connection().srcTable
+ && (*it).rcvTable == m_selectedConnection->connection().rcvTable
+ && (*it).srcField == m_selectedConnection->connection().srcField
+ && (*it).rcvField == m_selectedConnection->connection().rcvField)
+ {
+ kdDebug() << "KexiRelationView::removeSelectedConnection(): matching found!" << endl;
+// l.remove(it);
+ }
+ else
+ {
+ nl.append(*it);
+ }
+ }
+
+ kdDebug() << "KexiRelationView::removeSelectedConnection(): d2" << endl;
+ m_relation->updateRelationList(this, nl);
+ kdDebug() << "KexiRelationView::removeSelectedConnection(): d3" << endl;
+#endif
+ delete m_selectedConnection;
+ m_selectedConnection = 0;
+// invalidateActions();
+ }
+ else if (m_focusedTableView) {
+ KexiRelationViewTableContainer *tmp = m_focusedTableView;
+ m_focusedTableView = 0;
+ hideTable(tmp);
+ }
+}
+
+void
+KexiRelationView::hideTable(KexiRelationViewTableContainer* tableView)
+{
+/*! @todo what about query? */
+ KexiDB::TableSchema *ts = tableView->schema()->table();
+ //for all connections: find and remove all connected with this table
+ QPtrListIterator<KexiRelationViewConnection> it(m_connectionViews);
+ for (;it.current();) {
+ if (it.current()->masterTable() == tableView
+ || it.current()->detailsTable() == tableView)
+ {
+ //remove this
+ removeConnection(it.current());
+ }
+ else {
+ ++it;
+ }
+ }
+ m_tables.take(tableView->schema()->name());
+ delete tableView;
+ emit tableHidden( *ts );
+}
+
+void
+KexiRelationView::hideAllTablesExcept( KexiDB::TableSchema::List* tables )
+{
+//! @todo what about queries?
+ for (TablesDictIterator it(m_tables); it.current();) {
+ KexiDB::TableSchema *table = it.current()->schema()->table();
+ if (!table || tables->findRef( table )!=-1) {
+ ++it;
+ continue;
+ }
+ hideTable(it.current());
+ }
+}
+
+void
+KexiRelationView::removeConnection(KexiRelationViewConnection *conn)
+{
+ emit aboutConnectionRemove(conn);
+ m_connectionViews.remove(conn);
+ updateContents(conn->connectionRect());
+ kdDebug() << "KexiRelationView::removeConnection()" << endl;
+}
+
+void KexiRelationView::slotTableViewGotFocus()
+{
+ if (m_focusedTableView == sender())
+ return;
+ kdDebug() << "GOT FOCUS!" <<endl;
+ clearSelection();
+// if (m_focusedTableView)
+// m_focusedTableView->unsetFocus();
+ m_focusedTableView = (KexiRelationViewTableContainer*)sender();
+// invalidateActions();
+ emit tableViewGotFocus();
+}
+
+QSize KexiRelationView::sizeHint() const
+{
+ return QSize(QScrollView::sizeHint());//.width(), 600);
+}
+
+void KexiRelationView::clear()
+{
+ removeAllConnections();
+ m_tables.setAutoDelete(true);
+ m_tables.clear();
+ m_tables.setAutoDelete(false);
+ updateContents();
+}
+
+void KexiRelationView::removeAllConnections()
+{
+ clearSelection(); //sanity
+ m_connectionViews.setAutoDelete(true);
+ m_connectionViews.clear();
+ m_connectionViews.setAutoDelete(false);
+ updateContents();
+}
+
+/*
+
+void KexiRelationView::tableHeaderContextMenuRequest(const QPoint& pos)
+{
+ if (m_focusedTableView != sender())
+ return;
+ kdDebug() << "HEADER CTXT MENU!" <<endl;
+ invalidateActions();
+ m_tableQueryPopup->exec(pos);
+}
+
+//! Invalidates all actions availability
+void KexiRelationView::invalidateActions()
+{
+ setAvailable("edit_delete", m_selectedConnection || m_focusedTableView);
+}
+
+void KexiRelationView::executePopup( QPoint pos )
+{
+ if (pos==QPoint(-1,-1)) {
+ pos = mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() );
+ }
+ if (m_focusedTableView)
+ m_tableQueryPopup->exec(pos);
+ else if (m_selectedConnection)
+ m_connectionPopup->exec(pos);
+}
+*/
+
+#include "kexirelationview.moc"
diff --git a/kexi/widget/relations/kexirelationview.h b/kexi/widget/relations/kexirelationview.h
new file mode 100644
index 00000000..2de6620d
--- /dev/null
+++ b/kexi/widget/relations/kexirelationview.h
@@ -0,0 +1,167 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-2004, 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 KEXIRELATIONVIEW_H
+#define KEXIRELATIONVIEW_H
+
+#include <qguardedptr.h>
+#include <qscrollview.h>
+#include <qptrlist.h>
+#include <qdict.h>
+
+#include <kexidb/tableschema.h>
+
+#include "kexirelationviewconnection.h"
+
+class QFrame;
+
+class KexiRelationViewTable;
+class KexiRelationViewTableContainer;
+class KAction;
+class KPopupMenu;
+
+namespace KexiDB
+{
+ class Reference;
+ class Connection;
+}
+
+typedef QDict<KexiRelationViewTableContainer> TablesDict;
+typedef QDictIterator<KexiRelationViewTableContainer> TablesDictIterator;
+typedef QPtrList<KexiRelationViewConnection> ConnectionList;
+typedef QPtrListIterator<KexiRelationViewConnection> ConnectionListIterator;
+
+struct SourceConnection
+{
+ QString masterTable;
+ QString detailsTable;
+ QString masterField;
+ QString detailsField;
+};
+
+/*! @short provides a view for displaying relations between database tables.
+
+ It is currently used for two purposes:
+ - displaying global database relations
+ - displaying relations defined for a database query
+
+ The class is for displaying only - retrieving data and updating data on the backend side is implemented
+ in KexiRelationWidget, and more specifically in: Kexi Relation Part and Kexi Query Part.
+*/
+class KEXIRELATIONSVIEW_EXPORT KexiRelationView : public QScrollView
+{
+ Q_OBJECT
+
+ public:
+ KexiRelationView(QWidget *parent, const char *name=0);
+ virtual ~KexiRelationView();
+
+ //! \return a dictionary of added tables
+ TablesDict* tables() { return &m_tables; }
+
+ /*! Adds a table \a t to the area. This changes only visual representation.
+ If \a rect is valid, table widget geometry will be initialized.
+ \return added table container or 0 on failure.
+ */
+ KexiRelationViewTableContainer* addTableContainer(KexiDB::TableSchema *t,
+ const QRect &rect = QRect());
+
+ /*! \return table container for table \a t. */
+ KexiRelationViewTableContainer * tableContainer(KexiDB::TableSchema *t) const;
+
+ //! Adds a connection \a con to the area. This changes only visual representation.
+ void addConnection(const SourceConnection& _conn /*, bool interactive=true*/);
+
+ void setReadOnly(bool);
+
+ inline KexiRelationViewConnection* selectedConnection() const { return m_selectedConnection; }
+
+ inline KexiRelationViewTableContainer* focusedTableView() const { return m_focusedTableView; }
+
+ virtual QSize sizeHint() const;
+
+ const ConnectionList* connections() const { return &m_connectionViews; }
+
+// KexiRelationViewTableContainer* containerForTable(KexiDB::TableSchema* tableSchema);
+
+ signals:
+ void tableContextMenuRequest( const QPoint& pos );
+ void connectionContextMenuRequest( const QPoint& pos );
+ void emptyAreaContextMenuRequest( const QPoint& pos );
+ void tableViewGotFocus();
+ void connectionViewGotFocus();
+ void emptyAreaGotFocus();
+ void tableHidden(KexiDB::TableSchema& t);
+ void tablePositionChanged(KexiRelationViewTableContainer*);
+ void aboutConnectionRemove(KexiRelationViewConnection*);
+
+ public slots:
+ //! Clears current selection - table/query or connection
+ void clearSelection();
+
+ /*! Removes all tables and connections from the view.
+ Does not emit signals like tableHidden(). */
+ void clear();
+
+ /*! Removes all coonections from the view. */
+ void removeAllConnections();
+
+ /*! Hides all tables except \a tables. */
+ void hideAllTablesExcept( KexiDB::TableSchema::List* tables );
+
+ void slotTableScrolling(const QString&);
+
+ //! removes selected table or connection
+ void removeSelectedObject();
+
+
+ protected slots:
+ void containerMoved(KexiRelationViewTableContainer *c);
+ void slotListUpdate(QObject *s);
+ void slotTableViewEndDrag();
+ void slotTableViewGotFocus();
+
+ protected:
+// /*! executes popup menu at \a pos, or,
+// if \a pos not specified: at center of selected table view (if any selected),
+// or at center point of the relations view. */
+// void executePopup( QPoint pos = QPoint(-1,-1) );
+
+ void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+ void contentsMousePressEvent(QMouseEvent *ev);
+ virtual void keyPressEvent(QKeyEvent *ev);
+
+ void recalculateSize(int width, int height);
+ void stretchExpandSize();
+// void invalidateActions();
+// void clearTableSelection();
+// void clearConnSelection();
+
+ void hideTable(KexiRelationViewTableContainer* tableView);
+ void removeConnection(KexiRelationViewConnection *conn);
+
+ TablesDict m_tables;
+ bool m_readOnly;
+ ConnectionList m_connectionViews;
+ KexiRelationViewConnection* m_selectedConnection;
+ QGuardedPtr<KexiRelationViewTableContainer> m_focusedTableView;
+};
+
+#endif
diff --git a/kexi/widget/relations/kexirelationviewconnection.cpp b/kexi/widget/relations/kexirelationviewconnection.cpp
new file mode 100644
index 00000000..2c27de87
--- /dev/null
+++ b/kexi/widget/relations/kexirelationviewconnection.cpp
@@ -0,0 +1,298 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004-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 <qpainter.h>
+#include <qpixmap.h>
+#include <qcolor.h>
+#include <qapplication.h>
+#include <qpointarray.h>
+
+#include <kdebug.h>
+
+#include <math.h>
+
+#include "kexirelationview.h"
+#include "kexirelationviewtable.h"
+#include "kexirelationviewconnection.h"
+#include <kexidb/tableschema.h>
+#include <kexidb/utils.h>
+#include <core/kexi.h>
+
+//#include "r1.xpm"
+//#include "rn.xpm"
+
+KexiRelationViewConnection::KexiRelationViewConnection(
+ KexiRelationViewTableContainer *masterTbl, KexiRelationViewTableContainer *detailsTbl,
+ SourceConnection &c, KexiRelationView *parent)
+{
+ m_parent = parent;
+// kdDebug() << "KexiRelationViewConnection::KexiRelationViewConnection()" << endl;
+
+ m_masterTable = masterTbl;
+ if(!masterTbl || !detailsTbl)
+ {
+ kdDebug() << "KexiRelationViewConnection::KexiRelationViewConnection(): expect sig11" << endl;
+ kdDebug() << "KexiRelationViewConnection::KexiRelationViewConnection()" << masterTbl << endl;
+ kdDebug() << "KexiRelationViewConnection::KexiRelationViewConnection()" << detailsTbl << endl;
+ }
+
+ m_detailsTable = detailsTbl;
+ m_masterField = c.masterField;
+ m_detailsField = c.detailsField;
+
+ m_selected = false;
+}
+
+KexiRelationViewConnection::~KexiRelationViewConnection()
+{
+}
+
+void
+KexiRelationViewConnection::drawConnection(QPainter *p)
+{
+ p->setPen(m_parent->palette().active().foreground());
+ int sx = m_masterTable->x() + m_masterTable->width() + m_parent->contentsX();
+ int sy = m_masterTable->globalY(m_masterField);
+ int rx = m_detailsTable->x() + m_parent->contentsX();
+ int ry = m_detailsTable->globalY(m_detailsField);
+
+ QFont f( Kexi::smallFont( m_parent ) );
+ QFontMetrics fm(f);
+ int side1x=0, side1y=sy - fm.height(),
+ sideNx=0, sideNy=ry - fm.height();
+//! @todo details char can be also just a '1' for some cases
+ QChar sideNChar(0x221E); //infinity char
+ uint sideNCharWidth = 2+2+ fm.width( sideNChar );
+ QChar side1Char('1');
+ uint side1CharWidth = 2+2+ fm.width( side1Char );
+ p->setBrush(p->pen().color());
+
+ if(m_masterTable->x() < m_detailsTable->x())
+ {
+ //det. side
+ p->drawLine(rx - sideNCharWidth, ry, rx, ry);
+ QPointArray pa(3);
+ pa.setPoint(0, rx - 4, ry - 3);
+ pa.setPoint(1, rx - 4, ry + 3);
+ pa.setPoint(2, rx - 1, ry);
+ p->drawPolygon(pa, true);
+
+ //master side
+ p->drawLine(sx, sy - 1, sx + side1CharWidth -1, sy - 1);
+ p->drawLine(sx, sy, sx + side1CharWidth -1, sy);
+ p->drawLine(sx, sy + 1, sx + side1CharWidth -1, sy + 1);
+
+ side1x = sx;
+// side1y = sy - 7;
+
+ sideNx = rx - sideNCharWidth - 1;
+// sideNy = ry - 6;
+
+ QPen pen(p->pen());
+ if(m_selected)
+ {
+ QPen pen(p->pen());
+ pen.setWidth(2);
+ p->setPen(pen);
+ }
+
+ p->drawLine(sx + side1CharWidth, sy, rx - sideNCharWidth, ry);
+
+ if(m_selected)
+ {
+ QPen pen(p->pen());
+ pen.setWidth(1);
+ p->setPen(pen);
+ }
+
+ }
+ else
+ {
+ int lx = rx + m_detailsTable->width();
+ int rx = sx - m_masterTable->width();
+
+ //det. side
+ p->drawLine(lx, ry, lx + sideNCharWidth, ry);
+ QPointArray pa(3);
+ pa.setPoint(0, lx + 3, ry - 3);
+ pa.setPoint(1, lx + 3, ry + 3);
+ pa.setPoint(2, lx, ry);
+ p->drawPolygon(pa, true);
+
+// p->drawLine(lx, ry, lx + 8, ry);
+// p->drawPoint(lx + 1, ry - 1);
+// p->drawPoint(lx + 1, ry + 1);
+// p->drawLine(lx + 2, ry - 2, lx + 2, ry + 2);
+
+ //master side
+ p->drawLine(rx - side1CharWidth +1, sy - 1, rx, sy - 1);
+ p->drawLine(rx - side1CharWidth +1, sy + 1, rx, sy + 1);
+ p->drawLine(rx - side1CharWidth +1, sy, rx, sy);
+
+ side1x = rx - side1CharWidth;
+// side1y = sy - 7;
+
+ sideNx = lx + 1;
+// sideNy = ry - 6;
+
+ if(m_selected)
+ {
+ QPen pen(p->pen());
+ pen.setWidth(2);
+ p->setPen(pen);
+ }
+
+ p->drawLine(lx + sideNCharWidth, ry, rx - side1CharWidth, sy);
+
+ if(m_selected)
+ {
+ QPen pen(p->pen());
+ pen.setWidth(1);
+ p->setPen(pen);
+ }
+ }
+
+ p->drawText(side1x, side1y, side1CharWidth, fm.height(), Qt::AlignCenter, side1Char);
+ p->drawText(sideNx, sideNy, sideNCharWidth, fm.height(), Qt::AlignCenter, sideNChar);
+ //p->drawRect(QRect(connectionRect().topLeft(), QSize(50,50)));
+// p->drawPixmap(side1, QPixmap(r1_xpm));
+// p->drawPixmap(sideN, QPixmap(rn_xpm));
+}
+
+const QRect
+KexiRelationViewConnection::connectionRect()
+{
+ int sx = m_masterTable->x() + m_parent->contentsX();
+ int rx = m_detailsTable->x() + m_parent->contentsX();
+ int ry = m_detailsTable->globalY(m_detailsField);
+ int sy = m_masterTable->globalY(m_masterField);
+
+ int width, leftX, rightX;
+
+ if(sx < rx)
+ {
+ leftX = sx;
+ rightX = rx;
+ width = m_masterTable->width();
+ }
+ else
+ {
+ leftX = rx;
+ rightX = sx;
+ width = m_detailsTable->width();
+ }
+
+
+ int dx = QABS((leftX + width) - rightX);
+ int dy = QABS(sy - ry) + 2;
+
+ int top = QMIN(sy, ry);
+ int left = leftX + width;
+
+
+// return QRect(sx - 1, sy - 1, (rx + m_detailsTable->width()) - sx + 1, ry - sy + 1);
+ QRect rect(left - 150, top - 150, dx + 150, dy + 150);
+// kdDebug() << "KexiRelationViewConnection::connectionRect():" << m_oldRect << "," << rect << endl;
+
+ m_oldRect = rect;
+
+ return rect;
+}
+
+bool
+KexiRelationViewConnection::matchesPoint(const QPoint &p, int tolerance)
+{
+ QRect we = connectionRect();
+
+ if(!we.contains(p))
+ return false;
+
+ /** get our coordinats
+ * you know what i mean the x1, y1 is the top point
+ * and the x2, y2 is the bottom point
+ * (quite tirvial :) although that was the entrace to the magic
+ * gate...
+ */
+
+ int sx = m_masterTable->x() + m_masterTable->width();
+ int sy = m_masterTable->globalY(m_masterField);
+ int rx = m_detailsTable->x();
+ int ry = m_detailsTable->globalY(m_detailsField);
+
+ int x1 = sx + 8;
+ int y1 = sy;
+ int x2 = rx - 8;
+ int y2 = ry;
+
+ if(sx > rx)
+ {
+ x1 = m_detailsTable->x() + m_detailsTable->width();
+ x2 = m_masterTable->x();
+ y2 = sy;
+ y1 = ry;
+ }
+
+ /*
+ here we call pythagoras (the greek math geek :p)
+ see: http://w1.480.telia.com/%7Eu48019406/geekporn.gif if you don't know
+ how these people have got sex :)
+ */
+ float mx = x2-x1;
+ float my = y2-y1;
+ float mag = sqrt(mx * mx + my * my);
+ float u = (((p.x() - x1)*(x2 - x1))+((p.y() - y1)*(y2 - y1)))/(mag * mag);
+ kdDebug() << "KexiRelationViewConnection::matchesPoint(): u: " << u << endl;
+
+ float iX = x1 + u * (x2 - x1);
+ float iY = y1 + u * (y2 - y1);
+ kdDebug() << "KexiRelationViewConnection::matchesPoint(): px: " << p.x() << endl;
+ kdDebug() << "KexiRelationViewConnection::matchesPoint(): py: " << p.y() << endl;
+ kdDebug() << "KexiRelationViewConnection::matchesPoint(): ix: " << iX << endl;
+ kdDebug() << "KexiRelationViewConnection::matchesPoint(): iy: " << iY << endl;
+
+ float dX = iX - p.x();
+ float dY = iY - p.y();
+
+ kdDebug() << "KexiRelationViewConnection::matchesPoint(): dx: " << dX << endl;
+ kdDebug() << "KexiRelationViewConnection::matchesPoint(): dy: " << dY << endl;
+
+ float distance = sqrt(dX * dX + dY * dY);
+ kdDebug() << "KexiRelationViewConnection::matchesPoint(): distance: " << distance << endl;
+
+ if(distance <= tolerance)
+ return true;
+
+ return false;
+}
+
+QString
+KexiRelationViewConnection::toString() const
+{
+ QString str;
+/*! @todo what about query? */
+ if (m_masterTable && m_masterTable->schema()->table()) {
+ str += (QString(m_masterTable->schema()->name()) + "." + m_masterField);
+ }
+ if (m_detailsTable && m_detailsTable->schema()->table()) {
+ str += " - ";
+ str += (QString(m_detailsTable->schema()->name()) + "." + m_detailsField);
+ }
+ return str;
+}
diff --git a/kexi/widget/relations/kexirelationviewconnection.h b/kexi/widget/relations/kexirelationviewconnection.h
new file mode 100644
index 00000000..699fdf4f
--- /dev/null
+++ b/kexi/widget/relations/kexirelationviewconnection.h
@@ -0,0 +1,75 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 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 KEXIRELATIONVIEWCONNECTION_H
+#define KEXIRELATIONVIEWCONNECTION_H
+
+#include <qstring.h>
+#include <qguardedptr.h>
+
+class QPainter;
+class KexiRelationViewTableContainer;
+class KexiRelationView;
+
+class KEXIRELATIONSVIEW_EXPORT KexiRelationViewConnection
+{
+ public:
+
+ KexiRelationViewConnection(KexiRelationViewTableContainer *masterTbl,
+ KexiRelationViewTableContainer *detailsTbl, struct SourceConnection &s, KexiRelationView *parent);
+ ~KexiRelationViewConnection();
+
+
+ /*
+ C++PROGRAMMIERER bestehen darauf, da�der Elefant eine Klasse sei,
+ und somit schlie�ich seine Fang-Methoden selbst mitzubringen habe.
+
+ http://www.c-plusplus.de ;)
+ */
+ void drawConnection(QPainter *p);
+
+ bool selected() { return m_selected; }
+ void setSelected(bool s) { m_selected = s; }
+
+ const QRect connectionRect();
+ const QRect oldRect() const { return m_oldRect; }
+
+ KexiRelationViewTableContainer *masterTable() { return m_masterTable; }
+ KexiRelationViewTableContainer *detailsTable() { return m_detailsTable; }
+ QString masterField() const { return m_masterField; }
+ QString detailsField() const { return m_detailsField; }
+
+
+ bool matchesPoint(const QPoint &p, int tolerance=3);
+// SourceConnection connection() { return m_conn; }
+
+ QString toString() const;
+
+ private:
+ QGuardedPtr<KexiRelationViewTableContainer> m_masterTable;
+ QGuardedPtr<KexiRelationViewTableContainer> m_detailsTable;
+ QString m_masterField;
+ QString m_detailsField;
+ QRect m_oldRect;
+ bool m_selected;
+ QGuardedPtr<KexiRelationView> m_parent;
+};
+
+#endif
diff --git a/kexi/widget/relations/kexirelationviewtable.cpp b/kexi/widget/relations/kexirelationviewtable.cpp
new file mode 100644
index 00000000..97beaa87
--- /dev/null
+++ b/kexi/widget/relations/kexirelationviewtable.cpp
@@ -0,0 +1,429 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-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 <stdlib.h>
+
+#include <qheader.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qstyle.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kdeversion.h>
+#include <kconfig.h>
+#include <kglobalsettings.h>
+
+#include <kexidb/tableschema.h>
+#include <kexidb/utils.h>
+#include <kexidragobjects.h>
+#include "kexirelationviewtable.h"
+#include "kexirelationview.h"
+
+KexiRelationViewTableContainer::KexiRelationViewTableContainer(
+ KexiRelationView *parent, KexiDB::TableOrQuerySchema *schema)
+ : QFrame(parent,"KexiRelationViewTableContainer" )
+// , m_table(t)
+ , m_parent(parent)
+// , m_mousePressed(false)
+{
+
+// setFixedSize(100, 150);
+//js: resize(100, 150);
+ //setMouseTracking(true);
+
+ setFrameStyle( QFrame::WinPanel | QFrame::Raised );
+
+ QVBoxLayout *lyr = new QVBoxLayout(this,4,1); //js: using Q*BoxLayout is a good idea
+
+ m_tableHeader = new KexiRelationViewTableContainerHeader(schema->name(), this);
+
+ m_tableHeader->unsetFocus();
+ m_tableHeader->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+ lyr->addWidget(m_tableHeader);
+ connect(m_tableHeader,SIGNAL(moved()),this,SLOT(moved()));
+ connect(m_tableHeader, SIGNAL(endDrag()), this, SIGNAL(endDrag()));
+
+ m_tableView = new KexiRelationViewTable(schema, parent, this, "KexiRelationViewTable");
+ //m_tableHeader->setFocusProxy( m_tableView );
+ m_tableView->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
+
+ m_tableView->setMaximumSize( m_tableView->sizeHint() );
+
+// m_tableView->resize( m_tableView->sizeHint() );
+ lyr->addWidget(m_tableView, 0);
+ connect(m_tableView, SIGNAL(tableScrolling()), this, SLOT(moved()));
+ connect(m_tableView, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
+ this, SLOT(slotContextMenu(KListView*, QListViewItem*, const QPoint&)));
+}
+
+KexiRelationViewTableContainer::~KexiRelationViewTableContainer()
+{
+}
+
+KexiDB::TableOrQuerySchema* KexiRelationViewTableContainer::schema() const
+{
+ return m_tableView->schema();
+}
+
+void KexiRelationViewTableContainer::slotContextMenu(KListView *, QListViewItem *, const QPoint &p)
+{
+// m_parent->executePopup(p);
+ emit contextMenuRequest( p );
+}
+
+void KexiRelationViewTableContainer::moved() {
+// kdDebug()<<"finally emitting moved"<<endl;
+ emit moved(this);
+}
+
+int KexiRelationViewTableContainer::globalY(const QString &field)
+{
+// kdDebug() << "KexiRelationViewTableContainer::globalY()" << endl;
+// QPoint o = mapFromGlobal(QPoint(0, (m_tableView->globalY(field))/*+m_parent->contentsY()*/));
+
+ QPoint o(0, (m_tableView->globalY(field)) + m_parent->contentsY());
+// kdDebug() << "KexiRelationViewTableContainer::globalY() db2" << endl;
+ return m_parent->viewport()->mapFromGlobal(o).y();
+}
+
+#if 0//js
+QSize KexiRelationViewTableContainer::sizeHint()
+{
+#ifdef Q_WS_WIN
+ QSize s = m_tableView->sizeHint()
+ + QSize( 2 * 5 , m_tableHeader->height() + 2 * 5 );
+#else
+ QSize s = m_tableView->sizeHint();
+ s.setWidth(s.width() + 4);
+ s.setHeight(m_tableHeader->height() + s.height());
+#endif
+ return s;
+}
+#endif
+
+void KexiRelationViewTableContainer::setFocus()
+{
+ kdDebug() << "SET FOCUS" << endl;
+ //select 1st:
+ if (m_tableView->firstChild()) {
+ if (!m_tableView->selectedItems().first())
+ m_tableView->setSelected( m_tableView->firstChild(), true );
+ }
+ m_tableHeader->setFocus();
+ m_tableView->setFocus();
+/* QPalette p = qApp->palette();
+ p.setColor( QPalette::Active, QColorGroup::Highlight, KGlobalSettings::highlightColor() );
+ p.setColor( QPalette::Active, QColorGroup::HighlightedText, KGlobalSettings::highlightedTextColor() );
+ m_tableView->setPalette(p);*/
+
+ raise();
+ repaint();
+ emit gotFocus();
+}
+
+void KexiRelationViewTableContainer::unsetFocus()
+{
+ kdDebug() << "UNSET FOCUS" << endl;
+// if (m_tableView->selectedItem()) //unselect item if was selected
+// m_tableView->setSelected(m_tableView->selectedItem(), false);
+// m_tableView->clearSelection();
+ m_tableHeader->unsetFocus();
+
+ m_tableView->clearSelection();
+
+// m_tableView->unsetPalette();
+/* QPalette p = m_tableView->palette();
+// p.setColor( QPalette::Active, QColorGroup::Highlight, KGlobalSettings::highlightColor() );
+// p.setColor( QPalette::Active, QColorGroup::HighlightedText, KGlobalSettings::highlightedTextColor() );
+ p.setColor( QPalette::Active, QColorGroup::Highlight, p.color(QPalette::Active, QColorGroup::Background ) );
+// p.setColor( QPalette::Active, QColorGroup::Highlight, gray );
+ p.setColor( QPalette::Active, QColorGroup::HighlightedText, p.color(QPalette::Active, QColorGroup::Foreground ) );
+// p.setColor( QPalette::Active, QColorGroup::Highlight, green );
+// p.setColor( QPalette::Active, QColorGroup::HighlightedText, blue );
+ m_tableView->setPalette(p);*/
+
+ clearFocus();
+ repaint();
+}
+
+
+//END KexiRelationViewTableContainer
+
+//============================================================================
+//BEGIN KexiRelatoinViewTableContainerHeader
+
+KexiRelationViewTableContainerHeader::KexiRelationViewTableContainerHeader(
+ const QString& text,QWidget *parent)
+ :QLabel(text,parent),m_dragging(false)
+{
+ setMargin(1);
+ m_activeBG = KGlobalSettings::activeTitleColor();
+ m_activeFG = KGlobalSettings::activeTextColor();
+ m_inactiveBG = KGlobalSettings::inactiveTitleColor();
+ m_inactiveFG = KGlobalSettings::inactiveTextColor();
+
+ installEventFilter(this);
+}
+
+KexiRelationViewTableContainerHeader::~KexiRelationViewTableContainerHeader()
+{
+}
+
+void KexiRelationViewTableContainerHeader::setFocus()
+{
+ setPaletteBackgroundColor(m_activeBG);
+ setPaletteForegroundColor(m_activeFG);
+}
+
+void KexiRelationViewTableContainerHeader::unsetFocus()
+{
+ setPaletteBackgroundColor(m_inactiveBG);
+ setPaletteForegroundColor(m_inactiveFG);
+}
+
+bool KexiRelationViewTableContainerHeader::eventFilter(QObject *, QEvent *ev)
+{
+ if (ev->type()==QEvent::MouseMove)
+ {
+ if (m_dragging && static_cast<QMouseEvent*>(ev)->state()==Qt::LeftButton) {
+ int diffX,diffY;
+ diffX=static_cast<QMouseEvent*>(ev)->globalPos().x()-m_grabX;
+ diffY=static_cast<QMouseEvent*>(ev)->globalPos().y()-m_grabY;
+ if ((abs(diffX)>2) || (abs(diffY)>2))
+ {
+ QPoint newPos=parentWidget()->pos()+QPoint(diffX,diffY);
+//correct the x position
+ if (newPos.x()<0) {
+ m_offsetX+=newPos.x();
+ newPos.setX(0);
+ }
+ else if (m_offsetX<0) {
+ m_offsetX+=newPos.x();
+ if (m_offsetX>0) {
+ newPos.setX(m_offsetX);
+ m_offsetX=0;
+ }
+ else newPos.setX(0);
+ }
+//correct the y position
+ if (newPos.y()<0) {
+ m_offsetY+=newPos.y();
+ newPos.setY(0);
+ }
+ else
+ if (m_offsetY<0) {
+ m_offsetY+=newPos.y();
+ if (m_offsetY>0) {
+ newPos.setY(m_offsetY);
+ m_offsetY=0;
+ }
+ else newPos.setY(0);
+ }
+//move and update helpers
+
+ parentWidget()->move(newPos);
+ m_grabX=static_cast<QMouseEvent*>(ev)->globalPos().x();
+ m_grabY=static_cast<QMouseEvent*>(ev)->globalPos().y();
+// kdDebug()<<"HEADER:emitting moved"<<endl;
+ emit moved();
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void KexiRelationViewTableContainerHeader::mousePressEvent(QMouseEvent *ev) {
+ kdDebug()<<"KexiRelationViewTableContainerHeader::Mouse Press Event"<<endl;
+ parentWidget()->setFocus();
+ ev->accept();
+ if (ev->button()==Qt::LeftButton) {
+ m_dragging=true;
+ m_grabX=ev->globalPos().x();
+ m_grabY=ev->globalPos().y();
+ m_offsetX=0;
+ m_offsetY=0;
+ setCursor(Qt::SizeAllCursor);
+ return;
+ }
+ if (ev->button()==Qt::RightButton) {
+ emit static_cast<KexiRelationViewTableContainer*>(parentWidget())
+ ->contextMenuRequest(ev->globalPos());
+ }
+// QLabel::mousePressEvent(ev);
+}
+
+void KexiRelationViewTableContainerHeader::mouseReleaseEvent(QMouseEvent *ev) {
+ kdDebug()<<"KexiRelationViewTableContainerHeader::Mouse Release Event"<<endl;
+ if (m_dragging && ev->button() & Qt::LeftButton) {
+ setCursor(Qt::ArrowCursor);
+ m_dragging=false;
+ emit endDrag();
+ }
+ ev->accept();
+}
+
+//END KexiRelatoinViewTableContainerHeader
+
+
+//=====================================================================================
+
+KexiRelationViewTable::KexiRelationViewTable(KexiDB::TableOrQuerySchema* tableOrQuerySchema,
+ KexiRelationView *view, QWidget *parent, const char *name)
+ : KexiFieldListView(parent, name, KexiFieldListView::ShowAsterisk)
+ , m_view(view)
+{
+ setSchema(tableOrQuerySchema);
+ header()->hide();
+
+ connect(this, SIGNAL(dropped(QDropEvent *, QListViewItem *)), this, SLOT(slotDropped(QDropEvent *)));
+ connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotContentsMoving(int,int)));
+}
+
+KexiRelationViewTable::~KexiRelationViewTable()
+{
+}
+
+QSize KexiRelationViewTable::sizeHint() const
+{
+ QFontMetrics fm(fontMetrics());
+
+// kdDebug() << schema()->name() << " cw=" << columnWidth(0) + fm.width("i")
+// << ", " << fm.width(schema()->name()+" ") << endl;
+
+ int maxWidth = -1;
+ const int iconWidth = IconSize(KIcon::Small) + fm.width("i")+20;
+ for (QListViewItem *item = firstChild(); item; item = item->nextSibling())
+ maxWidth = QMAX(maxWidth, iconWidth + fm.width(item->text(0)));
+
+ const uint rowCount = QMIN( 8, childCount() );
+
+ QSize s(
+ QMAX( maxWidth, fm.width(schema()->name()+" ")),
+ rowCount*firstChild()->totalHeight() + 4 );
+ return s;
+}
+
+#if 0
+void KexiRelationViewTable::setReadOnly(bool b)
+{
+ setAcceptDrops(!b);
+ viewport()->setAcceptDrops(!b);
+}
+#endif
+
+int
+KexiRelationViewTable::globalY(const QString &item)
+{
+ QListViewItem *i = findItem(item, 0);
+ if (!i)
+ return -1;
+ int y = itemRect(i).y() + (itemRect(i).height() / 2);
+ if (contentsY() > itemPos(i))
+ y = 0;
+ else if (y == 0)
+ y = height();
+ return mapToGlobal(QPoint(0, y)).y();
+}
+
+bool
+KexiRelationViewTable::acceptDrag(QDropEvent *ev) const
+{
+// kdDebug() << "KexiRelationViewTable::acceptDrag()" << endl;
+ QListViewItem *receiver = itemAt(ev->pos() - QPoint(0,contentsY()));
+ if (!receiver || !KexiFieldDrag::canDecodeSingle(ev))
+ return false;
+ QString sourceMimeType;
+ QString srcTable;
+ QString srcField;
+ if (!KexiFieldDrag::decodeSingle(ev,sourceMimeType,srcTable,srcField))
+ return false;
+ if (sourceMimeType!="kexi/table" && sourceMimeType=="kexi/query")
+ return false;
+ QString f = receiver->text(0).stripWhiteSpace();
+ if (!srcField.stripWhiteSpace().startsWith("*") && !f.startsWith("*") && ev->source() != (QWidget*)this)
+ return true;
+
+ return false;
+}
+
+void
+KexiRelationViewTable::slotDropped(QDropEvent *ev)
+{
+ QListViewItem *recever = itemAt(ev->pos() - QPoint(0,contentsY()));
+ if (!recever || !KexiFieldDrag::canDecodeSingle(ev)) {
+ ev->ignore();
+ return;
+ }
+ QString sourceMimeType;
+ QString srcTable;
+ QString srcField;
+ if (!KexiFieldDrag::decodeSingle(ev,sourceMimeType,srcTable,srcField))
+ return;
+ if (sourceMimeType!="kexi/table" && sourceMimeType=="kexi/query")
+ return;
+// kdDebug() << "KexiRelationViewTable::slotDropped() srcfield: " << srcField << endl;
+
+ QString rcvField = recever->text(0);
+
+ SourceConnection s;
+ s.masterTable = srcTable;
+ s.detailsTable = schema()->name();
+ s.masterField = srcField;
+ s.detailsField = rcvField;
+
+ m_view->addConnection(s);
+
+ kdDebug() << "KexiRelationViewTable::slotDropped() " << srcTable << ":" << srcField << " "
+ << schema()->name() << ":" << rcvField << endl;
+ ev->accept();
+}
+
+void
+KexiRelationViewTable::slotContentsMoving(int,int)
+{
+ emit tableScrolling();
+}
+
+void KexiRelationViewTable::contentsMousePressEvent(QMouseEvent *ev)
+{
+ parentWidget()->setFocus();
+ setFocus();
+ KListView::contentsMousePressEvent(ev);
+// if (ev->button()==Qt::RightButton)
+// static_cast<KexiRelationView*>(parentWidget())->executePopup(ev->pos());
+}
+
+QRect KexiRelationViewTable::drawItemHighlighter(QPainter *painter, QListViewItem *item)
+{
+ if (painter) {
+ style().drawPrimitive(QStyle::PE_FocusRect, painter, itemRect(item), colorGroup(),
+ QStyle::Style_FocusAtBorder);
+ }
+ return itemRect(item);
+}
+
+#include "kexirelationviewtable.moc"
diff --git a/kexi/widget/relations/kexirelationviewtable.h b/kexi/widget/relations/kexirelationviewtable.h
new file mode 100644
index 00000000..cc90e16d
--- /dev/null
+++ b/kexi/widget/relations/kexirelationviewtable.h
@@ -0,0 +1,157 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-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 KEXIRELATIONVIEWTABLE_H
+#define KEXIRELATIONVIEWTABLE_H
+
+#include <qframe.h>
+#include <qstringlist.h>
+#include <qlabel.h>
+#include <klistview.h>
+
+#include <widget/kexifieldlistview.h>
+
+class KexiRelationView;
+class KexiRelationViewTable;
+class KexiRelationViewTableContainerHeader;
+
+namespace KexiDB
+{
+ class TableOrQuerySchema;
+}
+
+class KEXIRELATIONSVIEW_EXPORT KexiRelationViewTableContainer : public QFrame
+{
+ Q_OBJECT
+
+ public:
+// KexiRelationViewTableContainer(KexiRelationView *parent, KexiDB::TableSchema *t);
+ KexiRelationViewTableContainer(
+ KexiRelationView *parent, KexiDB::TableOrQuerySchema *schema);
+
+ virtual ~KexiRelationViewTableContainer();
+
+ int globalY(const QString &field);
+// KexiDB::TableSchema *table();
+
+ KexiRelationViewTable* tableView() const { return m_tableView; }
+ KexiDB::TableOrQuerySchema* schema() const;
+
+ int right() { return x() + width() - 1; }
+ int bottom() { return y() + height() - 1; }
+
+ signals:
+ void moved(KexiRelationViewTableContainer *);
+ void endDrag();
+ void gotFocus();
+ void contextMenuRequest(const QPoint& pos);
+
+ public slots:
+ virtual void setFocus();
+ virtual void unsetFocus();
+
+ protected slots:
+ void moved();
+ void slotContextMenu(KListView *lv, QListViewItem *i, const QPoint& p);
+
+ protected:
+// KexiDB::TableSchema *m_table;
+ KexiRelationViewTableContainerHeader *m_tableHeader;
+ KexiRelationViewTable *m_tableView;
+ KexiRelationView *m_parent;
+
+ friend class KexiRelationViewTableContainerHeader;
+};
+
+/*
+class KEXIRELATIONSVIEW_EXPORT KexiRelationViewTableItem : public KListViewItem
+{
+ public:
+ KexiRelationViewTableItem(QListView *parent, QListViewItem *after,
+ QString key, QString field);
+ virtual void paintFocus ( QPainter * p, const QColorGroup & cg, const QRect & r );
+};*/
+
+
+class KEXIRELATIONSVIEW_EXPORT KexiRelationViewTable : public KexiFieldListView
+{
+ Q_OBJECT
+
+ public:
+ KexiRelationViewTable(KexiDB::TableOrQuerySchema* tableOrQuerySchema,
+ KexiRelationView *view, QWidget *parent, const char *name = 0);
+// KexiRelationViewTable(QWidget *parent, KexiRelationView *view, KexiDB::TableSchema *t, const char *name=0);
+ virtual ~KexiRelationViewTable();
+
+// KexiDB::TableSchema *table() const { return m_table; };
+ int globalY(const QString &item);
+// void setReadOnly(bool);
+ virtual QSize sizeHint() const;
+
+ signals:
+ void tableScrolling();
+
+ protected slots:
+ void slotDropped(QDropEvent *e);
+ void slotContentsMoving(int, int);
+// void slotItemDoubleClicked( QListViewItem *i, const QPoint &, int );
+
+ protected:
+ virtual void contentsMousePressEvent( QMouseEvent * e );
+ virtual bool acceptDrag(QDropEvent *e) const;
+//moved virtual QDragObject *dragObject();
+ virtual QRect drawItemHighlighter(QPainter *painter, QListViewItem *item);
+
+ private:
+// QStringList m_fieldList;
+// KexiDB::TableSchema *m_table;
+ KexiRelationView *m_view;
+// QPixmap m_keyIcon, m_noIcon;
+};
+
+class KEXIRELATIONSVIEW_EXPORT KexiRelationViewTableContainerHeader : public QLabel
+{
+ Q_OBJECT
+ public:
+ KexiRelationViewTableContainerHeader(const QString& text,QWidget *parent);
+ virtual ~KexiRelationViewTableContainerHeader();
+
+ virtual void setFocus();
+ virtual void unsetFocus();
+
+ signals:
+ void moved();
+ void endDrag();
+
+ protected:
+ bool eventFilter(QObject *obj, QEvent *ev);
+ void mousePressEvent(QMouseEvent *ev);
+ void mouseReleaseEvent(QMouseEvent *ev);
+
+ bool m_dragging;
+ int m_grabX;
+ int m_grabY;
+ int m_offsetX;
+ int m_offsetY;
+
+ QColor m_activeBG, m_activeFG, m_inactiveBG, m_inactiveFG;
+};
+
+#endif
diff --git a/kexi/widget/relations/kexirelationwidget.cpp b/kexi/widget/relations/kexirelationwidget.cpp
new file mode 100644
index 00000000..14ec4ce0
--- /dev/null
+++ b/kexi/widget/relations/kexirelationwidget.cpp
@@ -0,0 +1,425 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Joseph Wenninger<jowenn@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 "kexirelationwidget.h"
+
+#include <qlayout.h>
+#include <qlistbox.h>
+#include <qpushbutton.h>
+#include <qtimer.h>
+
+#include <kcombobox.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kpushbutton.h>
+
+#include <kexidb/connection.h>
+#include <kexidb/utils.h>
+
+#include <kexiproject.h>
+#include <keximainwindow.h>
+#include "kexirelationview.h"
+#include "kexirelationviewtable.h"
+#include "kexirelationviewconnection.h"
+
+KexiRelationWidget::KexiRelationWidget(KexiMainWindow *win, QWidget *parent,
+ const char *name)
+ : KexiViewBase(win, parent, name)
+ , m_win(win)
+{
+ m_conn = m_win->project()->dbConnection();
+
+ QHBoxLayout *hlyr = new QHBoxLayout(0);
+ QGridLayout *g = new QGridLayout(this);
+ g->addLayout( hlyr, 0, 0 );
+
+ m_tableCombo = new KComboBox(this, "tables_combo");
+ m_tableCombo->setMinimumWidth(QFontMetrics(font()).width("w")*20);
+ QLabel *lbl = new QLabel(m_tableCombo, i18n("Table")+": ", this);
+ lbl->setIndent(3);
+ m_tableCombo->setInsertionPolicy(QComboBox::NoInsertion);
+ hlyr->addWidget(lbl);
+ hlyr->addWidget(m_tableCombo);
+ m_tableCombo->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred));
+ fillTablesCombo();
+
+ m_btnAdd = new KPushButton(i18n("&Add"), this);
+ hlyr->addWidget(m_btnAdd);
+ hlyr->addStretch(1);
+ connect(m_btnAdd, SIGNAL(clicked()), this, SLOT(slotAddTable()));
+
+ m_relationView = new KexiRelationView(this, "relation_view");
+ setViewWidget(m_relationView);
+ g->addWidget(m_relationView, 1, 0);
+ //m_relationView->setFocus();
+
+ //actions
+ m_tableQueryPopup = new KPopupMenu(this, "m_popup");
+ m_tableQueryPopupTitleID = m_tableQueryPopup->insertTitle(SmallIcon("table"), "");
+ connect(m_tableQueryPopup, SIGNAL(aboutToShow()), this, SLOT(aboutToShowPopupMenu()));
+
+ m_connectionPopup = new KPopupMenu(this, "m_connectionPopup");
+ m_connectionPopupTitleID = m_connectionPopup->insertTitle("");
+ connect(m_connectionPopup, SIGNAL(aboutToShow()), this, SLOT(aboutToShowPopupMenu()));
+
+ m_areaPopup = new KPopupMenu(this, "m_areaPopup");
+
+ m_openSelectedTableAction = new KAction(i18n("&Open Table"), SmallIcon("fileopen"), KShortcut(),
+ this, SLOT(openSelectedTable()), this, "relationsview_openTable");
+ m_openSelectedTableAction->plug( m_tableQueryPopup );
+ m_designSelectedTableAction = new KAction(i18n("&Design Table"), SmallIcon("edit"), KShortcut(),
+ this, SLOT(designSelectedTable()), this, "relationsview_designTable");
+ m_designSelectedTableAction->plug( m_tableQueryPopup );
+ m_tableQueryPopup->insertSeparator();
+
+ KAction* hide_action = plugSharedAction("edit_delete", i18n("&Hide Table"), m_tableQueryPopup);
+ hide_action->setIconSet(QIconSet());
+
+ plugSharedAction("edit_delete",m_connectionPopup);
+ plugSharedAction("edit_delete",this, SLOT(removeSelectedObject()));
+
+ connect(m_relationView, SIGNAL(tableViewGotFocus()),
+ this, SLOT(tableViewGotFocus()));
+ connect(m_relationView, SIGNAL(connectionViewGotFocus()),
+ this, SLOT(connectionViewGotFocus()));
+ connect(m_relationView, SIGNAL(emptyAreaGotFocus()),
+ this, SLOT(emptyAreaGotFocus()));
+ connect(m_relationView, SIGNAL(tableContextMenuRequest( const QPoint& )),
+ this, SLOT(tableContextMenuRequest( const QPoint& )));
+ connect(m_relationView, SIGNAL(connectionContextMenuRequest( const QPoint& )),
+ this, SLOT(connectionContextMenuRequest( const QPoint& )));
+ connect(m_relationView, SIGNAL(tableHidden(KexiDB::TableSchema&)),
+ this, SLOT(slotTableHidden(KexiDB::TableSchema&)));
+ connect(m_relationView, SIGNAL(tablePositionChanged(KexiRelationViewTableContainer*)),
+ this, SIGNAL(tablePositionChanged(KexiRelationViewTableContainer*)));
+ connect(m_relationView, SIGNAL(aboutConnectionRemove(KexiRelationViewConnection*)),
+ this, SIGNAL(aboutConnectionRemove(KexiRelationViewConnection*)));
+
+#if 0
+ if(!embedd)
+ {
+/*todo setContextHelp(i18n("Relations"), i18n("To create a relationship simply drag the source field onto the target field. "
+ "An arrowhead is used to show which table is the parent (master) and which table is the child (slave) in the relationship."));*/
+ }
+#endif
+// else
+//js: while embedding means read-only? m_relationView->setReadOnly(true);
+
+#ifdef TESTING_KexiRelationWidget
+ for (int i=0;i<(int)m_db->tableNames().count();i++)
+ QTimer::singleShot(100,this,SLOT(slotAddTable()));
+#endif
+
+ invalidateActions();
+}
+
+KexiRelationWidget::~KexiRelationWidget()
+{
+}
+
+TablesDict* KexiRelationWidget::tables() const
+{
+ return m_relationView->tables();
+}
+
+KexiRelationViewTableContainer* KexiRelationWidget::table(const QString& name) const
+{
+ return m_relationView->tables()->find( name );
+}
+
+const ConnectionList* KexiRelationWidget::connections() const
+{
+ return m_relationView->connections();
+}
+
+void
+KexiRelationWidget::slotAddTable()
+{
+ if (m_tableCombo->currentItem()==-1)
+ return;
+ QString tname = m_tableCombo->text(m_tableCombo->currentItem());
+ KexiDB::TableSchema *t = m_conn->tableSchema(tname);
+ addTable(t);
+}
+
+void
+KexiRelationWidget::addTable(KexiDB::TableSchema *t, const QRect &rect)
+{
+ if (!t)
+ return;
+ if (!m_relationView->tableContainer(t)) {
+ KexiRelationViewTableContainer *c = m_relationView->addTableContainer(t, rect);
+ kdDebug() << "KexiRelationWidget::slotAddTable(): adding table " << t->name() << endl;
+ if (!c)
+ return;
+ connect(c->tableView(), SIGNAL(doubleClicked(QListViewItem*,const QPoint&,int)),
+ this, SLOT(slotTableFieldDoubleClicked(QListViewItem*,const QPoint&,int)));
+ }
+
+ const QString tname = t->name().lower();
+ const int count = m_tableCombo->count();
+ int i = 0;
+ for (; i < count; i++ ) {
+ if (m_tableCombo->text(i).lower() == tname )
+ break;
+ }
+ if (i<count) {
+ int oi = m_tableCombo->currentItem();
+ kdDebug()<<"KexiRelationWidget::slotAddTable(): removing a table from the combo box"<<endl;
+ m_tableCombo->removeItem(i);
+ if (m_tableCombo->count()>0) {
+ if (oi>=m_tableCombo->count()) {
+ oi=m_tableCombo->count()-1;
+ }
+ m_tableCombo->setCurrentItem(oi);
+ }
+ else {
+ m_tableCombo->setEnabled(false);
+ m_btnAdd->setEnabled(false);
+ }
+ }
+ emit tableAdded(*t);
+}
+
+void
+KexiRelationWidget::addConnection(const SourceConnection& conn)
+{
+ m_relationView->addConnection(conn);
+}
+
+void
+KexiRelationWidget::addTable(const QString& t)
+{
+ for(int i=0; i < m_tableCombo->count(); i++)
+ {
+ if(m_tableCombo->text(i) == t)
+ {
+ m_tableCombo->setCurrentItem(i);
+ slotAddTable();
+ }
+ }
+}
+
+void KexiRelationWidget::tableViewGotFocus()
+{
+// if (m_relationView->focusedTableView == sender())
+// return;
+// kdDebug() << "GOT FOCUS!" <<endl;
+// clearSelection();
+// if (m_focusedTableView)
+// m_focusedTableView->unsetFocus();
+// m_focusedTableView = (KexiRelationViewTableContainer*)sender();
+ invalidateActions();
+}
+
+void KexiRelationWidget::connectionViewGotFocus()
+{
+ invalidateActions();
+}
+
+void KexiRelationWidget::emptyAreaGotFocus()
+{
+ invalidateActions();
+}
+
+void KexiRelationWidget::tableContextMenuRequest(const QPoint& pos)
+{
+ invalidateActions();
+ executePopup( pos );
+}
+
+void KexiRelationWidget::connectionContextMenuRequest(const QPoint& pos)
+{
+ invalidateActions();
+ executePopup( pos );
+// m_connectionPopup->exec(pos);
+}
+
+void KexiRelationWidget::emptyAreaContextMenuRequest( const QPoint& /*pos*/ )
+{
+ invalidateActions();
+ //TODO
+}
+
+void KexiRelationWidget::invalidateActions()
+{
+ setAvailable("edit_delete", m_relationView->selectedConnection() || m_relationView->focusedTableView());
+}
+
+void KexiRelationWidget::executePopup( QPoint pos )
+{
+ if (pos==QPoint(-1,-1)) {
+ pos = mapToGlobal(
+ m_relationView->focusedTableView() ? m_relationView->focusedTableView()->pos() + m_relationView->focusedTableView()->rect().center() : rect().center() );
+ }
+ if (m_relationView->focusedTableView())
+ m_tableQueryPopup->exec(pos);
+ else if (m_relationView->selectedConnection())
+ m_connectionPopup->exec(pos);
+}
+
+void KexiRelationWidget::removeSelectedObject()
+{
+ m_relationView->removeSelectedObject();
+}
+
+void KexiRelationWidget::openSelectedTable()
+{
+/*! @todo what about query? */
+ if (!m_relationView->focusedTableView() || !m_relationView->focusedTableView()->schema()->table())
+ return;
+ bool openingCancelled;
+ m_win->openObject("kexi/table", m_relationView->focusedTableView()->schema()->name(),
+ Kexi::DataViewMode, openingCancelled);
+}
+
+void KexiRelationWidget::designSelectedTable()
+{
+/*! @todo what about query? */
+ if (!m_relationView->focusedTableView() || !m_relationView->focusedTableView()->schema()->table())
+ return;
+ bool openingCancelled;
+ m_win->openObject("kexi/table", m_relationView->focusedTableView()->schema()->name(),
+ Kexi::DesignViewMode, openingCancelled);
+}
+
+QSize KexiRelationWidget::sizeHint() const
+{
+ return m_relationView->sizeHint();
+}
+
+void KexiRelationWidget::slotTableHidden(KexiDB::TableSchema &table)
+{
+ const QString &t = table.name().lower();
+ int i;
+ for (i=0; i<m_tableCombo->count() && t > m_tableCombo->text(i).lower(); i++)
+ ;
+ m_tableCombo->insertItem(table.name(), i);
+ if (!m_tableCombo->isEnabled()) {
+ m_tableCombo->setCurrentItem(0);
+ m_tableCombo->setEnabled(true);
+ m_btnAdd->setEnabled(true);
+ }
+
+ emit tableHidden(table);
+}
+
+void KexiRelationWidget::aboutToShowPopupMenu()
+{
+/*! @todo what about query? */
+ if (m_relationView->focusedTableView() && m_relationView->focusedTableView()->schema()->table()) {
+ m_tableQueryPopup->changeTitle(m_tableQueryPopupTitleID, SmallIcon("table"),
+ QString(m_relationView->focusedTableView()->schema()->name()) + " : " + i18n("Table"));
+ }
+ else if (m_relationView->selectedConnection()) {
+ m_connectionPopup->changeTitle( m_connectionPopupTitleID,
+ m_relationView->selectedConnection()->toString() + " : " + i18n("Relationship") );
+ }
+}
+
+void
+KexiRelationWidget::slotTableFieldDoubleClicked(QListViewItem *i,const QPoint&,int)
+{
+ if (!sender()->isA("KexiRelationViewTable"))
+ return;
+ const KexiRelationViewTable* t = static_cast<const KexiRelationViewTable*>(sender());
+ const QStringList selectedFieldNames( t->selectedFieldNames() );
+ if (selectedFieldNames.count()==1)
+ emit tableFieldDoubleClicked( t->schema()->table(), selectedFieldNames.first() );
+}
+
+void
+KexiRelationWidget::clear()
+{
+ m_relationView->clear();
+ fillTablesCombo();
+}
+
+/*! Removes all coonections from the view. */
+void KexiRelationWidget::removeAllConnections()
+{
+ m_relationView->removeAllConnections();
+}
+
+void
+KexiRelationWidget::fillTablesCombo()
+{
+ m_tableCombo->clear();
+ QStringList tmp = m_conn->tableNames();
+ tmp.sort();
+ m_tableCombo->insertStringList(tmp);
+}
+
+void
+KexiRelationWidget::objectCreated(const QCString &mime, const QCString& name)
+{
+ if (mime=="kexi/table" || mime=="kexi/query") {
+//! @todo query?
+ m_tableCombo->insertItem(QString(name));
+ m_tableCombo->listBox()->sort();
+ }
+}
+
+void
+KexiRelationWidget::objectDeleted(const QCString &mime, const QCString& name)
+{
+ if (mime=="kexi/table" || mime=="kexi/query") {
+ QString strName(name);
+ for (int i=0; i<m_tableCombo->count(); i++) {
+//! @todo query?
+ if (m_tableCombo->text(i)==strName) {
+ m_tableCombo->removeItem(i);
+ if (m_tableCombo->currentItem()==i) {
+ if (i==(m_tableCombo->count()-1))
+ m_tableCombo->setCurrentItem(i-1);
+ else
+ m_tableCombo->setCurrentItem(i);
+ }
+ break;
+ }
+ }
+ }
+}
+
+void
+KexiRelationWidget::objectRenamed(const QCString &mime, const QCString& name, const QCString& newName)
+{
+ if (mime=="kexi/table" || mime=="kexi/query") {
+ QString strName(name);
+ for (int i=0; i<m_tableCombo->count(); i++) {
+//! @todo query?
+ if (m_tableCombo->text(i)==strName) {
+ m_tableCombo->changeItem(QString(newName), i);
+ m_tableCombo->listBox()->sort();
+ break;
+ }
+ }
+ }
+}
+
+void
+KexiRelationWidget::hideAllTablesExcept( KexiDB::TableSchema::List* tables )
+{
+ m_relationView->hideAllTablesExcept(tables);
+}
+
+#include "kexirelationwidget.moc"
diff --git a/kexi/widget/relations/kexirelationwidget.h b/kexi/widget/relations/kexirelationwidget.h
new file mode 100644
index 00000000..3beb0b34
--- /dev/null
+++ b/kexi/widget/relations/kexirelationwidget.h
@@ -0,0 +1,137 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ 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 KEXIRELATIONWIDGET_H
+#define KEXIRELATIONWIDGET_H
+
+//#include <qwidget.h>
+//#include "kexiactionproxy.h"
+#include "kexiviewbase.h"
+#include "kexirelationview.h"
+
+class KComboBox;
+class KPushButton;
+class KPopupMenu;
+class KAction;
+class QListViewItem;
+
+class KexiMainWindow;
+
+namespace KexiDB
+{
+ class Connection;
+ class TableSchema;
+ class Reference;
+}
+
+class KEXIRELATIONSVIEW_EXPORT KexiRelationWidget : public KexiViewBase
+{
+ Q_OBJECT
+
+ public:
+ KexiRelationWidget(KexiMainWindow *win, QWidget *parent, const char *name=0);
+ virtual ~KexiRelationWidget();
+
+ //! \return a dictionary of added tables
+ TablesDict* tables() const;
+ KexiRelationViewTableContainer* table(const QString& name) const;
+ const ConnectionList* connections() const;
+
+// KexiRelationView *relationView() const { return m_relationView; }
+ void addTable(const QString& t);
+
+// void openTable(KexiDB::TableSchema* table, bool designMode);
+
+ virtual QSize sizeHint() const;
+
+ /*! Used to add newly created object information to the combo box. */
+ void objectCreated(const QCString &mime, const QCString& name);
+ void objectDeleted(const QCString &mime, const QCString& name);
+ void objectRenamed(const QCString &mime, const QCString& name, const QCString& newName);
+
+ signals:
+ void tableAdded(KexiDB::TableSchema& t);
+ void tableHidden(KexiDB::TableSchema& t);
+ void tablePositionChanged(KexiRelationViewTableContainer*);
+ void aboutConnectionRemove(KexiRelationViewConnection*);
+ void tableFieldDoubleClicked( KexiDB::TableSchema* table, const QString& fieldName );
+
+ public slots:
+ /*! Adds a table \a t to the area. This changes only visual representation.
+ If \a rect is valid, table widget rgeometry will be initialized.
+ */
+ void addTable(KexiDB::TableSchema *t, const QRect &rect = QRect());
+
+ //! Adds a connection \a con to the area. This changes only visual representation.
+ void addConnection(const SourceConnection& conn);
+
+ void removeSelectedObject();
+
+ /*! Removes all tables and coonections from the widget. */
+ void clear();
+
+ /*! Removes all coonections from the view. */
+ void removeAllConnections();
+
+ /*! Hides all tables except \a tables. */
+ void hideAllTablesExcept( KexiDB::TableSchema::List* tables );
+
+ protected slots:
+ void slotAddTable();
+ void tableViewGotFocus();
+ void connectionViewGotFocus();
+ void emptyAreaGotFocus();
+ void tableContextMenuRequest(const QPoint& pos);
+ void connectionContextMenuRequest(const QPoint& pos);
+ void emptyAreaContextMenuRequest( const QPoint& pos );
+ void openSelectedTable();
+ void designSelectedTable();
+ void slotTableHidden(KexiDB::TableSchema &table);
+ void aboutToShowPopupMenu();
+ void slotTableFieldDoubleClicked(QListViewItem *i,const QPoint&,int);
+
+ protected:
+ /*! executes popup menu at \a pos, or,
+ if \a pos not specified: at center of selected table view (if any selected),
+ or at center point of the relations view. */
+ void executePopup( QPoint pos = QPoint(-1,-1) );
+
+ //! Invalidates all actions availability.
+ void invalidateActions();
+
+ //! Fills table's combo box with all available table names.
+ void fillTablesCombo();
+
+ private:
+ KexiMainWindow *m_win;
+ KComboBox *m_tableCombo;
+ KPushButton *m_btnAdd;
+ KexiRelationView *m_relationView;
+ KexiDB::Connection *m_conn;
+
+ KPopupMenu *m_tableQueryPopup //over table/query
+ , *m_connectionPopup //over connection
+ , *m_areaPopup; //over outer area
+ KAction *m_openSelectedTableAction, *m_designSelectedTableAction;
+
+ int m_tableQueryPopupTitleID, m_connectionPopupTitleID;
+};
+
+#endif
diff --git a/kexi/widget/relations/r1.xpm b/kexi/widget/relations/r1.xpm
new file mode 100644
index 00000000..11b8dc66
--- /dev/null
+++ b/kexi/widget/relations/r1.xpm
@@ -0,0 +1,10 @@
+/* XPM */
+static const char * r1_xpm[] = {
+"2 5 2 1",
+" c None",
+". c #000000",
+" .",
+"..",
+" .",
+" .",
+" ."};
diff --git a/kexi/widget/relations/rn.xpm b/kexi/widget/relations/rn.xpm
new file mode 100644
index 00000000..d721e5f5
--- /dev/null
+++ b/kexi/widget/relations/rn.xpm
@@ -0,0 +1,9 @@
+/* XPM */
+static const char * rn_xpm[] = {
+"7 4 2 1",
+" c None",
+". c #000000",
+" .. .. ",
+". . .",
+". . .",
+" .. .. "};
diff --git a/kexi/widget/tableview/Makefile.am b/kexi/widget/tableview/Makefile.am
new file mode 100644
index 00000000..69bb400d
--- /dev/null
+++ b/kexi/widget/tableview/Makefile.am
@@ -0,0 +1,49 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+lib_LTLIBRARIES = libkexidatatable.la
+libkexidatatable_la_SOURCES = kexidataawareobjectiface.cpp \
+ kexitableview.cpp kexitableview_p.cpp kexidatatableview.cpp \
+ kexicelleditorfactory.cpp kexitableedit.cpp \
+ kexitableviewheader.cpp kexitableitem.cpp kexitableviewdata.cpp \
+ kexidatetableedit.cpp kexitimetableedit.cpp kexidatetimetableedit.cpp \
+ kexiinputtableedit.cpp kexiblobtableedit.cpp kexibooltableedit.cpp \
+ kexicomboboxbase.cpp kexicomboboxtableedit.cpp kexicomboboxpopup.cpp \
+ kexidataawarepropertyset.cpp kexitextformatter.cpp
+
+noinst_HEADERS = kexitableview_p.h
+
+libkexidatatable_la_LDFLAGS = $(all_libraries) $(VER_INFO) -Wnounresolved
+libkexidatatable_la_LIBADD = $(top_builddir)/kexi/core/libkexicore.la $(top_builddir)/kexi/widget/utils/libkexiguiutils.la \
+ $(top_builddir)/lib/koproperty/libkoproperty.la $(LIB_KDEUI)
+
+#TODO: remove libkexicore link when kexiutils arrive
+
+SUBDIRS = .
+
+
+# kde_appsdir Where your application's menu entry (.desktop) should go to.
+# kde_icondir Where your icon should go to - better use KDE_ICON.
+# kde_sounddir Where your sounds should go to.
+# kde_htmldir Where your docs should go to. (contains lang subdirs)
+# kde_datadir Where you install application data. (Use a subdir)
+# kde_locale Where translation files should go to. (contains lang subdirs)
+# kde_cgidir Where cgi-bin executables should go to.
+# kde_confdir Where config files should go to (system-wide ones with default values).
+# kde_mimedir Where mimetypes .desktop files should go to.
+# kde_servicesdir Where services .desktop files should go to.
+# kde_servicetypesdir Where servicetypes .desktop files should go to.
+# kde_toolbardir Where general toolbar icons should go to (deprecated, use KDE_ICON).
+# kde_wallpaperdir Where general wallpapers should go to.
+# kde_templatesdir Where templates for the "New" menu (Konqueror/KDesktop) should go to.
+# kde_bindir Where executables should go to. Use bin_PROGRAMS or bin_SCRIPTS.
+# kde_libdir Where shared libraries should go to. Use lib_LTLIBRARIES.
+# kde_moduledir Where modules (e.g. parts) should go to. Use kde_module_LTLIBRARIES.
+# kde_styledir Where Qt/KDE widget styles should go to (new in KDE 3).
+# kde_designerdir Where Qt Designer plugins should go to (new in KDE 3).
+
+# set the include path for X, qt and KDE
+INCLUDES= -I$(top_srcdir)/kexi -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi/kexidb \
+ -I$(top_srcdir)/lib -I$(top_srcdir)/lib/kofficecore -I$(top_srcdir)/lib/koproperty/ $(all_includes)
+
+METASOURCES = AUTO
+
diff --git a/kexi/widget/tableview/autonumber.png b/kexi/widget/tableview/autonumber.png
new file mode 100644
index 00000000..23fff353
--- /dev/null
+++ b/kexi/widget/tableview/autonumber.png
Binary files differ
diff --git a/kexi/widget/tableview/kexiblobtableedit.cpp b/kexi/widget/tableview/kexiblobtableedit.cpp
new file mode 100644
index 00000000..db0799a4
--- /dev/null
+++ b/kexi/widget/tableview/kexiblobtableedit.cpp
@@ -0,0 +1,595 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2004, 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 "kexiblobtableedit.h"
+
+#include <stdlib.h>
+
+#include <qdatastream.h>
+#include <qfile.h>
+#include <qpopupmenu.h>
+#include <qtextedit.h>
+#include <qlayout.h>
+#include <qstatusbar.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qbuffer.h>
+
+#include <kdebug.h>
+#include <ktempfile.h>
+#include <kmimetype.h>
+#include <kmimemagic.h>
+#include <kuserprofile.h>
+#include <kservice.h>
+#include <kprocess.h>
+#include <kopenwith.h>
+#include <kurl.h>
+#include <karrowbutton.h>
+#include <klocale.h>
+#include <kfiledialog.h>
+#include <kio/job.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kpopupmenu.h>
+#include <kstdaccel.h>
+
+#include <kexiutils/utils.h>
+#include <widget/utils/kexidropdownbutton.h>
+#include <widget/utils/kexicontextmenuutils.h>
+
+//! @internal
+class KexiBlobTableEdit::Private
+{
+public:
+ Private()
+ : popup(0)
+ , readOnly(false)
+ , setValueInternalEnabled(true)
+ {
+ }
+
+ QByteArray value;
+ KexiDropDownButton *button;
+ QSize totalSize;
+ KexiImageContextMenu *popup;
+ bool readOnly : 1; //!< cached for slotUpdateActionsAvailabilityRequested()
+ bool setValueInternalEnabled : 1; //!< used to disable KexiBlobTableEdit::setValueInternal()
+};
+
+//======================================================
+
+KexiBlobTableEdit::KexiBlobTableEdit(KexiTableViewColumn &column, QWidget *parent)
+ : KexiTableEdit(column, parent)
+ , d ( new Private() )
+{
+ setName("KexiBlobTableEdit");
+// m_proc = 0;
+// m_content = 0;
+ m_hasFocusableWidget = false;
+ d->button = new KexiDropDownButton( parentWidget() /*usually a viewport*/ );
+ d->button->hide();
+ QToolTip::add(d->button, i18n("Click to show available actions for this cell"));
+
+ d->popup = new KexiImageContextMenu(this);
+ d->popup->installEventFilter(this);
+ if (column.columnInfo)
+ KexiImageContextMenu::updateTitle( d->popup, column.columnInfo->captionOrAliasOrName(),
+//! @todo pixmaplabel icon is hardcoded...
+ "pixmaplabel" );
+ d->button->setPopup( d->popup );
+
+ //force edit requested to start editing... (this will call slotUpdateActionsAvailabilityRequested())
+ //connect(d->popup, SIGNAL(aboutToShow()), this, SIGNAL(editRequested()));
+
+ connect(d->popup, SIGNAL(updateActionsAvailabilityRequested(bool&, bool&)),
+ this, SLOT(slotUpdateActionsAvailabilityRequested(bool&, bool&)));
+
+ connect(d->popup, SIGNAL(insertFromFileRequested(const KURL&)),
+ this, SLOT(handleInsertFromFileAction(const KURL&)));
+ connect(d->popup, SIGNAL(saveAsRequested(const QString&)),
+ this, SLOT(handleSaveAsAction(const QString&)));
+ connect(d->popup, SIGNAL(cutRequested()),
+ this, SLOT(handleCutAction()));
+ connect(d->popup, SIGNAL(copyRequested()),
+ this, SLOT(handleCopyAction()));
+ connect(d->popup, SIGNAL(pasteRequested()),
+ this, SLOT(handlePasteAction()));
+ connect(d->popup, SIGNAL(clearRequested()),
+ this, SLOT(clear()));
+ connect(d->popup, SIGNAL(showPropertiesRequested()),
+ this, SLOT(handleShowPropertiesAction()));
+}
+
+KexiBlobTableEdit::~KexiBlobTableEdit()
+{
+ delete d;
+#if 0
+ kdDebug() << "KexiBlobTableEdit: Cleaning up..." << endl;
+ if (m_tempFile) {
+ m_tempFile->unlink();
+ //todo
+ }
+ delete m_proc;
+ m_proc = 0;
+ kdDebug() << "KexiBlobTableEdit: Ready." << endl;
+#endif
+}
+
+//! initializes this editor with \a add value
+void KexiBlobTableEdit::setValueInternal(const QVariant& add, bool removeOld)
+{
+ if (!d->setValueInternalEnabled)
+ return;
+ if (removeOld)
+ d->value = add.toByteArray();
+ else //do not add "m_origValue" to "add" as this is QByteArray
+ d->value = m_origValue.toByteArray();
+
+#if 0 //todo?
+ QByteArray val = m_origValue.toByteArray();
+ kdDebug() << "KexiBlobTableEdit: Size of BLOB: " << val.size() << endl;
+ m_tempFile = new KTempFile();
+ m_tempFile->setAutoDelete(true);
+ kdDebug() << "KexiBlobTableEdit: Creating temporary file: " << m_tempFile->name() << endl;
+ m_tempFile->dataStream()->writeRawBytes(val.data(), val.size());
+ m_tempFile->close();
+ delete m_tempFile;
+ m_tempFile = 0;
+
+ KMimeMagicResult* mmr = KMimeMagic::self()->findFileType(m_tempFile->name());
+ kdDebug() << "KexiBlobTableEdit: Mimetype = " << mmr->mimeType() << endl;
+
+ setViewWidget( new QWidget(this) );
+#endif
+}
+
+bool KexiBlobTableEdit::valueIsNull()
+{
+//TODO
+ d->value.size();
+ return d->value.isEmpty();
+}
+
+bool KexiBlobTableEdit::valueIsEmpty()
+{
+//TODO
+ return d->value.isEmpty();
+}
+
+QVariant
+KexiBlobTableEdit::value()
+{
+ return d->value;
+#if 0
+ //todo
+// ok = true;
+
+ if(m_content && m_content->isModified())
+ {
+ return QVariant(m_content->text());
+ }
+ QByteArray value;
+ QFile f( m_tempFile->name() );
+ f.open(IO_ReadOnly);
+ QDataStream stream(&f);
+ char* data = (char*) malloc(f.size());
+ value.resize(f.size());
+ stream.readRawBytes(data, f.size());
+ value.duplicate(data, f.size());
+ free(data);
+ kdDebug() << "KexiBlobTableEdit: Size of BLOB: " << value.size() << endl;
+ return QVariant(value);
+#endif
+}
+
+void KexiBlobTableEdit::paintFocusBorders( QPainter *p, QVariant &, int x, int y, int w, int h )
+{
+// d->currentEditorWidth = w;
+ if (!d->readOnly && w > d->button->width())
+ w -= d->button->width();
+ p->drawRect(x, y, w, h);
+}
+
+void
+KexiBlobTableEdit::setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h )
+{
+ Q_UNUSED(focused);
+ Q_UNUSED(txt);
+ Q_UNUSED(align);
+
+//! @todo optimize: load to m_pixmap, downsize
+ QPixmap pixmap;
+ x = 0;
+ w -= 1; //a place for border
+ h -= 1; //a place for border
+ if (p && val.canCast(QVariant::ByteArray) && pixmap.loadFromData(val.toByteArray())) {
+ KexiUtils::drawPixmap( *p, 0/*lineWidth*/, QRect(x, y_offset, w, h),
+ pixmap, Qt::AlignCenter, true/*scaledContents*/, true/*keepAspectRatio*/);
+ }
+}
+
+bool KexiBlobTableEdit::cursorAtStart()
+{
+ return true;
+}
+
+bool KexiBlobTableEdit::cursorAtEnd()
+{
+ return true;
+}
+
+void KexiBlobTableEdit::handleInsertFromFileAction(const KURL& url)
+{
+ if (isReadOnly())
+ return;
+
+ QString fileName( url.isLocalFile() ? url.path() : url.prettyURL() );
+
+ //! @todo download the file if remote, then set fileName properly
+ QFile f(fileName);
+ if (!f.open(IO_ReadOnly)) {
+ //! @todo err msg
+ return;
+ }
+ QByteArray ba = f.readAll();
+ if (f.status()!=IO_Ok) {
+ //! @todo err msg
+ f.close();
+ return;
+ }
+ f.close();
+// m_valueMimeType = KImageIO::mimeType( fileName );
+ setValueInternal( ba, true );
+ signalEditRequested();
+ //emit acceptRequested();
+}
+
+void KexiBlobTableEdit::handleAboutToSaveAsAction(QString& origFilename, QString& fileExtension, bool& dataIsEmpty)
+{
+ Q_UNUSED(origFilename);
+ Q_UNUSED(fileExtension);
+ dataIsEmpty = valueIsEmpty();
+//! @todo no fname stored for now
+}
+
+void KexiBlobTableEdit::handleSaveAsAction(const QString& fileName)
+{
+ QFile f(fileName);
+ if (!f.open(IO_WriteOnly)) {
+ //! @todo err msg
+ return;
+ }
+ f.writeBlock( d->value );
+ if (f.status()!=IO_Ok) {
+ //! @todo err msg
+ f.close();
+ return;
+ }
+ f.close();
+}
+
+void KexiBlobTableEdit::handleCutAction()
+{
+ if (isReadOnly())
+ return;
+ handleCopyAction();
+ clear();
+}
+
+void KexiBlobTableEdit::handleCopyAction()
+{
+ executeCopyAction(d->value);
+}
+
+void KexiBlobTableEdit::executeCopyAction(const QByteArray& data)
+{
+ QPixmap pixmap;
+ if (!pixmap.loadFromData(data))
+ return;
+ qApp->clipboard()->setPixmap(pixmap, QClipboard::Clipboard);
+}
+
+void KexiBlobTableEdit::handlePasteAction()
+{
+ if (isReadOnly())
+ return;
+ QPixmap pm( qApp->clipboard()->pixmap(QClipboard::Clipboard) );
+ QByteArray ba;
+ QBuffer buffer( ba );
+ buffer.open( IO_WriteOnly );
+ if (pm.save( &buffer, "PNG" )) {// write pixmap into ba in PNG format
+ setValueInternal( ba, true );
+ }
+ else {
+ setValueInternal( QByteArray(), true );
+ }
+ signalEditRequested();
+ //emit acceptRequested();
+ repaintRelatedCell();
+}
+
+void KexiBlobTableEdit::clear()
+{
+ setValueInternal( QByteArray(), true );
+ signalEditRequested();
+ //emit acceptRequested();
+ repaintRelatedCell();
+}
+
+void KexiBlobTableEdit::handleShowPropertiesAction()
+{
+ //! @todo
+}
+
+void KexiBlobTableEdit::showFocus( const QRect& r, bool readOnly )
+{
+ d->readOnly = readOnly; //cache for slotUpdateActionsAvailabilityRequested()
+// d->button->move( pos().x()+ width(), pos().y() );
+ updateFocus( r );
+// d->button->setEnabled(!readOnly);
+ if (d->readOnly)
+ d->button->hide();
+ else
+ d->button->show();
+}
+
+void KexiBlobTableEdit::resize(int w, int h)
+{
+ d->totalSize = QSize(w,h);
+ const int addWidth = d->readOnly ? 0 : d->button->width();
+ QWidget::resize(w - addWidth, h);
+ if (!d->readOnly)
+ d->button->resize( h, h );
+ m_rightMarginWhenFocused = m_rightMargin + addWidth;
+ QRect r( pos().x(), pos().y(), w+1, h+1 );
+ r.moveBy(m_scrollView->contentsX(),m_scrollView->contentsY());
+ updateFocus( r );
+//todo if (d->popup) {
+//todo d->popup->updateSize();
+//todo }
+}
+
+void KexiBlobTableEdit::updateFocus( const QRect& r )
+{
+ if (!d->readOnly) {
+ if (d->button->width() > r.width())
+ moveChild(d->button, r.right() + 1, r.top());
+ else
+ moveChild(d->button, r.right() - d->button->width(), r.top() );
+ }
+}
+
+void KexiBlobTableEdit::hideFocus()
+{
+ d->button->hide();
+}
+
+QSize KexiBlobTableEdit::totalSize() const
+{
+ return d->totalSize;
+}
+
+void KexiBlobTableEdit::slotUpdateActionsAvailabilityRequested(bool& valueIsNull, bool& valueIsReadOnly)
+{
+ emit editRequested();
+ valueIsNull = this->valueIsNull();
+ valueIsReadOnly = d->readOnly || isReadOnly();
+}
+
+void KexiBlobTableEdit::signalEditRequested()
+{
+ d->setValueInternalEnabled = false;
+ emit editRequested();
+ d->setValueInternalEnabled = true;
+}
+
+bool KexiBlobTableEdit::handleKeyPress( QKeyEvent* ke, bool editorActive )
+{
+ Q_UNUSED(editorActive);
+
+ const int k = ke->key();
+ KKey kkey(ke);
+ if (!d->readOnly) {
+ if ((ke->state()==Qt::NoButton && k==Qt::Key_F4)
+ || (ke->state()==Qt::AltButton && k==Qt::Key_Down)) {
+ d->button->animateClick();
+ QMouseEvent me( QEvent::MouseButtonPress, QPoint(2,2), Qt::LeftButton, Qt::NoButton );
+ QApplication::sendEvent( d->button, &me );
+ }
+ else if ((ke->state()==NoButton && (k==Qt::Key_F2 || k==Qt::Key_Space || k==Qt::Key_Enter || k==Qt::Key_Return))) {
+ d->popup->insertFromFile();
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+ return true;
+}
+
+bool KexiBlobTableEdit::handleDoubleClick()
+{
+ d->popup->insertFromFile();
+ return true;
+}
+
+void KexiBlobTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
+{
+ Q_UNUSED(visibleValue);
+ executeCopyAction(value.toByteArray());
+}
+
+void KexiBlobTableEdit::handleAction(const QString& actionName)
+{
+ if (actionName=="edit_paste") {
+ d->popup->paste();
+ }
+ else if (actionName=="edit_cut") {
+ emit editRequested();
+ d->popup->cut();
+ }
+}
+
+bool KexiBlobTableEdit::eventFilter( QObject *o, QEvent *e )
+{
+ if (o == d->popup && e->type()==QEvent::KeyPress) {
+ QKeyEvent* ke = static_cast<QKeyEvent*>(e);
+ const int state = ke->state();
+ const int k = ke->key();
+ if ( (state==Qt::NoButton && (k==Qt::Key_Tab || k==Qt::Key_Left || k==Qt::Key_Right))
+ || (state==Qt::ShiftButton && k==Qt::Key_Backtab)
+ )
+ {
+ d->popup->hide();
+ QApplication::sendEvent( this, ke ); //re-send to move cursor
+ return true;
+ }
+ }
+ return false;
+}
+
+KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiBlobEditorFactoryItem, KexiBlobTableEdit)
+
+//=======================
+// KexiKIconTableEdit class is temporarily here:
+
+//! @internal
+class KexiKIconTableEdit::Private
+{
+public:
+ Private()
+ : pixmapCache(17, 17, false)
+ {
+ }
+ //! We've no editor widget that would store current value, so we do this here
+ QVariant currentValue;
+
+ QCache<QPixmap> pixmapCache;
+};
+
+KexiKIconTableEdit::KexiKIconTableEdit(KexiTableViewColumn &column, QWidget *parent)
+ : KexiTableEdit(column, parent)
+ , d( new Private() )
+{
+ setName("KexiKIconTableEdit");
+ init();
+}
+
+KexiKIconTableEdit::~KexiKIconTableEdit()
+{
+ delete d;
+}
+
+void KexiKIconTableEdit::init()
+{
+ m_hasFocusableWidget = false;
+ d->pixmapCache.setAutoDelete(true);
+}
+
+void KexiKIconTableEdit::setValueInternal(const QVariant& /*add*/, bool /*removeOld*/)
+{
+ d->currentValue = m_origValue;
+}
+
+bool KexiKIconTableEdit::valueIsNull()
+{
+ return d->currentValue.isNull();
+}
+
+bool KexiKIconTableEdit::valueIsEmpty()
+{
+ return d->currentValue.isNull();
+}
+
+QVariant KexiKIconTableEdit::value()
+{
+ return d->currentValue;
+}
+
+void KexiKIconTableEdit::clear()
+{
+ d->currentValue = QVariant();
+}
+
+bool KexiKIconTableEdit::cursorAtStart()
+{
+ return true;
+}
+
+bool KexiKIconTableEdit::cursorAtEnd()
+{
+ return true;
+}
+
+void KexiKIconTableEdit::setupContents( QPainter *p, bool /*focused*/, const QVariant& val,
+ QString &/*txt*/, int &/*align*/, int &/*x*/, int &y_offset, int &w, int &h )
+{
+ Q_UNUSED( y_offset );
+
+#if 0
+#ifdef Q_WS_WIN
+ y_offset = -1;
+#else
+ y_offset = 0;
+#endif
+ int s = QMAX(h - 5, 12);
+ s = QMIN( h-3, s );
+ s = QMIN( w-3, s );//avoid too large box
+ QRect r( QMAX( w/2 - s/2, 0 ) , h/2 - s/2 /*- 1*/, s, s);
+ p->setPen(QPen(colorGroup().text(), 1));
+ p->drawRect(r);
+ if (val.asBool()) {
+ p->drawLine(r.x(), r.y(), r.right(), r.bottom());
+ p->drawLine(r.x(), r.bottom(), r.right(), r.y());
+ }
+#endif
+
+ QString key = val.toString();
+ QPixmap *pix = 0;
+ if (!key.isEmpty() && !(pix = d->pixmapCache[ key ])) {
+ //cache pixmap
+ QPixmap pm = KGlobal::iconLoader()->loadIcon( key, KIcon::Small,
+ 0, KIcon::DefaultState, 0L, true/*canReturnNull*/ );
+ if (!pm.isNull()) {
+ pix = new QPixmap(pm);
+ d->pixmapCache.insert(key, pix);
+ }
+ }
+
+ if (p && pix) {
+ p->drawPixmap( (w-pix->width())/2, (h-pix->height())/2, *pix );
+ }
+}
+
+void KexiKIconTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
+{
+ Q_UNUSED(value);
+ Q_UNUSED(visibleValue);
+}
+
+KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiKIconTableEditorFactoryItem, KexiKIconTableEdit)
+
+#include "kexiblobtableedit.moc"
diff --git a/kexi/widget/tableview/kexiblobtableedit.h b/kexi/widget/tableview/kexiblobtableedit.h
new file mode 100644
index 00000000..a44559be
--- /dev/null
+++ b/kexi/widget/tableview/kexiblobtableedit.h
@@ -0,0 +1,170 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2004, 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 _KEXIBLOBTABLEEDIT_H_
+#define _KEXIBLOBTABLEEDIT_H_
+
+#include <qcstring.h>
+#include <qcache.h>
+
+#include <kurl.h>
+
+#include "kexitableedit.h"
+#include "kexicelleditorfactory.h"
+
+class KTempFile;
+class KProcess;
+class QTextEdit;
+
+class KexiBlobTableEdit : public KexiTableEdit
+{
+ Q_OBJECT
+ public:
+ KexiBlobTableEdit(KexiTableViewColumn &column, QWidget *parent=0);
+ virtual ~KexiBlobTableEdit();
+
+ bool valueIsNull();
+ bool valueIsEmpty();
+
+ virtual QVariant value();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+
+ /*! Reimplemented: resizes a view(). */
+ virtual void resize(int w, int h);
+
+ virtual void showFocus( const QRect& r, bool readOnly );
+
+ virtual void hideFocus();
+
+ /*! \return total size of this editor, including popup button. */
+ virtual QSize totalSize() const;
+
+ virtual void paintFocusBorders( QPainter *p, QVariant &, int x, int y, int w, int h );
+
+ /*! Reimplemented to handle the key events. */
+ virtual bool handleKeyPress( QKeyEvent* ke, bool editorActive );
+
+ /*! Handles double click request coming from the table view.
+ \return true if it has been consumed.
+ Reimplemented in KexiBlobTableEdit (to execute "insert file" action. */
+ virtual bool handleDoubleClick();
+
+ /*! Handles action having standard name \a actionName.
+ Action could be: "edit_cut", "edit_paste", etc. */
+ virtual void handleAction(const QString& actionName);
+
+ /*! Handles copy action for value. The \a value is copied to clipboard in format appropriate
+ for the editor's impementation, e.g. for image cell it can be a pixmap.
+ \a visibleValue is unused here. Reimplemented after KexiTableEdit. */
+ virtual void handleCopyAction(const QVariant& value, const QVariant& visibleValue);
+
+ virtual void setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h );
+
+ protected slots:
+ void slotUpdateActionsAvailabilityRequested(bool& valueIsNull, bool& valueIsReadOnly);
+
+ void handleInsertFromFileAction(const KURL& url);
+ void handleAboutToSaveAsAction(QString& origFilename, QString& fileExtension, bool& dataIsEmpty);
+ void handleSaveAsAction(const QString& fileName);
+ void handleCutAction();
+ void handleCopyAction();
+ void handlePasteAction();
+ virtual void clear();
+ void handleShowPropertiesAction();
+
+ protected:
+ //! initializes this editor with \a add value
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ //todo QString openWithDlg(const QString& file);
+ //todo void execute(const QString& app, const QString& file);
+
+ //! @internal
+ void updateFocus( const QRect& r );
+
+ void signalEditRequested();
+
+ //! @internal
+ void executeCopyAction(const QByteArray& data);
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ class Private;
+ Private *d;
+//todo KTempFile* m_tempFile;
+//todo KProcess* m_proc;
+//todo QTextEdit *m_content;
+};
+
+KEXI_DECLARE_CELLEDITOR_FACTORY_ITEM(KexiBlobEditorFactoryItem)
+
+
+//=======================
+//This class is temporarily here:
+
+/*! @short Cell editor for displaying kde icon (using icon name provided as string).
+ Read only.
+*/
+class KexiKIconTableEdit : public KexiTableEdit
+{
+ public:
+ KexiKIconTableEdit(KexiTableViewColumn &column, QWidget *parent=0);
+
+ virtual ~KexiKIconTableEdit();
+
+ //! \return true if editor's value is null (not empty)
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not null).
+ //! Only few field types can accept "EMPTY" property
+ //! (check this with KexiDB::Field::hasEmptyProperty()),
+ virtual bool valueIsEmpty();
+
+ virtual QVariant value();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+
+ virtual void clear();
+
+ virtual void setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h );
+
+ /*! Handles copy action for value. Does nothing.
+ \a visibleValue is unused here. Reimplemented after KexiTableEdit. */
+ virtual void handleCopyAction(const QVariant& value, const QVariant& visibleValue);
+
+ protected:
+ //! initializes this editor with \a add value
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ void showHintButton();
+ void init();
+
+ class Private;
+ Private *d;
+};
+
+KEXI_DECLARE_CELLEDITOR_FACTORY_ITEM(KexiKIconTableEditorFactoryItem)
+
+#endif
diff --git a/kexi/widget/tableview/kexibooltableedit.cpp b/kexi/widget/tableview/kexibooltableedit.cpp
new file mode 100644
index 00000000..7b7bc0ac
--- /dev/null
+++ b/kexi/widget/tableview/kexibooltableedit.cpp
@@ -0,0 +1,180 @@
+/* This file is part of the KDE project
+ Copyright (C) 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 "kexibooltableedit.h"
+
+#include <kexidb/field.h>
+
+#include <qpainter.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+
+
+KexiBoolTableEdit::KexiBoolTableEdit(KexiTableViewColumn &column, QWidget *parent)
+ : KexiTableEdit(column, parent)
+{
+ setName("KexiBoolTableEdit");
+ kdDebug() << "KexiBoolTableEdit: m_origValue.typeName()==" << m_origValue.typeName() << endl;
+ kdDebug() << "KexiBoolTableEdit: type== " << field()->typeName() << endl;
+ m_hasFocusableWidget = false;
+ m_acceptEditorAfterDeleteContents = true;
+ m_usesSelectedTextColor = false;
+}
+
+KexiBoolTableEdit::~KexiBoolTableEdit()
+{
+}
+
+void KexiBoolTableEdit::setValueInternal(const QVariant& /*add*/, bool /*removeOld*/)
+{
+ m_currentValue = m_origValue;
+ //nothing to do more...
+}
+
+bool KexiBoolTableEdit::valueIsNull()
+{
+ return m_currentValue.isNull();
+}
+
+bool KexiBoolTableEdit::valueIsEmpty()
+{
+ return m_currentValue.isNull();
+}
+
+QVariant KexiBoolTableEdit::value()
+{
+// ok = true;
+ return m_currentValue;
+}
+
+void KexiBoolTableEdit::clear()
+{
+ if (field()->isNotNull())
+ m_currentValue = QVariant(false, 0);
+ else
+ m_currentValue = QVariant();
+}
+
+bool KexiBoolTableEdit::cursorAtStart()
+{
+ return true;
+}
+
+bool KexiBoolTableEdit::cursorAtEnd()
+{
+ return true;
+}
+
+void KexiBoolTableEdit::setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h )
+{
+ Q_UNUSED(focused);
+ Q_UNUSED(txt);
+ Q_UNUSED(align);
+ Q_UNUSED(x);
+#ifdef Q_WS_WIN
+// x = 1;
+ y_offset = -1;
+#else
+// x = 1;
+ y_offset = 0;
+#endif
+ if (p) {
+ int s = QMAX(h - 5, 12);
+ s = QMIN( h-3, s );
+ s = QMIN( w-3, s );//avoid too large box
+ QRect r( QMAX( w/2 - s/2, 0 ) , h/2 - s/2 /*- 1*/, s, s);
+//already set ouotside: p->setPen(QPen(colorGroup().text(), 1));
+ p->drawRect(r);
+ if (val.isNull()) { // && !field()->isNotNull()) {
+ p->drawText( r, Qt::AlignCenter, "?" );
+ }
+ else if (val.toBool()) {
+ p->drawLine(r.x(), r.y(), r.right(), r.bottom());
+ p->drawLine(r.x(), r.bottom(), r.right(), r.y());
+ }
+ }
+}
+
+void KexiBoolTableEdit::clickedOnContents()
+{
+ if (field()->isNotNull())
+ m_currentValue = QVariant( !m_currentValue.toBool(), 0 );
+ else {
+ // null allowed: use the cycle: true -> false -> null
+ if (m_currentValue.isNull())
+ m_currentValue = QVariant( true, 1 );
+ else
+ m_currentValue = m_currentValue.toBool() ? QVariant( false, 1 ) : QVariant();
+ }
+}
+
+void KexiBoolTableEdit::handleAction(const QString& actionName)
+{
+ if (actionName=="edit_paste") {
+ emit editRequested();
+ bool ok;
+ const int value = qApp->clipboard()->text( QClipboard::Clipboard ).toInt(&ok);
+ if (ok) {
+ m_currentValue = (value==0) ? QVariant(false, 0) : QVariant(true, 1);
+ }
+ else {
+ m_currentValue = field()->isNotNull()
+ ? QVariant(0, false)/*0 instead of NULL - handle case when null is not allowed*/
+ : QVariant();
+ }
+ repaintRelatedCell();
+ }
+ else if (actionName=="edit_cut") {
+ emit editRequested();
+//! @todo handle defaultValue...
+ m_currentValue = field()->isNotNull()
+ ? QVariant(0, false)/*0 instead of NULL - handle case when null is not allowed*/
+ : QVariant();
+ handleCopyAction(m_origValue, QVariant());
+ repaintRelatedCell();
+ }
+}
+
+void KexiBoolTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
+{
+ Q_UNUSED(visibleValue);
+ if (value.type()==QVariant::Bool)
+ qApp->clipboard()->setText(value.toBool() ? "1" : "0");
+ else
+ qApp->clipboard()->setText(QString::null);
+}
+
+int KexiBoolTableEdit::widthForValue( QVariant &val, const QFontMetrics &fm )
+{
+ Q_UNUSED(fm);
+ return val.toPixmap().width();
+}
+
+//======================================================
+
+KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiBoolEditorFactoryItem, KexiBoolTableEdit)
+
+#include "kexibooltableedit.moc"
+
diff --git a/kexi/widget/tableview/kexibooltableedit.h b/kexi/widget/tableview/kexibooltableedit.h
new file mode 100644
index 00000000..3320a573
--- /dev/null
+++ b/kexi/widget/tableview/kexibooltableedit.h
@@ -0,0 +1,87 @@
+/* This file is part of the KDE project
+ Copyright (C) 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 KEXIBOOLTABLEEDIT_H
+#define KEXIBOOLTABLEEDIT_H
+
+#include <qvariant.h>
+
+#include "kexitableedit.h"
+#include "kexicelleditorfactory.h"
+
+/*! @short Cell editor for boolean type.
+*/
+class KexiBoolTableEdit : public KexiTableEdit
+{
+ Q_OBJECT
+
+ public:
+ KexiBoolTableEdit(KexiTableViewColumn &column, QWidget *parent=0);
+
+ virtual ~KexiBoolTableEdit();
+
+ //! \return true if editor's value is null (not empty)
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not null).
+ //! Only few field types can accept "EMPTY" property
+ //! (check this with KexiDB::Field::hasEmptyProperty()),
+ virtual bool valueIsEmpty();
+
+ virtual QVariant value();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+
+ virtual void clear();
+
+ virtual void setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h );
+
+ virtual void clickedOnContents();
+
+ /*! Handles action having standard name \a actionName.
+ Action could be: "edit_cut", "edit_paste", etc. */
+ virtual void handleAction(const QString& actionName);
+
+ /*! Handles copy action for value. Copies empty string for null, "1" for true, "0" for false.
+ \a visibleValue is unused here. Reimplemented after KexiTableEdit. */
+ virtual void handleCopyAction(const QVariant& value, const QVariant& visibleValue);
+
+ /*! \return width of \a value. Reimplemented after KexiTableEdit. */
+ virtual int widthForValue( QVariant &val, const QFontMetrics &fm );
+
+ protected slots:
+
+ protected:
+ //! initializes this editor with \a add value
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ void showHintButton();
+
+ //! We've no editor widget that would store current value, so we do this here
+ QVariant m_currentValue;
+
+ signals:
+ void hintClicked();
+};
+
+KEXI_DECLARE_CELLEDITOR_FACTORY_ITEM(KexiBoolEditorFactoryItem)
+
+#endif
diff --git a/kexi/widget/tableview/kexicelleditorfactory.cpp b/kexi/widget/tableview/kexicelleditorfactory.cpp
new file mode 100644
index 00000000..a20eac07
--- /dev/null
+++ b/kexi/widget/tableview/kexicelleditorfactory.cpp
@@ -0,0 +1,198 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-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 "kexicelleditorfactory.h"
+
+#include <qptrdict.h>
+#include <qintdict.h>
+#include <kstaticdeleter.h>
+
+#include <kexidb/indexschema.h>
+#include <kexidb/tableschema.h>
+#include "kexitableviewdata.h"
+#include "kexidatetableedit.h"
+#include "kexitimetableedit.h"
+#include "kexidatetimetableedit.h"
+#include "kexitableedit.h"
+#include "kexiinputtableedit.h"
+#include "kexicomboboxtableedit.h"
+#include "kexiblobtableedit.h"
+#include "kexibooltableedit.h"
+
+//============= KexiCellEditorFactoryItem ============
+
+KexiCellEditorFactoryItem::KexiCellEditorFactoryItem()
+{
+}
+
+KexiCellEditorFactoryItem::~KexiCellEditorFactoryItem()
+{
+}
+
+//============= KexiCellEditorFactoryPrivate ============
+
+//! @internal
+class KexiCellEditorFactoryPrivate
+{
+ public:
+ KexiCellEditorFactoryPrivate()
+ : items(101)
+ , items_by_type(101, false)
+ {
+ items.setAutoDelete( true );
+ items_by_type.setAutoDelete( false );
+ }
+ ~KexiCellEditorFactoryPrivate() {}
+
+ QString key(uint type, const QString& subType) const
+ {
+ QString key = QString::number(type);
+ if (!subType.isEmpty())
+ key += (QString(" ") + subType);
+ return key;
+ }
+
+ void registerItem( KexiCellEditorFactoryItem& item, uint type, const QString& subType = QString::null )
+ {
+ if (!items[ &item ])
+ items.insert( &item, &item );
+
+ items_by_type.insert( key(type, subType), &item );
+ }
+
+ KexiCellEditorFactoryItem *findItem(uint type, const QString& subType)
+ {
+ KexiCellEditorFactoryItem *item = items_by_type[ key(type, subType) ];
+ if (item)
+ return item;
+ item = items_by_type[ key(type, QString::null) ];
+ if (item)
+ return item;
+ return items_by_type[ key( KexiDB::Field::InvalidType, QString::null ) ];
+ }
+
+ QPtrDict<KexiCellEditorFactoryItem> items; //!< list of editor factory items (for later destroy)
+
+ QDict<KexiCellEditorFactoryItem> items_by_type; //!< editor factory items accessed by a key
+};
+
+static KStaticDeleter<KexiCellEditorFactoryPrivate> KexiCellEditorFactory_deleter;
+static KexiCellEditorFactoryPrivate *KexiCellEditorFactory_static = 0;
+
+//============= KexiCellEditorFactory ============
+
+KexiCellEditorFactory::KexiCellEditorFactory()
+{
+}
+
+KexiCellEditorFactory::~KexiCellEditorFactory()
+{
+}
+
+
+// Initializes standard editor cell editor factories
+void KexiCellEditorFactory::init()
+{
+ if (KexiCellEditorFactory_static)
+ return;
+ KexiCellEditorFactory_deleter.setObject(KexiCellEditorFactory_static, new KexiCellEditorFactoryPrivate());
+
+ KexiCellEditorFactory_static->registerItem( *new KexiBlobEditorFactoryItem(), KexiDB::Field::BLOB );
+ KexiCellEditorFactory_static->registerItem( *new KexiDateEditorFactoryItem(), KexiDB::Field::Date );
+ KexiCellEditorFactory_static->registerItem( *new KexiTimeEditorFactoryItem(), KexiDB::Field::Time );
+ KexiCellEditorFactory_static->registerItem( *new KexiDateTimeEditorFactoryItem(), KexiDB::Field::DateTime );
+ KexiCellEditorFactory_static->registerItem( *new KexiComboBoxEditorFactoryItem(), KexiDB::Field::Enum );
+ KexiCellEditorFactory_static->registerItem( *new KexiBoolEditorFactoryItem(), KexiDB::Field::Boolean );
+ KexiCellEditorFactory_static->registerItem( *new KexiKIconTableEditorFactoryItem(), KexiDB::Field::Text, "KIcon" );
+ //default type
+ KexiCellEditorFactory_static->registerItem( *new KexiInputEditorFactoryItem(), KexiDB::Field::InvalidType );
+}
+
+void KexiCellEditorFactory::registerItem( KexiCellEditorFactoryItem& item, uint type, const QString& subType )
+{
+ init();
+ KexiCellEditorFactory_static->registerItem( item, type, subType );
+}
+
+static bool hasEnumType( const KexiTableViewColumn &column )
+{
+ /*not db-aware case*/
+ if (column.relatedData())
+ return true;
+ /*db-aware case*/
+ if (!column.field() || !column.field()->table())
+ return false;
+ KexiDB::LookupFieldSchema *lookupFieldSchema = column.field()->table()->lookupFieldSchema( *column.field() );
+ if (!lookupFieldSchema)
+ return false;
+ if (lookupFieldSchema->rowSource().name().isEmpty())
+ return false;
+ return true;
+}
+
+KexiTableEdit* KexiCellEditorFactory::createEditor(KexiTableViewColumn &column, QWidget* parent)
+{
+ init();
+ KexiDB::Field *realField;
+ if (column.visibleLookupColumnInfo) {
+ realField = column.visibleLookupColumnInfo->field;
+ }
+ else {
+ realField = column.field();
+ }
+
+ KexiCellEditorFactoryItem *item = 0;
+
+ if (hasEnumType(column)) {
+ //--we need to create combo box because of relationship:
+ item = KexiCellEditorFactory::item( KexiDB::Field::Enum );
+ }
+ else {
+ item = KexiCellEditorFactory::item( realField->type(), realField->subType() );
+ }
+
+#if 0 //js: TODO LATER
+ //--check if we need to create combo box because of relationship:
+ //WARNING: it's assumed that indices are one-field long
+ KexiDB::TableSchema *table = f.table();
+ if (table) {
+ //find index that contain this field
+ KexiDB::IndexSchema::ListIterator it = table->indicesIterator();
+ for (;it.current();++it) {
+ KexiDB::IndexSchema *idx = it.current();
+ if (idx->fields()->findRef(&f)!=-1) {
+ //find details-side rel. for this index
+ KexiDB::Relationship *rel = idx->detailsRelationships()->first();
+ if (rel) {
+
+ }
+ }
+ }
+ }
+#endif
+
+ return item->createEditor(column, parent);
+}
+
+KexiCellEditorFactoryItem* KexiCellEditorFactory::item( uint type, const QString& subType )
+{
+ init();
+ return KexiCellEditorFactory_static->findItem(type, subType);
+}
+
diff --git a/kexi/widget/tableview/kexicelleditorfactory.h b/kexi/widget/tableview/kexicelleditorfactory.h
new file mode 100644
index 00000000..1b68cb8d
--- /dev/null
+++ b/kexi/widget/tableview/kexicelleditorfactory.h
@@ -0,0 +1,79 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-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 KEXICELLEDITORFACTORY_H
+#define KEXICELLEDITORFACTORY_H
+
+#include <qvariant.h>
+#include <qwidget.h>
+
+#include <kexidb/field.h>
+
+class KexiCellEditorFactoryItem;
+class KexiTableEdit;
+class KexiTableViewColumn;
+
+//! A singleton class providing access to cell editor factories
+class KEXIDATATABLE_EXPORT KexiCellEditorFactory
+{
+ public:
+ KexiCellEditorFactory();
+ virtual ~KexiCellEditorFactory();
+
+ /*! Registers factory item for \a type and (optional) \a subType.
+ \a subType is usually obtained (e.g. in KexiTableView) from KexiDB::Field::subType().
+ Passing KexiDB::Field::Invalid as type will set default item,
+ i.e. the one that will be used when no other item is defined for given data type.
+ You can register the same \a item many times for different types and subtypes.
+ Once registered, \a item object will be owned by the factory, so you shouldn't
+ care about deleting it. */
+ static void registerItem( KexiCellEditorFactoryItem& item, uint type,
+ const QString& subType = QString::null );
+
+ /*! \return item for \a type and (optional) \a subType.
+ If no item found, the one with empty subtype is tried.
+ If still no item found, the default is tried. Eventually, may return NULL. */
+ static KexiCellEditorFactoryItem* item( uint type, const QString& subType = QString::null );
+
+// static KexiTableEdit* createEditor(KexiDB::Field &f, QScrollView* parent = 0);
+ /*! Creates a new editor for \a column. If \a parent is of QScrollView, the new editor
+ will be created inside parent->viewport() instead. */
+ static KexiTableEdit* createEditor(KexiTableViewColumn &column, QWidget* parent = 0);
+
+ protected:
+ static void init();
+};
+
+//! A base class for implementing cell editor factories
+class KEXIDATATABLE_EXPORT KexiCellEditorFactoryItem
+{
+ public:
+ KexiCellEditorFactoryItem();
+ virtual ~KexiCellEditorFactoryItem();
+ QString className() { return m_className; }
+
+ protected:
+// virtual KexiTableEdit* createEditor(KexiDB::Field &f, QScrollView* parent = 0) = 0;
+ virtual KexiTableEdit* createEditor(KexiTableViewColumn &column, QWidget* parent = 0) = 0;
+
+ QString m_className;
+ friend class KexiCellEditorFactory;
+};
+
+#endif
diff --git a/kexi/widget/tableview/kexicomboboxbase.cpp b/kexi/widget/tableview/kexicomboboxbase.cpp
new file mode 100644
index 00000000..2d6d52ac
--- /dev/null
+++ b/kexi/widget/tableview/kexicomboboxbase.cpp
@@ -0,0 +1,597 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2003-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 <qlayout.h>
+#include <qstyle.h>
+#include <qwindowsstyle.h>
+#include <qpainter.h>
+
+#include "kexicomboboxbase.h"
+#include <widget/utils/kexicomboboxdropdownbutton.h>
+#include "kexicomboboxpopup.h"
+#include "kexitableview.h"
+#include "kexitableitem.h"
+#include "kexi.h"
+
+#include <klineedit.h>
+
+KexiComboBoxBase::KexiComboBoxBase()
+{
+ m_internalEditorValueChanged = false; //user has text or other value inside editor
+ m_slotInternalEditorValueChanged_enabled = true;
+ m_mouseBtnPressedWhenPopupVisible = false;
+ m_insideCreatePopup = false;
+ m_setValueOrTextInInternalEditor_enabled = true;
+ m_updatePopupSelectionOnShow = true;
+ m_moveCursorToEndInInternalEditor_enabled = true;
+ m_selectAllInInternalEditor_enabled = true;
+ m_setValueInInternalEditor_enabled = true;
+ m_setVisibleValueOnSetValueInternal = false;
+}
+
+KexiComboBoxBase::~KexiComboBoxBase()
+{
+}
+
+KexiDB::LookupFieldSchema *KexiComboBoxBase::lookupFieldSchema() const
+{
+ if (field() && field()->table()) {
+ KexiDB::LookupFieldSchema *lookupFieldSchema = field()->table()->lookupFieldSchema( *field() );
+ if (lookupFieldSchema && !lookupFieldSchema->rowSource().name().isEmpty())
+ return lookupFieldSchema;
+ }
+ return 0;
+}
+
+int KexiComboBoxBase::rowToHighlightForLookupTable() const
+{
+ if (!popup())
+ return -1;//err
+ KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
+ if (!lookupFieldSchema)
+ return -1;
+ if (lookupFieldSchema->boundColumn()==-1)
+ return -1; //err
+ bool ok;
+ const int rowUid = origValue().toInt();
+//! @todo for now we're assuming the id is INTEGER
+ KexiTableViewData *tvData = popup()->tableView()->data();
+ const int boundColumn = lookupFieldSchema->boundColumn();
+ KexiTableViewData::Iterator it(tvData->iterator());
+ int row=0;
+ for (;it.current();++it, row++)
+ {
+ if (it.current()->at(boundColumn).toInt(&ok) == rowUid && ok || !ok)
+ break;
+ }
+ if (!ok || !it.current()) //item not found: highlight 1st row, if available
+ return -1;
+ return row;
+}
+
+void KexiComboBoxBase::setValueInternal(const QVariant& add_, bool removeOld)
+{
+ Q_UNUSED(removeOld);
+ m_mouseBtnPressedWhenPopupVisible = false;
+ m_updatePopupSelectionOnShow = true;
+ QString add(add_.toString());
+ if (add.isEmpty()) {
+ KexiTableViewData *relData = column() ? column()->relatedData() : 0;
+ QVariant valueToSet;
+ bool hasValueToSet = true;
+ int rowToHighlight = -1;
+ KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
+ if (lookupFieldSchema) {
+ //use 'lookup field' model
+//! @todo support more RowSourceType's, not only table
+ if (lookupFieldSchema->boundColumn()==-1)
+//! @todo errmsg
+ return;
+ if (m_setVisibleValueOnSetValueInternal) {
+ //only for table views
+ if (!popup())
+ createPopup(false/*!show*/);
+ }
+ if (popup()) {
+ const int rowToHighlight = rowToHighlightForLookupTable();
+ popup()->tableView()->setHighlightedRow(rowToHighlight);
+
+ const int visibleColumn = lookupFieldSchema->visibleColumn( popup()->tableView()->data()->columnsCount() );
+ if (m_setVisibleValueOnSetValueInternal && -1!=visibleColumn) {
+ //only for table views
+ KexiTableItem *it = popup()->tableView()->highlightedItem();
+ if (it)
+ valueToSet = it->at( visibleColumn );
+ }
+ else {
+ hasValueToSet = false;
+ }
+ }
+ }
+ else if (relData) {
+ //use 'related table data' model
+ valueToSet = valueForString(origValue().toString(), &rowToHighlight, 0, 1);
+ }
+ else {
+ //use 'enum hints' model
+ const int row = origValue().toInt();
+ valueToSet = field()->enumHint(row).stripWhiteSpace();
+ }
+ if (hasValueToSet)
+ setValueOrTextInInternalEditor( valueToSet );
+ /*impl.*/moveCursorToEndInInternalEditor();
+ /*impl.*/selectAllInInternalEditor();
+
+ if (popup()) {
+ if (origValue().isNull()) {
+ popup()->tableView()->clearSelection();
+ popup()->tableView()->setHighlightedRow(0);
+ } else {
+ if (relData) {
+ if (rowToHighlight!=-1)
+ popup()->tableView()->setHighlightedRow(rowToHighlight);
+ }
+ else if (!lookupFieldSchema) {
+ //popup()->tableView()->selectRow(origValue().toInt());
+ popup()->tableView()->setHighlightedRow(origValue().toInt());
+ }
+ }
+ }
+ }
+ else {
+ //todo: autocompl.?
+ if (popup())
+ popup()->tableView()->clearSelection();
+ /*impl.*/setValueInInternalEditor(add); //not setLineEditText(), because 'add' is entered by user!
+ //setLineEditText( add );
+ /*impl.*/moveCursorToEndInInternalEditor();
+ }
+}
+
+KexiTableItem* KexiComboBoxBase::selectItemForEnteredValueInLookupTable(const QVariant& v)
+{
+ KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
+ if (!popup() || !lookupFieldSchema)
+ return 0; //safety
+//-not effective for large sets: please cache it!
+//.stripWhiteSpace() is not generic!
+
+ const bool valueIsText = v.type()==QVariant::String || v.type()==QVariant::CString; //most common case
+ const QString txt( valueIsText ? v.toString().stripWhiteSpace().lower() : QString::null );
+ KexiTableViewData *lookupData = popup()->tableView()->data();
+ const int visibleColumn = lookupFieldSchema->visibleColumn( lookupData->columnsCount() );
+ if (-1 == visibleColumn)
+ return 0;
+ KexiTableViewData::Iterator it(lookupData->iterator());
+ int row;
+ for (row = 0;it.current();++it, row++) {
+ if (valueIsText) {
+ if (it.current()->at(visibleColumn).toString().stripWhiteSpace().lower() == txt)
+ break;
+ }
+ else {
+ if (it.current()->at(visibleColumn) == v)
+ break;
+ }
+ }
+
+ m_setValueOrTextInInternalEditor_enabled = false; // <-- this is the entered value,
+ // so do not change the internal editor's contents
+ if (it.current())
+ popup()->tableView()->selectRow(row);
+ else
+ popup()->tableView()->clearSelection();
+
+ m_setValueOrTextInInternalEditor_enabled = true;
+
+ return it.current();
+}
+
+QString KexiComboBoxBase::valueForString(const QString& str, int* row,
+ uint lookInColumn, uint returnFromColumn, bool allowNulls)
+{
+ KexiTableViewData *relData = column() ? column()->relatedData() : 0;
+ if (!relData)
+ return QString::null; //safety
+ //use 'related table data' model
+ //-not effective for large sets: please cache it!
+ //.stripWhiteSpace() is not generic!
+
+ const QString txt = str.stripWhiteSpace().lower();
+ KexiTableViewData::Iterator it( relData->iterator() );
+ for (*row = 0;it.current();++it, (*row)++) {
+ if (it.current()->at(lookInColumn).toString().stripWhiteSpace().lower()==txt)
+ break;
+ }
+ if (it.current())
+ return it.current()->at(returnFromColumn).toString();
+
+ *row = -1;
+
+ if (column() && column()->relatedDataEditable())
+ return str; //new value entered and that's allowed
+
+ kexiwarn << "KexiComboBoxBase::valueForString(): no related row found, ID will be painted!" << endl;
+ if (allowNulls)
+ return QString::null;
+ return str; //for sanity but it's weird to show id to the user
+}
+
+QVariant KexiComboBoxBase::value()
+{
+ KexiTableViewData *relData = column() ? column()->relatedData() : 0;
+ KexiDB::LookupFieldSchema *lookupFieldSchema = 0;
+ if (relData) {
+ if (m_internalEditorValueChanged) {
+ //we've user-entered text: look for id
+//TODO: make error if matching text not found?
+ int rowToHighlight;
+ return valueForString(m_userEnteredValue.toString(), &rowToHighlight, 1, 0, true/*allowNulls*/);
+ }
+ else {
+ //use 'related table data' model
+ KexiTableItem *it = popup() ? popup()->tableView()->selectedItem() : 0;
+ return it ? it->at(0) : origValue();//QVariant();
+ }
+ }
+ else if ((lookupFieldSchema = this->lookupFieldSchema()))
+ {
+ if (lookupFieldSchema->boundColumn()==-1)
+ return origValue();
+ KexiTableItem *it = popup() ? popup()->tableView()->selectedItem() : 0;
+ if (/*!it &&*/ m_internalEditorValueChanged && !m_userEnteredValue.toString().isEmpty()) { //
+ //try to select a row using the user-entered text
+ if (!popup()) {
+ QVariant prevUserEnteredValue = m_userEnteredValue;
+ createPopup(false);
+ m_userEnteredValue = prevUserEnteredValue;
+ }
+ it = selectItemForEnteredValueInLookupTable( m_userEnteredValue );
+ }
+ return it ? it->at( lookupFieldSchema->boundColumn() ) : QVariant();
+ }
+ else if (popup()) {
+ //use 'enum hints' model
+ const int row = popup()->tableView()->currentRow();
+ if (row>=0)
+ return QVariant( row );
+ }
+
+ if (valueFromInternalEditor().toString().isEmpty())
+ return QVariant();
+/*! \todo don't return just 1st row, but use autocompletion feature
+ and: show message box if entered text does not match! */
+ return origValue(); //unchanged
+}
+
+QVariant KexiComboBoxBase::visibleValueForLookupField()
+{
+ KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
+ if (!popup() || !lookupFieldSchema)
+ return QVariant();
+ const int visibleColumn = lookupFieldSchema->visibleColumn( popup()->tableView()->data()->columnsCount() );
+ if (-1 == visibleColumn)
+ return QVariant();
+ KexiTableItem *it = popup()->tableView()->selectedItem();
+ return it ? it->at( QMIN( (uint)visibleColumn, it->count()-1)/*sanity*/ ) : QVariant();
+}
+
+QVariant KexiComboBoxBase::visibleValue()
+{
+ return m_visibleValue;
+}
+
+void KexiComboBoxBase::clear()
+{
+ if (popup())
+ popup()->hide();
+ slotInternalEditorValueChanged(QVariant());
+}
+
+tristate KexiComboBoxBase::valueChangedInternal()
+{
+ //avoid comparing values:
+ KexiTableViewData *relData = column() ? column()->relatedData() : 0;
+ KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
+ if (relData || lookupFieldSchema) {
+ if (m_internalEditorValueChanged)
+ return true;
+
+ //use 'related table data' model
+ KexiTableItem *it = popup() ? popup()->tableView()->selectedItem() : 0;
+ if (!it)
+ return false;
+ }
+ else {
+ //use 'enum hints' model
+ const int row = popup() ? popup()->tableView()->currentRow() : -1;
+ if (row<0 && !m_internalEditorValueChanged/*true if text box is cleared*/)
+ return false;
+ }
+
+ return cancelled;
+}
+
+bool KexiComboBoxBase::valueIsNull()
+{
+// bool ok;
+ QVariant v( value() );
+ return v.isNull();
+// return !ok || v.isNull();
+}
+
+bool KexiComboBoxBase::valueIsEmpty()
+{
+ return valueIsNull();
+}
+
+void KexiComboBoxBase::showPopup()
+{
+ createPopup(true);
+}
+
+void KexiComboBoxBase::createPopup(bool show)
+{
+ if (!field())
+ return;
+ m_insideCreatePopup = true;
+ QWidget* thisWidget = dynamic_cast<QWidget*>(this);
+ QWidget *widgetToFocus = internalEditor() ? internalEditor() : thisWidget;
+ if (!popup()) {
+ setPopup( column() ? new KexiComboBoxPopup(thisWidget, *column())
+ : new KexiComboBoxPopup(thisWidget, *field()) );
+ QObject::connect(popup(), SIGNAL(rowAccepted(KexiTableItem*,int)),
+ thisWidget, SLOT(slotRowAccepted(KexiTableItem*,int)));
+ QObject::connect(popup()->tableView(), SIGNAL(itemSelected(KexiTableItem*)),
+ thisWidget, SLOT(slotItemSelected(KexiTableItem*)));
+
+ popup()->setFocusProxy( widgetToFocus );
+ popup()->tableView()->setFocusProxy( widgetToFocus );
+ popup()->installEventFilter(thisWidget);
+
+ if (origValue().isNull())
+ popup()->tableView()->clearSelection();
+ else {
+ popup()->tableView()->selectRow( 0 );
+ popup()->tableView()->setHighlightedRow( 0 );
+ }
+ }
+ if (show && internalEditor() && !internalEditor()->isVisible())
+ /*emit*/editRequested();
+
+ QPoint posMappedToGlobal = mapFromParentToGlobal(thisWidget->pos());
+ if (posMappedToGlobal != QPoint(-1,-1)) {
+//! todo alter the position to fit the popup within screen boundaries
+ popup()->move( posMappedToGlobal + QPoint(0, thisWidget->height()) );
+ //to avoid flickering: first resize to 0-height, then show and resize back to prev. height
+ const int w = popupWidthHint();
+ popup()->resize(w, 0);
+ if (show)
+ popup()->show();
+ popup()->updateSize(w);
+ if (m_updatePopupSelectionOnShow) {
+ int rowToHighlight = -1;
+ KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
+ KexiTableViewData *relData = column() ? column()->relatedData() : 0;
+ if (lookupFieldSchema) {
+ rowToHighlight = rowToHighlightForLookupTable();
+ }
+ else if (relData) {
+ (void)valueForString(origValue().toString(), &rowToHighlight, 0, 1);
+ }
+ else //enum hint
+ rowToHighlight = origValue().toInt();
+
+/*-->*/ m_moveCursorToEndInInternalEditor_enabled = show;
+ m_selectAllInInternalEditor_enabled = show;
+ m_setValueInInternalEditor_enabled = show;
+ if (rowToHighlight==-1) {
+ rowToHighlight = QMAX( popup()->tableView()->highlightedRow(), 0);
+ setValueInInternalEditor(QVariant());
+ }
+ popup()->tableView()->selectRow( rowToHighlight );
+ popup()->tableView()->setHighlightedRow( rowToHighlight );
+ if (rowToHighlight < popup()->tableView()->rowsPerPage())
+ popup()->tableView()->ensureCellVisible( 0, -1 );
+
+/*-->*/ m_moveCursorToEndInInternalEditor_enabled = true;
+ m_selectAllInInternalEditor_enabled = true;
+ m_setValueInInternalEditor_enabled = true;
+ }
+ }
+
+ if (show) {
+ moveCursorToEndInInternalEditor();
+ selectAllInInternalEditor();
+ widgetToFocus->setFocus();
+ }
+ m_insideCreatePopup = false;
+}
+
+void KexiComboBoxBase::hide()
+{
+ if (popup())
+ popup()->hide();
+}
+
+void KexiComboBoxBase::slotRowAccepted(KexiTableItem * item, int row)
+{
+ Q_UNUSED(row);
+ //update our value
+ //..nothing to do?
+ updateButton();
+ slotItemSelected(item);
+ /*emit*/acceptRequested();
+}
+
+void KexiComboBoxBase::acceptPopupSelection()
+{
+ if (!popup())
+ return;
+ KexiTableItem *item = popup()->tableView()->highlightedItem();
+ if (item) {
+ popup()->tableView()->selectRow( popup()->tableView()->highlightedRow() );
+ slotRowAccepted(item, -1);
+ }
+ popup()->hide();
+}
+
+void KexiComboBoxBase::slotItemSelected(KexiTableItem*)
+{
+ kexidbg << "KexiComboBoxBase::slotItemSelected(): m_visibleValue = " << m_visibleValue << endl;
+
+ QVariant valueToSet;
+ KexiTableViewData *relData = column() ? column()->relatedData() : 0;
+ KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
+
+ m_visibleValue = lookupFieldSchema ? visibleValueForLookupField() : QVariant();
+
+ if (relData) {
+ //use 'related table data' model
+ KexiTableItem *item = popup()->tableView()->selectedItem();
+ if (item)
+ valueToSet = item->at(1);
+ }
+ else if (lookupFieldSchema) {
+ KexiTableItem *item = popup()->tableView()->selectedItem();
+ const int visibleColumn = lookupFieldSchema->visibleColumn( popup()->tableView()->data()->columnsCount() );
+ if (item && visibleColumn!=-1 /* && (int)item->size() >= visibleColumn --already checked*/) {
+ valueToSet = item->at( QMIN( (uint)visibleColumn, item->count()-1)/*sanity*/ );
+ }
+ }
+ else {
+ //use 'enum hints' model
+ valueToSet = field()->enumHint( popup()->tableView()->currentRow() );
+ if (valueToSet.toString().isEmpty() && !m_insideCreatePopup) {
+ clear();
+ QWidget* thisWidget = dynamic_cast<QWidget*>(this);
+ thisWidget->parentWidget()->setFocus();
+ return;
+ }
+ }
+ setValueOrTextInInternalEditor( valueToSet );
+ if (m_setValueOrTextInInternalEditor_enabled) {
+ moveCursorToEndInInternalEditor();
+ selectAllInInternalEditor();
+ }
+ // a new (temp) popup table index is selected: do not update selection next time:
+ m_updatePopupSelectionOnShow = false;
+}
+
+void KexiComboBoxBase::slotInternalEditorValueChanged(const QVariant& v)
+{
+ if (!m_slotInternalEditorValueChanged_enabled)
+ return;
+ m_userEnteredValue = v;
+ m_internalEditorValueChanged = true;
+ if (v.toString().isEmpty()) {
+ if (popup()) {
+ popup()->tableView()->clearSelection();
+ }
+ return;
+ }
+}
+
+void KexiComboBoxBase::setValueOrTextInInternalEditor(const QVariant& value)
+{
+ if (!m_setValueOrTextInInternalEditor_enabled)
+ return;
+ setValueInInternalEditor( value );
+ //this text is not entered by hand:
+ m_userEnteredValue = QVariant();
+ m_internalEditorValueChanged = false;
+}
+
+bool KexiComboBoxBase::handleKeyPressForPopup( QKeyEvent *ke )
+{
+ const int k = ke->key();
+ int highlightedOrSelectedRow = popup() ? popup()->tableView()->highlightedRow() : -1;
+ if (popup() && highlightedOrSelectedRow < 0)
+ highlightedOrSelectedRow = popup()->tableView()->currentRow();
+
+ const bool enterPressed = k==Qt::Key_Enter || k==Qt::Key_Return;
+
+ // The editor may be active but the pull down menu not existant/visible,
+ // e.g. when the user has pressed a normal button to activate the editor
+ // Don't handle the event here in that case.
+ if (!popup() || (!enterPressed && !popup()->isVisible())) {
+ return false;
+ }
+
+ switch (k) {
+ case Qt::Key_Up:
+ popup()->tableView()->setHighlightedRow(
+ QMAX(highlightedOrSelectedRow-1, 0) );
+ updateTextForHighlightedRow();
+ return true;
+ case Qt::Key_Down:
+ popup()->tableView()->setHighlightedRow(
+ QMIN(highlightedOrSelectedRow+1, popup()->tableView()->rows()-1) );
+ updateTextForHighlightedRow();
+ return true;
+ case Qt::Key_PageUp:
+ popup()->tableView()->setHighlightedRow(
+ QMAX(highlightedOrSelectedRow-popup()->tableView()->rowsPerPage(), 0) );
+ updateTextForHighlightedRow();
+ return true;
+ case Qt::Key_PageDown:
+ popup()->tableView()->setHighlightedRow(
+ QMIN(highlightedOrSelectedRow+popup()->tableView()->rowsPerPage(),
+ popup()->tableView()->rows()-1) );
+ updateTextForHighlightedRow();
+ return true;
+ case Qt::Key_Home:
+ popup()->tableView()->setHighlightedRow( 0 );
+ updateTextForHighlightedRow();
+ return true;
+ case Qt::Key_End:
+ popup()->tableView()->setHighlightedRow( popup()->tableView()->rows()-1 );
+ updateTextForHighlightedRow();
+ return true;
+ case Qt::Key_Enter:
+ case Qt::Key_Return: //accept
+ //select row that is highlighted
+ if (popup()->tableView()->highlightedRow()>=0)
+ popup()->tableView()->selectRow( popup()->tableView()->highlightedRow() );
+ //do not return true: allow to process event
+ default: ;
+ }
+ return false;
+}
+
+void KexiComboBoxBase::updateTextForHighlightedRow()
+{
+ KexiTableItem *item = popup() ? popup()->tableView()->highlightedItem() : 0;
+ if (item)
+ slotItemSelected(item);
+}
+
+void KexiComboBoxBase::undoChanges()
+{
+ KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
+ if (lookupFieldSchema) {
+// kexidbg << "KexiComboBoxBase::undoChanges(): m_visibleValue BEFORE = " << m_visibleValue << endl;
+ if (popup())
+ popup()->tableView()->selectRow( popup()->tableView()->highlightedRow() );
+ m_visibleValue = visibleValueForLookupField();
+// kexidbg << "KexiComboBoxBase::undoChanges(): m_visibleValue AFTER = " << m_visibleValue << endl;
+ setValueOrTextInInternalEditor( m_visibleValue );
+ }
+}
diff --git a/kexi/widget/tableview/kexicomboboxbase.h b/kexi/widget/tableview/kexicomboboxbase.h
new file mode 100644
index 00000000..1433ab0f
--- /dev/null
+++ b/kexi/widget/tableview/kexicomboboxbase.h
@@ -0,0 +1,170 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2003-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 _KEXICOMBOBOXBASE_H_
+#define _KEXICOMBOBOXBASE_H_
+
+#include "kexidb/field.h"
+#include "kexiinputtableedit.h"
+#include <kexidb/lookupfieldschema.h>
+
+class KPushButton;
+class KLineEdit;
+class KexiComboBoxPopup;
+class KexiTableItem;
+class KexiTableViewColumn;
+
+/*! @short A base class for handling data-aware combo boxes.
+ This class is used by KexiComboBoxTableEdit and KexiDBComboBox.
+*/
+class KEXIDATATABLE_EXPORT KexiComboBoxBase
+{
+ public:
+ KexiComboBoxBase();
+ virtual ~KexiComboBoxBase();
+
+ //! \return column related to this combo; for KexiComboBoxTableEdit 0 is returned here
+ virtual KexiTableViewColumn *column() const = 0;
+
+ //! \return database field related to this combo
+ virtual KexiDB::Field *field() const = 0;
+
+ //! \return the original value
+ virtual QVariant origValue() const = 0;
+
+ //! Note: Generally in current implementation this is integer > 0; may be null if no value is set
+ virtual QVariant value();
+
+ virtual QVariant visibleValue();
+
+ //! Reimplement this and call this impl.: used to clear internal editor
+ virtual void clear();
+
+ virtual tristate valueChangedInternal();
+ virtual bool valueIsNull();
+ virtual bool valueIsEmpty();
+
+ public:
+ virtual void hide();
+
+ void createPopup(bool show);
+
+ void showPopup();
+
+ //! Call this from slot
+ virtual void slotRowAccepted(KexiTableItem *item, int row);
+
+ //! Call this from slot
+ virtual void slotItemSelected(KexiTableItem*);
+
+ //! Call this from slot
+ void slotInternalEditorValueChanged(const QVariant &v);
+
+ //! Implement this to return the internal editor
+ virtual QWidget *internalEditor() const = 0;
+
+ protected:
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ //! Used to select row item for an user-entered value \a v.
+ //! Only for "lookup table" mode.
+ KexiTableItem* selectItemForEnteredValueInLookupTable(const QVariant& v);
+
+ /*! \return value from \a returnFromColumn related to \a str value from column \a lookInColumn.
+ If \a allowNulls is true, NULL is returend if no matched column found, else:
+ \a str is returned.
+ Example: lookInColumn=0, returnFromColumn=1 --returns user-visible string
+ for column #1 for id-column #0 */
+ QString valueForString(const QString& str, int* row, uint lookInColumn,
+ uint returnFromColumn, bool allowNulls = false);
+
+ //! sets \a value for the line edit without setting a flag (m_userEnteredValue) that indicates that
+ //! the text has been entered by hand (by a user)
+ void setValueOrTextInInternalEditor(const QVariant& value); //QString& text);
+
+ //! \return lookup field schema for this combo box, if present and if is valid (i.e. has defined row source)
+ KexiDB::LookupFieldSchema* lookupFieldSchema() const;
+
+ int rowToHighlightForLookupTable() const;
+
+ //! Implement this to perform "move cursor to end" in the internal editor
+ virtual void moveCursorToEndInInternalEditor() = 0;
+
+ //! Implement this to perform "select all" in the internal editor
+ virtual void selectAllInInternalEditor() = 0;
+
+ //! Implement this to perform "set value" in the internal editor
+ virtual void setValueInInternalEditor(const QVariant& value) = 0;
+
+ //! Implement this to return value from the internal editor
+ virtual QVariant valueFromInternalEditor() = 0;
+
+ //! Implement this as signal
+ virtual void editRequested() = 0;
+
+ //! Implement this as signal
+ virtual void acceptRequested() = 0;
+
+ //! Implement this to return a position \a pos mapped from parent (e.g. viewport)
+ //! to global coordinates. QPoint(-1, -1) should be returned if this cannot be computed.
+ virtual QPoint mapFromParentToGlobal(const QPoint& pos) const = 0;
+
+ //! Implement this to return a hint for popup width.
+ virtual int popupWidthHint() const = 0;
+
+ //! Implement this to update button state. Table view just updates on/off state
+ //! for the button depending on visibility of the popup
+ virtual void updateButton() {}
+
+ virtual KexiComboBoxPopup *popup() const = 0;
+ virtual void setPopup(KexiComboBoxPopup *popup) = 0;
+
+ virtual QVariant visibleValueForLookupField();
+
+ void updateTextForHighlightedRow();
+
+ bool handleKeyPressForPopup( QKeyEvent *ke );
+
+ void acceptPopupSelection();
+
+ //! Used by KexiDBComboBox.
+ void undoChanges();
+
+ QVariant m_visibleValue;
+
+ QVariant m_userEnteredValue; //!< value (usually a text) entered by hand (by the user)
+
+ bool m_internalEditorValueChanged : 1; //!< true if user has text or other value inside editor
+ bool m_slotInternalEditorValueChanged_enabled : 1; //!< Used in slotInternalEditorValueChanged()
+ bool m_setValueOrTextInInternalEditor_enabled : 1; //!< Used in setValueOrTextInInternalEditor() and slotItemSelected()
+ bool m_mouseBtnPressedWhenPopupVisible : 1; //!< Used only by KexiComboBoxTableEdit
+ bool m_insideCreatePopup : 1; //!< true if we're inside createPopup(); used in slotItemSelected()
+ bool m_updatePopupSelectionOnShow : 1; //!< Set to false as soon as the item corresponding with the current
+ //!< value is selected in the popup table. This avoids selecting item
+ //!< for origValue() and thus loosing the recent choice.
+ bool m_moveCursorToEndInInternalEditor_enabled : 1;
+ bool m_selectAllInInternalEditor_enabled : 1;
+ bool m_setValueInInternalEditor_enabled : 1;
+ bool m_setVisibleValueOnSetValueInternal : 1; //!< Used in setValueInternal() to control whether
+ //!< we want to set visible value on setValueInternal()
+ //!< - true for table view's combo box
+};
+
+#endif
diff --git a/kexi/widget/tableview/kexicomboboxpopup.cpp b/kexi/widget/tableview/kexicomboboxpopup.cpp
new file mode 100644
index 00000000..5cd65d0d
--- /dev/null
+++ b/kexi/widget/tableview/kexicomboboxpopup.cpp
@@ -0,0 +1,373 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-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 "kexicomboboxpopup.h"
+
+#include "kexidatatableview.h"
+#include "kexitableview_p.h"
+#include "kexitableitem.h"
+#include "kexitableedit.h"
+
+#include <kexidb/lookupfieldschema.h>
+#include <kexidb/expression.h>
+#include <kexidb/parser/sqlparser.h>
+
+#include <kdebug.h>
+
+#include <qlayout.h>
+#include <qevent.h>
+
+/*! @internal
+ Helper for KexiComboBoxPopup. */
+class KexiComboBoxPopup_KexiTableView : public KexiDataTableView
+{
+ public:
+ KexiComboBoxPopup_KexiTableView(QWidget* parent=0)
+ : KexiDataTableView(parent, "KexiComboBoxPopup_tv")
+ {
+ init();
+ }
+ void init()
+ {
+ setReadOnly( true );
+ setLineWidth( 0 );
+ d->moveCursorOnMouseRelease = true;
+ KexiTableView::Appearance a(appearance());
+ a.navigatorEnabled = false;
+//! @todo add option for backgroundAltering??
+ a.backgroundAltering = false;
+ a.fullRowSelection = true;
+ a.rowHighlightingEnabled = true;
+ a.rowMouseOverHighlightingEnabled = true;
+ a.persistentSelections = false;
+ a.rowMouseOverHighlightingColor = colorGroup().highlight();
+ a.rowMouseOverHighlightingTextColor = colorGroup().highlightedText();
+ a.rowHighlightingTextColor = a.rowMouseOverHighlightingTextColor;
+ a.gridEnabled = false;
+ setAppearance(a);
+ setInsertingEnabled( false );
+ setSortingEnabled( false );
+ setVerticalHeaderVisible( false );
+ setHorizontalHeaderVisible( false );
+ setContextMenuEnabled( false );
+ setScrollbarToolTipsEnabled( false );
+ installEventFilter(this);
+ setBottomMarginInternal( - horizontalScrollBar()->sizeHint().height() );
+ }
+ virtual void setData( KexiTableViewData *data, bool owner = true )
+ { KexiTableView::setData( data, owner ); }
+ bool setData(KexiDB::Cursor *cursor)
+ { return KexiDataTableView::setData( cursor ); }
+};
+
+//========================================
+
+//! @internal
+class KexiComboBoxPopupPrivate
+{
+ public:
+ KexiComboBoxPopupPrivate()
+ : int_f(0)
+ , privateQuery(0)
+ {
+ max_rows = KexiComboBoxPopup::defaultMaxRows;
+ }
+ ~KexiComboBoxPopupPrivate() {
+ delete int_f;
+ delete privateQuery;
+ }
+
+ KexiComboBoxPopup_KexiTableView *tv;
+ KexiDB::Field *int_f; //TODO: remove this -temporary
+ KexiDB::QuerySchema* privateQuery;
+ int max_rows;
+};
+
+//========================================
+
+const int KexiComboBoxPopup::defaultMaxRows = 8;
+
+KexiComboBoxPopup::KexiComboBoxPopup(QWidget* parent, KexiTableViewColumn &column)
+ : QFrame( parent, "KexiComboBoxPopup", WType_Popup )
+{
+ init();
+ //setup tv data
+ setData(&column, 0);
+}
+
+KexiComboBoxPopup::KexiComboBoxPopup(QWidget* parent, KexiDB::Field &field)
+ : QFrame( parent, "KexiComboBoxPopup", WType_Popup )
+{
+ init();
+ //setup tv data
+ setData(0, &field);
+}
+
+KexiComboBoxPopup::~KexiComboBoxPopup()
+{
+ delete d;
+}
+
+void KexiComboBoxPopup::init()
+{
+ d = new KexiComboBoxPopupPrivate();
+ setPaletteBackgroundColor(palette().color(QPalette::Active,QColorGroup::Base));
+ setLineWidth( 1 );
+ setFrameStyle( Box | Plain );
+
+ d->tv = new KexiComboBoxPopup_KexiTableView(this);
+ installEventFilter(this);
+
+ connect(d->tv, SIGNAL(itemReturnPressed(KexiTableItem*,int,int)),
+ this, SLOT(slotTVItemAccepted(KexiTableItem*,int,int)));
+
+ connect(d->tv, SIGNAL(itemMouseReleased(KexiTableItem*,int,int)),
+ this, SLOT(slotTVItemAccepted(KexiTableItem*,int,int)));
+
+ connect(d->tv, SIGNAL(itemDblClicked(KexiTableItem*,int,int)),
+ this, SLOT(slotTVItemAccepted(KexiTableItem*,int,int)));
+}
+
+void KexiComboBoxPopup::setData(KexiTableViewColumn *column, KexiDB::Field *field)
+{
+ if (column && !field)
+ field = column->field();
+ if (!field) {
+ kexiwarn << "KexiComboBoxPopup::setData(): !field" << endl;
+ return;
+ }
+
+ // case 1: simple related data
+ if (column && column->relatedData()) {
+ d->tv->setColumnStretchEnabled( true, -1 ); //only needed when using single column
+ setDataInternal( column->relatedData(), false /*!owner*/ );
+ return;
+ }
+ // case 2: lookup field
+ KexiDB::LookupFieldSchema *lookupFieldSchema = 0;
+ if (field->table())
+ lookupFieldSchema = field->table()->lookupFieldSchema( *field );
+ delete d->privateQuery;
+ d->privateQuery = 0;
+ if (lookupFieldSchema) {
+ const QValueList<uint> visibleColumns( lookupFieldSchema->visibleColumns() );
+ const bool multipleLookupColumnJoined = visibleColumns.count() > 1;
+//! @todo support more RowSourceType's, not only table and query
+ KexiDB::Cursor *cursor = 0;
+ switch (lookupFieldSchema->rowSource().type()) {
+ case KexiDB::LookupFieldSchema::RowSource::Table: {
+ KexiDB::TableSchema *lookupTable
+ = field->table()->connection()->tableSchema( lookupFieldSchema->rowSource().name() );
+ if (!lookupTable)
+//! @todo errmsg
+ return;
+ if (multipleLookupColumnJoined) {
+ kdDebug() << "--- Orig query: " << endl;
+ lookupTable->query()->debug();
+ d->privateQuery = new KexiDB::QuerySchema(*lookupTable->query());
+ }
+ else {
+ cursor = field->table()->connection()->prepareQuery( *lookupTable );
+ }
+ break;
+ }
+ case KexiDB::LookupFieldSchema::RowSource::Query: {
+ KexiDB::QuerySchema *lookupQuery
+ = field->table()->connection()->querySchema( lookupFieldSchema->rowSource().name() );
+ if (!lookupQuery)
+//! @todo errmsg
+ return;
+ if (multipleLookupColumnJoined) {
+ kdDebug() << "--- Orig query: " << endl;
+ lookupQuery->debug();
+ d->privateQuery = new KexiDB::QuerySchema(*lookupQuery);
+ }
+ else {
+ cursor = field->table()->connection()->prepareQuery( *lookupQuery );
+ }
+ break;
+ }
+ default:;
+ }
+ if (d->privateQuery) {
+ // append column computed using multiple columns
+ const KexiDB::QueryColumnInfo::Vector fieldsExpanded( d->privateQuery->fieldsExpanded() );
+ uint fieldsExpandedSize( fieldsExpanded.size() );
+ KexiDB::BaseExpr *expr = 0;
+ int count = visibleColumns.count();
+ for (QValueList<uint>::ConstIterator it( visibleColumns.at(count-1) ); count>0; count--, --it) {
+ KexiDB::QueryColumnInfo *ci = ((*it) < fieldsExpandedSize) ? fieldsExpanded.at( *it ) : 0;
+ if (!ci) {
+ kdWarning() << "KexiComboBoxPopup::setData(): " << *it << " >= fieldsExpandedSize" << endl;
+ continue;
+ }
+ KexiDB::VariableExpr *fieldExpr
+ = new KexiDB::VariableExpr( ci->field->table()->name()+"."+ci->field->name() );
+ fieldExpr->field = ci->field;
+ fieldExpr->tablePositionForField = d->privateQuery->tableBoundToColumn( *it );
+ if (expr) {
+//! @todo " " separator hardcoded...
+//! @todo use SQL sub-parser here...
+ KexiDB::ConstExpr *constExpr = new KexiDB::ConstExpr(CHARACTER_STRING_LITERAL, " ");
+ expr = new KexiDB::BinaryExpr(KexiDBExpr_Arithm, constExpr, CONCATENATION, expr);
+ expr = new KexiDB::BinaryExpr(KexiDBExpr_Arithm, fieldExpr, CONCATENATION, expr);
+ }
+ else
+ expr = fieldExpr;
+ }
+ expr->debug();
+ kdDebug() << expr->toString() << endl;
+
+ KexiDB::Field *f = new KexiDB::Field();
+ f->setExpression( expr );
+ d->privateQuery->addField( f );
+#if 0 //does not work yet
+// <remove later>
+//! @todo temp: improved display by hiding all columns except the computed one
+ const int numColumntoHide = d->privateQuery->fieldsExpanded().count() - 1;
+ for (int i=0; i < numColumntoHide; i++)
+ d->privateQuery->setColumnVisible(i, false);
+// </remove later>
+#endif
+//todo...
+ kdDebug() << "--- Private query: " << endl;
+ d->privateQuery->debug();
+ cursor = field->table()->connection()->prepareQuery( *d->privateQuery );
+ }
+ if (!cursor)
+//! @todo errmsg
+ return;
+
+ if (d->tv->data())
+ d->tv->data()->disconnect( this );
+ d->tv->setData( cursor );
+
+ connect( d->tv, SIGNAL(dataRefreshed()), this, SLOT(slotDataReloadRequested()));
+ updateSize();
+ return;
+ }
+
+ kdWarning() << "KexiComboBoxPopup::setData(KexiTableViewColumn &): no column relatedData \n - moving to setData(KexiDB::Field &)" << endl;
+
+ // case 3: enum hints
+ d->tv->setColumnStretchEnabled( true, -1 ); //only needed when using single column
+
+//! @todo THIS IS PRIMITIVE: we'd need to employ KexiDB::Reference here!
+ d->int_f = new KexiDB::Field(field->name(), KexiDB::Field::Text);
+ KexiTableViewData *data = new KexiTableViewData();
+ data->addColumn( new KexiTableViewColumn( *d->int_f ) );
+ QValueVector<QString> hints = field->enumHints();
+ for(uint i=0; i < hints.size(); i++) {
+ KexiTableItem *item = data->createItem();//new KexiTableItem(1);
+ (*item)[0]=QVariant(hints[i]);
+ kdDebug() << "added: '" << hints[i] <<"'"<<endl;
+ data->append( item );
+ }
+ setDataInternal( data, true );
+}
+
+void KexiComboBoxPopup::setDataInternal( KexiTableViewData *data, bool owner )
+{
+ if (d->tv->data())
+ d->tv->data()->disconnect( this );
+ d->tv->setData( data, owner );
+ connect( d->tv, SIGNAL(dataRefreshed()), this, SLOT(slotDataReloadRequested()));
+
+ updateSize();
+}
+
+void KexiComboBoxPopup::updateSize(int minWidth)
+{
+ const int rows = QMIN( d->max_rows, d->tv->rows() );
+
+ d->tv->adjustColumnWidthToContents(-1);
+
+ KexiTableEdit *te = dynamic_cast<KexiTableEdit*>(parentWidget());
+ const int width = QMAX( d->tv->tableSize().width(),
+ (te ? te->totalSize().width() : (parentWidget()?parentWidget()->width():0/*sanity*/)) );
+ kexidbg << "KexiComboBoxPopup::updateSize(): size=" << size() << endl;
+ resize( QMAX(minWidth, width)/*+(d->tv->columns()>1?2:0)*/ /*(d->updateSizeCalled?0:1)*/, d->tv->rowHeight() * rows +2 );
+ kexidbg << "KexiComboBoxPopup::updateSize(): size after=" << size() << endl;
+
+ //stretch the last column
+ d->tv->setColumnStretchEnabled(true, d->tv->columns()-1);
+}
+
+KexiTableView* KexiComboBoxPopup::tableView()
+{
+ return d->tv;
+}
+
+void KexiComboBoxPopup::resize( int w, int h )
+{
+ d->tv->horizontalScrollBar()->hide();
+ d->tv->verticalScrollBar()->hide();
+ d->tv->move(1,1);
+ d->tv->resize( w-2, h-2 );
+ QFrame::resize(w,h);
+ update();
+ updateGeometry();
+}
+
+void KexiComboBoxPopup::setMaxRows(int r)
+{
+ d->max_rows = r;
+}
+
+int KexiComboBoxPopup::maxRows() const
+{
+ return d->max_rows;
+}
+
+void KexiComboBoxPopup::slotTVItemAccepted(KexiTableItem *item, int row, int)
+{
+ hide();
+ emit rowAccepted(item, row);
+}
+
+bool KexiComboBoxPopup::eventFilter( QObject *o, QEvent *e )
+{
+ if (o==this && e->type()==QEvent::Hide) {
+ emit hidden();
+ }
+ else if (e->type()==QEvent::MouseButtonPress) {
+ kdDebug() << "QEvent::MousePress" << endl;
+ }
+ else if (o==d->tv) {
+ if (e->type()==QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ const int k = ke->key();
+ if ((ke->state()==NoButton && (k==Key_Escape || k==Key_F4))
+ || (ke->state()==AltButton && k==Key_Up))
+ {
+ hide();
+ emit cancelled();
+ return true;
+ }
+ }
+ }
+ return QFrame::eventFilter( o, e );
+}
+
+void KexiComboBoxPopup::slotDataReloadRequested()
+{
+ updateSize();
+}
+
+#include "kexicomboboxpopup.moc"
diff --git a/kexi/widget/tableview/kexicomboboxpopup.h b/kexi/widget/tableview/kexicomboboxpopup.h
new file mode 100644
index 00000000..42a15404
--- /dev/null
+++ b/kexi/widget/tableview/kexicomboboxpopup.h
@@ -0,0 +1,92 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-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 KEXICOMBOBOXPOPUP_H
+#define KEXICOMBOBOXPOPUP_H
+
+#include <qframe.h>
+
+class KexiComboBoxPopupPrivate;
+class KexiTableView;
+class KexiTableViewData;
+class KexiTableViewColumn;
+class KexiTableItem;
+namespace KexiDB {
+ class Field;
+}
+
+//! Internal class for displaying popup table view
+class KexiComboBoxPopup : public QFrame
+{
+ Q_OBJECT
+ public:
+//js TODO: more ctors!
+ /*! Constructor for creating a popup using definition from \a column.
+ If the column is lookup column, it's definition is used to display
+ one or more column within the popup. Otherwise column.field() is used
+ to display single-column data. */
+ KexiComboBoxPopup(QWidget* parent, KexiTableViewColumn &column);
+
+ /*! Alternative constructor supporting lookup fields and enum hints. */
+ KexiComboBoxPopup(QWidget* parent, KexiDB::Field &field);
+
+ virtual ~KexiComboBoxPopup();
+
+ KexiTableView* tableView();
+
+ /*! Sets maximum number of rows for this popup. */
+ void setMaxRows(int r);
+
+ /*! \return maximum number of rows for this popup. */
+ int maxRows() const;
+
+ /*! Default maximum number of rows for KexiComboBoxPopup objects. */
+ static const int defaultMaxRows;
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ signals:
+ void rowAccepted(KexiTableItem *item, int row);
+ void cancelled();
+ void hidden();
+
+ public slots:
+ virtual void resize( int w, int h );
+ void updateSize(int minWidth = 0);
+
+ protected slots:
+ void slotTVItemAccepted(KexiTableItem *item, int row, int col);
+ void slotDataReloadRequested();
+
+ protected:
+ void init();
+ //! The main function for setting data; data can be set either by passing \a column or \a field.
+ //! The second case is used for lookup
+ void setData(KexiTableViewColumn *column, KexiDB::Field *field);
+
+ //! used by setData()
+ void setDataInternal( KexiTableViewData *data, bool owner = true ); //!< helper
+
+ KexiComboBoxPopupPrivate *d;
+
+ friend class KexiComboBoxTableEdit;
+};
+
+#endif
+
diff --git a/kexi/widget/tableview/kexicomboboxtableedit.cpp b/kexi/widget/tableview/kexicomboboxtableedit.cpp
new file mode 100644
index 00000000..75815a8a
--- /dev/null
+++ b/kexi/widget/tableview/kexicomboboxtableedit.cpp
@@ -0,0 +1,446 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Peter Simonsson <psn@linux.se>
+ 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 <qlayout.h>
+#include <qstyle.h>
+#include <qwindowsstyle.h>
+#include <qpainter.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+
+#include "kexicomboboxtableedit.h"
+#include <widget/utils/kexicomboboxdropdownbutton.h>
+#include "kexicomboboxpopup.h"
+#include "kexitableview.h"
+#include "kexitableitem.h"
+#include "kexi.h"
+
+#include <klineedit.h>
+
+//! @internal
+class KexiComboBoxTableEdit::Private
+{
+public:
+ Private()
+ : popup(0)
+ , currentEditorWidth(0)
+ , visibleTableViewColumn(0)
+ , internalEditor(0)
+ {
+ }
+ ~Private()
+ {
+ delete internalEditor;
+ delete visibleTableViewColumn;
+ }
+
+ KPushButton *button;
+ KexiComboBoxPopup *popup;
+ int currentEditorWidth;
+ QSize totalSize;
+ KexiTableViewColumn* visibleTableViewColumn;
+ KexiTableEdit* internalEditor;
+};
+
+//======================================================
+
+KexiComboBoxTableEdit::KexiComboBoxTableEdit(KexiTableViewColumn &column, QWidget *parent)
+ : KexiInputTableEdit(column, parent)
+ , KexiComboBoxBase()
+ , d(new Private())
+{
+ setName("KexiComboBoxTableEdit");
+ m_setVisibleValueOnSetValueInternal = true;
+ d->button = new KexiComboBoxDropDownButton( parentWidget() /*usually a viewport*/ );
+ d->button->hide();
+ d->button->setFocusPolicy( NoFocus );
+ connect(d->button, SIGNAL(clicked()), this, SLOT(slotButtonClicked()));
+
+ connect(m_lineedit, SIGNAL(textChanged(const QString&)), this, SLOT(slotLineEditTextChanged(const QString&)));
+
+// m_lineedit = new KLineEdit(this, "lineedit");
+// m_lineedit->setFrame(false);
+// m_lineedit->setFrameStyle( QFrame::Plain | QFrame::Box );
+// m_lineedit->setLineWidth( 1 );
+// if (f.isNumericType()) {
+// m_lineedit->setAlignment(AlignRight);
+// }
+// setView( m_lineedit );
+
+// layout->addWidget(m_view);
+// m_combo->setEditable( true );
+// m_combo->clear();
+// m_combo->insertStringList(f.enumHints());
+// QStringList::ConstIterator it, end( f.enumHints().constEnd() );
+// for ( it = f.enumHints().constBegin(); it != end; ++it) {
+// if(!hints.at(i).isEmpty())
+// m_combo->insertItem(hints.at(i));
+// }
+
+//js: TODO
+//js static_cast<KComboBox*>(m_view)->insertStringList(list);
+//js static_cast<KComboBox*>(m_view)->setCurrentItem(static_cast<int>(t));
+}
+
+KexiComboBoxTableEdit::~KexiComboBoxTableEdit()
+{
+ delete d;
+}
+
+void KexiComboBoxTableEdit::createInternalEditor(KexiDB::QuerySchema& schema)
+{
+ if (!m_column->visibleLookupColumnInfo || d->visibleTableViewColumn/*sanity*/)
+ return;
+ const KexiDB::Field::Type t = m_column->visibleLookupColumnInfo->field->type();
+//! @todo subtype?
+ KexiCellEditorFactoryItem *item = KexiCellEditorFactory::item(t);
+ if (!item || item->className()=="KexiInputTableEdit")
+ return; //unsupported type or there is no need to use subeditor for KexiInputTableEdit
+ //special cases: BLOB, Bool datatypes
+//todo
+ //find real type to display
+ KexiDB::QueryColumnInfo *ci = m_column->visibleLookupColumnInfo;
+ KexiDB::QueryColumnInfo *visibleLookupColumnInfo = 0;
+ if (ci->indexForVisibleLookupValue() != -1) {
+ //Lookup field is defined
+ visibleLookupColumnInfo = schema.expandedOrInternalField( ci->indexForVisibleLookupValue() );
+ }
+ d->visibleTableViewColumn = new KexiTableViewColumn(schema, *ci, visibleLookupColumnInfo);
+//! todo set d->internalEditor visible and use it to enable data entering by hand
+ d->internalEditor = KexiCellEditorFactory::createEditor(*d->visibleTableViewColumn, 0);
+ m_lineedit->hide();
+}
+
+KexiComboBoxPopup *KexiComboBoxTableEdit::popup() const
+{
+ return d->popup;
+}
+
+void KexiComboBoxTableEdit::setPopup(KexiComboBoxPopup *popup)
+{
+ d->popup = popup;
+}
+
+void KexiComboBoxTableEdit::showFocus( const QRect& r, bool readOnly )
+{
+// d->button->move( pos().x()+ width(), pos().y() );
+ updateFocus( r );
+ d->button->setEnabled(!readOnly);
+ if (readOnly)
+ d->button->hide();
+ else
+ d->button->show();
+}
+
+void KexiComboBoxTableEdit::resize(int w, int h)
+{
+ d->totalSize = QSize(w,h);
+ if (!column()->isReadOnly()) {
+ d->button->resize( h, h );
+ QWidget::resize(w - d->button->width(), h);
+ }
+ m_rightMarginWhenFocused = m_rightMargin + (column()->isReadOnly() ? 0 : d->button->width());
+ QRect r( pos().x(), pos().y(), w+1, h+1 );
+ if (m_scrollView)
+ r.moveBy(m_scrollView->contentsX(), m_scrollView->contentsY());
+ updateFocus( r );
+ if (popup()) {
+ popup()->updateSize();
+ }
+}
+
+// internal
+void KexiComboBoxTableEdit::updateFocus( const QRect& r )
+{
+ if (!column()->isReadOnly()) {
+ if (d->button->width() > r.width())
+ moveChild(d->button, r.right() + 1, r.top());
+ else
+ moveChild(d->button, r.right() - d->button->width(), r.top() );
+ }
+}
+
+void KexiComboBoxTableEdit::hideFocus()
+{
+ d->button->hide();
+}
+
+QVariant KexiComboBoxTableEdit::visibleValue()
+{
+ return KexiComboBoxBase::visibleValue();
+/* KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
+ if (!popup() || !lookupFieldSchema)
+ return QVariant();
+ KexiTableItem *it = popup()->tableView()->selectedItem();
+ return it ? it->at( lookupFieldSchema->visibleColumn() ) : QVariant();*/
+}
+
+void KexiComboBoxTableEdit::clear()
+{
+ m_lineedit->clear();
+ KexiComboBoxBase::clear();
+}
+
+bool KexiComboBoxTableEdit::valueChanged()
+{
+ const tristate res = valueChangedInternal();
+ if (~res) //no result: just compare values
+ return KexiInputTableEdit::valueChanged();
+ return res == true;
+}
+
+void KexiComboBoxTableEdit::paintFocusBorders( QPainter *p, QVariant &, int x, int y, int w, int h )
+{
+// d->currentEditorWidth = w;
+ if (!column()->isReadOnly()) {
+ if (w > d->button->width())
+ w -= d->button->width();
+ }
+ p->drawRect(x, y, w, h);
+}
+
+void KexiComboBoxTableEdit::setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h )
+{
+ if (d->internalEditor) {
+ d->internalEditor->setupContents( p, focused, val, txt, align, x, y_offset, w, h );
+ }
+ else {
+ KexiInputTableEdit::setupContents( p, focused, val, txt, align, x, y_offset, w, h );
+ }
+ if (!column()->isReadOnly() && focused && (w > d->button->width()))
+ w -= (d->button->width() - x);
+ if (!val.isNull()) {
+ KexiTableViewData *relData = column()->relatedData();
+ KexiDB::LookupFieldSchema *lookupFieldSchema = 0;
+ if (relData) {
+ int rowToHighlight;
+ txt = valueForString(val.toString(), &rowToHighlight, 0, 1);
+ }
+ else if ((lookupFieldSchema = this->lookupFieldSchema())) {
+ /* handled at at KexiTableView level
+ if (popup()) {
+ KexiTableItem *it = popup()->tableView()->selectedItem();
+ if (it && lookupFieldSchema->visibleColumn()!=-1 && (int)it->size() >= lookupFieldSchema->visibleColumn())
+ txt = it->at( lookupFieldSchema->visibleColumn() ).toString();
+ }*/
+ }
+ else {
+ //use 'enum hints' model
+ txt = field()->enumHint( val.toInt() );
+ }
+ }
+}
+
+void KexiComboBoxTableEdit::slotButtonClicked()
+{
+ // this method is sometimes called by hand:
+ // do not allow to simulate clicks when the button is disabled
+ if (column()->isReadOnly() || !d->button->isEnabled())
+ return;
+
+ if (m_mouseBtnPressedWhenPopupVisible) {
+ m_mouseBtnPressedWhenPopupVisible = false;
+ d->button->setOn(false);
+ return;
+ }
+ kdDebug() << "KexiComboBoxTableEdit::slotButtonClicked()" << endl;
+ if (!popup() || !popup()->isVisible()) {
+ kdDebug() << "SHOW POPUP" << endl;
+ showPopup();
+ d->button->setOn(true);
+ }
+}
+
+void KexiComboBoxTableEdit::slotPopupHidden()
+{
+ d->button->setOn(false);
+// d->currentEditorWidth = 0;
+}
+
+void KexiComboBoxTableEdit::updateButton()
+{
+ d->button->setOn(popup()->isVisible());
+}
+
+void KexiComboBoxTableEdit::hide()
+{
+ KexiInputTableEdit::hide();
+ KexiComboBoxBase::hide();
+ d->button->setOn(false);
+}
+
+void KexiComboBoxTableEdit::show()
+{
+ KexiInputTableEdit::show();
+ if (!column()->isReadOnly()) {
+ d->button->show();
+ }
+}
+
+bool KexiComboBoxTableEdit::handleKeyPress( QKeyEvent *ke, bool editorActive )
+{
+ const int k = ke->key();
+ if ((ke->state()==NoButton && k==Qt::Key_F4)
+ || (ke->state()==AltButton && k==Qt::Key_Down))
+ {
+ //show popup
+ slotButtonClicked();
+ return true;
+ }
+ else if (editorActive) {
+ const bool enterPressed = k==Qt::Key_Enter || k==Qt::Key_Return;
+ if (enterPressed && m_internalEditorValueChanged) {
+ createPopup(false);
+ selectItemForEnteredValueInLookupTable( m_userEnteredValue );
+ return false;
+ }
+
+ return handleKeyPressForPopup( ke );
+ }
+
+ return false;
+}
+
+void KexiComboBoxTableEdit::slotLineEditTextChanged(const QString& s)
+{
+ slotInternalEditorValueChanged(s);
+}
+
+int KexiComboBoxTableEdit::widthForValue( QVariant &val, const QFontMetrics &fm )
+{
+ KexiTableViewData *relData = column() ? column()->relatedData() : 0;
+ if (lookupFieldSchema() || relData) {
+ // in 'lookupFieldSchema' or or 'related table data' model
+ // we're assuming val is already the text, not the index
+//! @todo ok?
+ return QMAX(KEXITV_MINIMUM_COLUMN_WIDTH, fm.width(val.toString()));
+ }
+ //use 'enum hints' model
+ QValueVector<QString> hints = field()->enumHints();
+ bool ok;
+ int idx = val.toInt(&ok);
+ if (!ok || idx < 0 || idx > int(hints.size()-1))
+ return KEXITV_MINIMUM_COLUMN_WIDTH;
+ QString txt = hints.at( idx, &ok );
+ if (!ok)
+ return KEXITV_MINIMUM_COLUMN_WIDTH;
+ return fm.width( txt );
+}
+
+bool KexiComboBoxTableEdit::eventFilter( QObject *o, QEvent *e )
+{
+ if (!column()->isReadOnly() && e->type()==QEvent::MouseButtonPress && m_scrollView) {
+ QPoint gp = static_cast<QMouseEvent*>(e)->globalPos()
+ + QPoint(m_scrollView->childX(d->button), m_scrollView->childY(d->button));
+ QRect r(d->button->mapToGlobal(d->button->geometry().topLeft()),
+ d->button->mapToGlobal(d->button->geometry().bottomRight()));
+ if (o==popup() && popup()->isVisible() && r.contains( gp )) {
+ m_mouseBtnPressedWhenPopupVisible = true;
+ }
+ }
+ return false;
+}
+
+QSize KexiComboBoxTableEdit::totalSize() const
+{
+ return d->totalSize;
+}
+
+QWidget *KexiComboBoxTableEdit::internalEditor() const
+{
+ return m_lineedit;
+}
+
+void KexiComboBoxTableEdit::moveCursorToEndInInternalEditor()
+{
+ moveCursorToEnd();
+}
+
+void KexiComboBoxTableEdit::selectAllInInternalEditor()
+{
+ selectAll();
+}
+
+void KexiComboBoxTableEdit::moveCursorToEnd()
+{
+ m_lineedit->end(false/*!mark*/);
+}
+
+void KexiComboBoxTableEdit::moveCursorToStart()
+{
+ m_lineedit->home(false/*!mark*/);
+}
+
+void KexiComboBoxTableEdit::selectAll()
+{
+ m_lineedit->selectAll();
+}
+
+void KexiComboBoxTableEdit::setValueInInternalEditor(const QVariant& value)
+{
+ m_lineedit->setText(value.toString());
+}
+
+QVariant KexiComboBoxTableEdit::valueFromInternalEditor()
+{
+ return m_lineedit->text();
+}
+
+QPoint KexiComboBoxTableEdit::mapFromParentToGlobal(const QPoint& pos) const
+{
+ KexiTableView *tv = dynamic_cast<KexiTableView*>(m_scrollView);
+ if (!tv)
+ return QPoint(-1,-1);
+ return tv->viewport()->mapToGlobal(pos);
+}
+
+int KexiComboBoxTableEdit::popupWidthHint() const
+{
+ return m_lineedit->width() + m_leftMargin + m_rightMarginWhenFocused; //QMAX(popup()->width(), d->currentEditorWidth);
+}
+
+void KexiComboBoxTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
+{
+ Q_UNUSED(value);
+//! @todo does not work with BLOBs!
+ qApp->clipboard()->setText( visibleValue.toString() );
+}
+
+void KexiComboBoxTableEdit::handleAction(const QString& actionName)
+{
+ const bool alreadyVisible = m_lineedit->isVisible();
+
+ if (actionName=="edit_paste") {
+ if (!alreadyVisible) { //paste as the entire text if the cell was not in edit mode
+ emit editRequested();
+ m_lineedit->clear();
+ }
+//! @todo does not work with BLOBs!
+ setValueInInternalEditor( qApp->clipboard()->text() );
+ }
+ else
+ KexiInputTableEdit::handleAction(actionName);
+}
+
+
+KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiComboBoxEditorFactoryItem, KexiComboBoxTableEdit)
+
+#include "kexicomboboxtableedit.moc"
diff --git a/kexi/widget/tableview/kexicomboboxtableedit.h b/kexi/widget/tableview/kexicomboboxtableedit.h
new file mode 100644
index 00000000..713fa55e
--- /dev/null
+++ b/kexi/widget/tableview/kexicomboboxtableedit.h
@@ -0,0 +1,166 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Peter Simonsson <psn@linux.se>
+ 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 _KEXICOMBOBOXTABLEEDIT_H_
+#define _KEXICOMBOBOXTABLEEDIT_H_
+
+#include "kexidb/field.h"
+#include "kexiinputtableedit.h"
+#include "kexicomboboxbase.h"
+#include <kexidb/lookupfieldschema.h>
+
+class KPushButton;
+class KLineEdit;
+class KexiComboBoxPopup;
+class KexiTableItem;
+class KexiTableViewColumn;
+
+/*! @short Drop-down cell editor.
+*/
+class KexiComboBoxTableEdit : public KexiInputTableEdit, public KexiComboBoxBase
+{
+ Q_OBJECT
+
+ public:
+ KexiComboBoxTableEdit(KexiTableViewColumn &column, QWidget *parent=0);
+ virtual ~KexiComboBoxTableEdit();
+
+ //! Implemented for KexiComboBoxBase
+ virtual KexiTableViewColumn *column() const { return m_column; }
+
+ //! Implemented for KexiComboBoxBase
+ virtual KexiDB::Field *field() const { return m_column->field(); }
+
+ //! Implemented for KexiComboBoxBase
+ virtual QVariant origValue() const { return m_origValue; }
+
+ virtual void setValueInternal(const QVariant& add, bool removeOld)
+ { KexiComboBoxBase::setValueInternal(add, removeOld); }
+
+ virtual QVariant value() { return KexiComboBoxBase::value(); }
+
+ virtual void clear();
+
+ virtual bool valueChanged();
+
+ virtual QVariant visibleValue();
+
+ /*! Reimplemented: resizes a view(). */
+ virtual void resize(int w, int h);
+
+ virtual void showFocus( const QRect& r, bool readOnly );
+
+ virtual void hideFocus();
+
+ virtual void paintFocusBorders( QPainter *p, QVariant &cal, int x, int y, int w, int h );
+
+ /*! Setups contents of the cell. As a special case, if there is lookup field schema
+ defined, \a val already contains the visible value (usually the text)
+ set by \ref KexiTableView::paintcell(), so there is noo need to lookup the value
+ in the combo box's popup. */
+ virtual void setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h );
+
+ /*! Used to handle key press events for the item. */
+ virtual bool handleKeyPress( QKeyEvent *ke, bool editorActive );
+
+ virtual int widthForValue( QVariant &val, const QFontMetrics &fm );
+
+ virtual void hide();
+ virtual void show();
+
+ /*! \return total size of this editor, including popup button. */
+ virtual QSize totalSize() const;
+
+ virtual void createInternalEditor(KexiDB::QuerySchema& schema);
+
+ /*! Reimplemented after KexiInputTableEdit. */
+ virtual void handleAction(const QString& actionName);
+
+ /*! Reimplemented after KexiInputTableEdit.
+ For a special case (combo box), \a visibleValue can be provided,
+ so it can be copied to the clipboard instead of unreadable \a value. */
+ virtual void handleCopyAction(const QVariant& value, const QVariant& visibleValue);
+
+ public slots:
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToEnd();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToStart();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void selectAll();
+
+ protected slots:
+ void slotButtonClicked();
+ void slotRowAccepted(KexiTableItem *item, int row) { KexiComboBoxBase::slotRowAccepted(item, row); }
+ void slotItemSelected(KexiTableItem* item) { KexiComboBoxBase::slotItemSelected(item); }
+ void slotInternalEditorValueChanged(const QVariant& v)
+ { KexiComboBoxBase::slotInternalEditorValueChanged(v); }
+ void slotLineEditTextChanged(const QString& s);
+ void slotPopupHidden();
+
+ protected:
+ //! internal
+ void updateFocus( const QRect& r );
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ //! Implemented for KexiComboBoxBase
+ virtual QWidget *internalEditor() const;
+
+ //! Implemented for KexiComboBoxBase
+ virtual void moveCursorToEndInInternalEditor();
+
+ //! Implemented for KexiComboBoxBase
+ virtual void selectAllInInternalEditor();
+
+ //! Implemented for KexiComboBoxBase
+ virtual void setValueInInternalEditor(const QVariant& value);
+
+ //! Implemented for KexiComboBoxBase
+ virtual QVariant valueFromInternalEditor();
+
+ //! Implemented for KexiComboBoxBase
+ virtual void editRequested() { KexiInputTableEdit::editRequested(); }
+
+ //! Implemented for KexiComboBoxBase
+ virtual void acceptRequested() { KexiInputTableEdit::acceptRequested(); }
+
+ //! Implemented for KexiComboBoxBase
+ virtual QPoint mapFromParentToGlobal(const QPoint& pos) const;
+
+ //! Implemented for KexiComboBoxBase
+ virtual int popupWidthHint() const;
+
+ //! Implemented this to update button state.
+ virtual void updateButton();
+
+ virtual KexiComboBoxPopup *popup() const;
+ virtual void setPopup(KexiComboBoxPopup *popup);
+
+ class Private;
+ Private *d;
+};
+
+KEXI_DECLARE_CELLEDITOR_FACTORY_ITEM(KexiComboBoxEditorFactoryItem)
+
+#endif
diff --git a/kexi/widget/tableview/kexidataawareobjectiface.cpp b/kexi/widget/tableview/kexidataawareobjectiface.cpp
new file mode 100644
index 00000000..59edbed3
--- /dev/null
+++ b/kexi/widget/tableview/kexidataawareobjectiface.cpp
@@ -0,0 +1,2108 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ Based on KexiTableView code.
+ Copyright (C) 2002 Till Busch <till@bux.at>
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
+
+ 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 "kexidataawareobjectiface.h"
+
+#include <qscrollview.h>
+#include <qlabel.h>
+#include <qtooltip.h>
+
+#include <kmessagebox.h>
+
+#include <kexi.h>
+#include <kexiutils/validator.h>
+#include <widget/utils/kexirecordnavigator.h>
+#include <widget/utils/kexirecordmarker.h>
+#include <kexidb/roweditbuffer.h>
+#include <kexidataiteminterface.h>
+
+#include "kexitableviewheader.h"
+
+using namespace KexiUtils;
+
+KexiDataAwareObjectInterface::KexiDataAwareObjectInterface()
+{
+ m_data = 0;
+ m_itemIterator = 0;
+ m_readOnly = -1; //don't know
+ m_insertingEnabled = -1; //don't know
+ m_isSortingEnabled = true;
+ m_isFilteringEnabled = true;
+ m_deletionPolicy = AskDelete;
+ m_inside_acceptEditor = false;
+ m_acceptsRowEditAfterCellAccepting = false;
+ m_internal_acceptsRowEditAfterCellAccepting = false;
+ m_contentsMousePressEvent_dblClick = false;
+ m_navPanel = 0;
+ m_initDataContentsOnShow = false;
+ m_cursorPositionSetExplicityBeforeShow = false;
+ m_verticalHeader = 0;
+ m_horizontalHeader = 0;
+ m_insertItem = 0;
+// m_rowEditBuffer = 0;
+ m_spreadSheetMode = false;
+ m_dropsAtRowEnabled = false;
+ m_updateEntireRowWhenMovingToOtherRow = false;
+ m_dragIndicatorLine = -1;
+ m_emptyRowInsertingEnabled = false;
+ m_popupMenu = 0;
+ m_contextMenuEnabled = true;
+ m_rowWillBeDeleted = -1;
+ m_alsoUpdateNextRow = false;
+ m_verticalHeaderAlreadyAdded = false;
+ m_vScrollBarValueChanged_enabled = true;
+ m_scrollbarToolTipsEnabled = true;
+ m_scrollBarTipTimerCnt = 0;
+ m_scrollBarTip = 0;
+ m_recentSearchDirection = KexiSearchAndReplaceViewInterface::Options::DefaultSearchDirection;
+
+ // setup scrollbar tooltip and related members
+ m_scrollBarTip = new QLabel("",0, "vScrollBarToolTip",
+ Qt::WStyle_Customize |Qt::WStyle_NoBorder|Qt::WX11BypassWM|Qt::WStyle_StaysOnTop|Qt::WStyle_Tool);
+ m_scrollBarTip->setPalette(QToolTip::palette());
+ m_scrollBarTip->setMargin(2);
+ m_scrollBarTip->setIndent(0);
+ m_scrollBarTip->setAlignment(Qt::AlignCenter);
+ m_scrollBarTip->setFrameStyle( QFrame::Plain | QFrame::Box );
+ m_scrollBarTip->setLineWidth(1);
+
+ clearVariables();
+}
+
+KexiDataAwareObjectInterface::~KexiDataAwareObjectInterface()
+{
+ delete m_insertItem;
+// delete m_rowEditBuffer;
+ delete m_itemIterator;
+ delete m_scrollBarTip;
+ //we cannot delete m_data here... subclasses should do this
+}
+
+void KexiDataAwareObjectInterface::clearVariables()
+{
+ m_editor = 0;
+// m_rowEditBuffer = 0;
+ m_rowEditing = false;
+ m_newRowEditing = false;
+ m_curRow = -1;
+ m_curCol = -1;
+ m_currentItem = 0;
+}
+
+void KexiDataAwareObjectInterface::setData( KexiTableViewData *data, bool owner )
+{
+ const bool theSameData = m_data && m_data==data;
+ if (m_owner && m_data && m_data!=data/*don't destroy if it's the same*/) {
+ kexidbg << "KexiDataAwareObjectInterface::setData(): destroying old data (owned)" << endl;
+ delete m_itemIterator;
+ delete m_data; //destroy old data
+ m_data = 0;
+ m_itemIterator = 0;
+ }
+ m_owner = owner;
+ m_data = data;
+ if (m_data)
+ m_itemIterator = m_data->createIterator();
+
+ kdDebug(44021) << "KexiDataAwareObjectInterface::setData(): using shared data" << endl;
+ //add columns
+//OK?
+ clearColumnsInternal(false);
+ if (m_data) {
+ int i = 0;
+ for (KexiTableViewColumn::ListIterator it(m_data->columns);
+ it.current(); ++it, i++)
+ {
+ KexiDB::Field *f = it.current()->field();
+ if (it.current()->visible()) {
+ int wid = f->width();
+ if (wid==0)
+ wid=KEXI_DEFAULT_DATA_COLUMN_WIDTH;//default col width in pixels
+//! @todo add col width configuration and storage
+ addHeaderColumn(it.current()->isHeaderTextVisible()
+ ? it.current()->captionAliasOrName() : QString::null,
+ f->description(), it.current()->icon(), wid);
+ }
+ }
+ }
+ if (m_verticalHeader) {
+ m_verticalHeader->clear();
+ if (m_data)
+ m_verticalHeader->addLabels(m_data->count());
+ }
+ if (m_data && m_data->count()==0)
+ m_navPanel->setCurrentRecordNumber(0+1);
+
+ if (m_data && !theSameData) {
+//! @todo: store sorting settings?
+ setSorting(-1);
+// connect(m_data, SIGNAL(refreshRequested()), this, SLOT(slotRefreshRequested()));
+ connectToReloadDataSlot(m_data, SIGNAL(reloadRequested()));
+ QObject* thisObject = dynamic_cast<QObject*>(this);
+ if (thisObject) {
+ QObject::connect(m_data, SIGNAL(destroying()), thisObject, SLOT(slotDataDestroying()));
+ QObject::connect(m_data, SIGNAL(rowsDeleted( const QValueList<int> & )),
+ thisObject, SLOT(slotRowsDeleted( const QValueList<int> & )));
+ QObject::connect(m_data, SIGNAL(aboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)),
+ thisObject, SLOT(slotAboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)));
+ QObject::connect(m_data, SIGNAL(rowDeleted()), thisObject, SLOT(slotRowDeleted()));
+ QObject::connect(m_data, SIGNAL(rowInserted(KexiTableItem*,bool)),
+ thisObject, SLOT(slotRowInserted(KexiTableItem*,bool)));
+ QObject::connect(m_data, SIGNAL(rowInserted(KexiTableItem*,uint,bool)),
+ thisObject, SLOT(slotRowInserted(KexiTableItem*,uint,bool))); //not db-aware
+ QObject::connect(m_data, SIGNAL(rowRepaintRequested(KexiTableItem&)),
+ thisObject, SLOT(slotRowRepaintRequested(KexiTableItem&)));
+ // setup scrollbar's tooltip
+ QObject::connect(verticalScrollBar(),SIGNAL(sliderReleased()),
+ thisObject,SLOT(vScrollBarSliderReleased()));
+ QObject::connect(verticalScrollBar(),SIGNAL(valueChanged(int)),
+ thisObject,SLOT(vScrollBarValueChanged(int)));
+ QObject::connect(&m_scrollBarTipTimer,SIGNAL(timeout()),
+ thisObject,SLOT(scrollBarTipTimeout()));
+ }
+ }
+
+ if (!m_data) {
+// clearData();
+ cancelRowEdit();
+ //m_data->clearInternal();
+ clearVariables();
+ }
+ else {
+ if (!m_insertItem) {//first setData() call - add 'insert' item
+ m_insertItem = m_data->createItem(); //new KexiTableItem(m_data->columns.count());
+ }
+ else {//just reinit
+ m_insertItem->init(m_data->columns.count());
+ }
+ }
+
+ //update gui mode
+ m_navPanel->setInsertingEnabled(m_data && isInsertingEnabled());
+ if (m_verticalHeader)
+ m_verticalHeader->showInsertRow(m_data && isInsertingEnabled());
+
+ initDataContents();
+ updateIndicesForVisibleValues();
+
+ if (m_data)
+ /*emit*/ dataSet( m_data );
+}
+
+void KexiDataAwareObjectInterface::initDataContents()
+{
+ m_editor = 0;
+// QSize s(tableSize());
+// resizeContents(s.width(),s.height());
+
+ m_navPanel->setRecordCount(rows());
+
+ if (m_data && !m_cursorPositionSetExplicityBeforeShow) {
+ //set current row:
+ m_currentItem = 0;
+ int curRow = -1, curCol = -1;
+ if (m_data->columnsCount()>0) {
+ if (rows()>0) {
+ m_itemIterator->toFirst();
+ m_currentItem = **m_itemIterator;
+ curRow = 0;
+ curCol = 0;
+ }
+ else {//no data
+ if (isInsertingEnabled()) {
+ m_currentItem = m_insertItem;
+ curRow = 0;
+ curCol = 0;
+ }
+ }
+ }
+ setCursorPosition(curRow, curCol, true/*force*/);
+ }
+ ensureCellVisible(m_curRow, m_curCol);
+// updateRowCountInfo();
+// setNavRowCount(rows());
+
+//OK?
+// updateContents();
+ updateWidgetContents();
+
+ m_cursorPositionSetExplicityBeforeShow = false;
+
+ /*emit*/ dataRefreshed();
+}
+
+void KexiDataAwareObjectInterface::setSortingEnabled(bool set)
+{
+ if (m_isSortingEnabled && !set)
+ setSorting(-1);
+ m_isSortingEnabled = set;
+ /*emit*/ reloadActions();
+}
+
+void KexiDataAwareObjectInterface::setSorting(int col, bool ascending)
+{
+ if (!m_data || !m_isSortingEnabled)
+ return;
+// d->pTopHeader->setSortIndicator(col, ascending ? Ascending : Descending);
+ setLocalSortingOrder(col, ascending ? 1 : -1);
+ m_data->setSorting(col, ascending);
+}
+
+int KexiDataAwareObjectInterface::dataSortedColumn() const
+{
+ if (m_data && m_isSortingEnabled)
+ return m_data->sortedColumn();
+ return -1;
+}
+
+int KexiDataAwareObjectInterface::dataSortingOrder() const
+{
+ return m_data ? m_data->sortingOrder() : 0;
+}
+
+bool KexiDataAwareObjectInterface::sort()
+{
+ if (!m_data || !m_isSortingEnabled)
+ return false;
+
+ if (rows() < 2)
+ return true;
+
+ if (!acceptRowEdit())
+ return false;
+
+ const int oldRow = m_curRow;
+ if (m_data->sortedColumn()!=-1)
+ m_data->sort();
+
+ //locate current record
+ if (!m_currentItem) {
+ m_itemIterator->toFirst();
+ m_currentItem = **m_itemIterator; //m_data->first();
+ m_curRow = 0;
+ if (!m_currentItem)
+ return true;
+ }
+ if (m_currentItem != m_insertItem) {
+ m_curRow = m_data->findRef(m_currentItem);
+ int jump = m_curRow - oldRow;
+ if (jump<0)
+ (*m_itemIterator) -= -jump;
+ else
+ (*m_itemIterator) += jump;
+ }
+
+ updateGUIAfterSorting();
+ editorShowFocus( m_curRow, m_curCol );
+ if (m_verticalHeader)
+ m_verticalHeader->setCurrentRow(m_curRow);
+ if (m_horizontalHeader)
+ m_horizontalHeader->setSelectedSection(m_curCol);
+ if (m_navPanel)
+ m_navPanel->setCurrentRecordNumber(m_curRow+1);
+ return true;
+}
+
+void KexiDataAwareObjectInterface::sortAscending()
+{
+ if (currentColumn()<0)
+ return;
+ sortColumnInternal( currentColumn(), 1 );
+}
+
+void KexiDataAwareObjectInterface::sortDescending()
+{
+ if (currentColumn()<0)
+ return;
+ sortColumnInternal( currentColumn(), -1 );
+}
+
+void KexiDataAwareObjectInterface::sortColumnInternal(int col, int order)
+{
+ //-select sorting
+ bool asc;
+ if (order == 0) {// invert
+ if (col==dataSortedColumn() && dataSortingOrder()==1)
+ asc = dataSortingOrder()==-1; //inverse sorting for this column -> descending order
+ else
+ asc = true;
+ }
+ else
+ asc = (order==1);
+
+ int prevSortOrder = currentLocalSortingOrder();
+ const int prevSortColumn = currentLocalSortingOrder();
+ setSorting( col, asc );
+ //-perform sorting
+ if (!sort())
+ setLocalSortingOrder(prevSortColumn, prevSortOrder); //this will also remove indicator
+ //if prevSortColumn==-1
+ if (col != prevSortColumn)
+ /*emit*/ sortedColumnChanged(col);
+}
+
+bool KexiDataAwareObjectInterface::isInsertingEnabled() const
+{
+ if (isReadOnly())
+ return false;
+ if (m_insertingEnabled == 1 || m_insertingEnabled == 0)
+ return (bool)m_insertingEnabled;
+ if (!hasData())
+ return true;
+ return m_data->isInsertingEnabled();
+}
+
+void KexiDataAwareObjectInterface::setFilteringEnabled(bool set)
+{
+ m_isFilteringEnabled = set;
+}
+
+bool KexiDataAwareObjectInterface::isDeleteEnabled() const
+{
+ return (m_deletionPolicy != NoDelete) && !isReadOnly();
+}
+
+void KexiDataAwareObjectInterface::setDeletionPolicy(DeletionPolicy policy)
+{
+ m_deletionPolicy = policy;
+// updateContextMenu();
+}
+
+void KexiDataAwareObjectInterface::setReadOnly(bool set)
+{
+ if (isReadOnly() == set || (m_data && m_data->isReadOnly() && !set))
+ return; //not allowed!
+ m_readOnly = (set ? 1 : 0);
+ if (set)
+ setInsertingEnabled(false);
+ updateWidgetContents();
+ /*emit*/ reloadActions();
+}
+
+bool KexiDataAwareObjectInterface::isReadOnly() const
+{
+ if (!hasData())
+ return true;
+ if (m_readOnly == 1 || m_readOnly == 0)
+ return (bool)m_readOnly;
+ if (!hasData())
+ return true;
+ return m_data->isReadOnly();
+}
+
+void KexiDataAwareObjectInterface::setInsertingEnabled(bool set)
+{
+ if (isInsertingEnabled() == set || (m_data && !m_data->isInsertingEnabled() && set))
+ return; //not allowed!
+ m_insertingEnabled = (set ? 1 : 0);
+ m_navPanel->setInsertingEnabled(set);
+ if (m_verticalHeader)
+ m_verticalHeader->showInsertRow(set);
+ if (set)
+ setReadOnly(false);
+// update();
+ updateWidgetContents();
+ /*emit*/ reloadActions();
+}
+
+void KexiDataAwareObjectInterface::setSpreadSheetMode()
+{
+ m_spreadSheetMode = true;
+ setSortingEnabled( false );
+ setInsertingEnabled( false );
+ setAcceptsRowEditAfterCellAccepting( true );
+ setFilteringEnabled( false );
+ setEmptyRowInsertingEnabled( true );
+ m_navPanelEnabled = false;
+}
+
+void KexiDataAwareObjectInterface::selectNextRow()
+{
+ selectRow( QMIN( rows() - 1 +(isInsertingEnabled()?1:0), m_curRow + 1 ) );
+}
+
+void KexiDataAwareObjectInterface::selectPrevPage()
+{
+ selectRow(
+ QMAX( 0, m_curRow - rowsPerPage() )
+ );
+}
+
+void KexiDataAwareObjectInterface::selectNextPage()
+{
+ selectRow(
+ QMIN(
+ rows() - 1 + (isInsertingEnabled()?1:0),
+ m_curRow + rowsPerPage()
+ )
+ );
+}
+
+void KexiDataAwareObjectInterface::selectFirstRow()
+{
+ selectRow(0);
+}
+
+void KexiDataAwareObjectInterface::selectLastRow()
+{
+// selectRow(rows() - 1 + (isInsertingEnabled()?1:0));
+ selectRow(rows() - 1);
+}
+
+void KexiDataAwareObjectInterface::selectRow(int row)
+{
+ m_vScrollBarValueChanged_enabled = false; //disable tooltip
+ setCursorPosition(row, -1);
+ m_vScrollBarValueChanged_enabled = true;
+}
+
+void KexiDataAwareObjectInterface::selectPrevRow()
+{
+ selectRow( QMAX( 0, m_curRow - 1 ) );
+}
+
+void KexiDataAwareObjectInterface::clearSelection()
+{
+// selectRow( -1 );
+ int oldRow = m_curRow;
+// int oldCol = m_curCol;
+ m_curRow = -1;
+ m_curCol = -1;
+ m_currentItem = 0;
+ updateRow( oldRow );
+ m_navPanel->setCurrentRecordNumber(0);
+// setNavRowNumber(-1);
+}
+
+void KexiDataAwareObjectInterface::setCursorPosition(int row, int col/*=-1*/, bool forceSet)
+{
+ int newrow = row;
+ int newcol = col;
+
+ if(rows() <= 0) {
+ if (m_verticalHeader)
+ m_verticalHeader->setCurrentRow(-1);
+ if (m_horizontalHeader)
+ m_horizontalHeader->setSelectedSection(-1);
+ if (isInsertingEnabled()) {
+ m_currentItem=m_insertItem;
+ newrow=0;
+ if (col>=0)
+ newcol=col;
+ else
+ newcol=0;
+ }
+ else {
+ m_currentItem=0;
+ m_curRow=-1;
+ m_curCol=-1;
+ return;
+ }
+ }
+
+ if(col>=0)
+ {
+ newcol = QMAX(0, col);
+ newcol = QMIN(columns() - 1, newcol);
+ }
+ else {
+ newcol = m_curCol; //no changes
+ newcol = QMAX(0, newcol); //may not be < 0 !
+ }
+ newrow = QMAX(0, row);
+ newrow = QMIN(rows() - 1 + (isInsertingEnabled()?1:0), newrow);
+
+// d->pCurrentItem = itemAt(d->curRow);
+// kdDebug(44021) << "setCursorPosition(): d->curRow=" << d->curRow << " oldRow=" << oldRow << " d->curCol=" << d->curCol << " oldCol=" << oldCol << endl;
+
+ if ( forceSet || m_curRow != newrow || m_curCol != newcol )
+ {
+ kexidbg << "setCursorPosition(): " <<QString("old:%1,%2 new:%3,%4").arg(m_curCol)
+ .arg(m_curRow).arg(newcol).arg(newrow) << endl;
+
+ // cursor moved: get rid of editor
+ if (m_editor) {
+ if (!m_contentsMousePressEvent_dblClick) {
+ if (!acceptEditor()) {
+ return;
+ }
+ //update row num. again
+ newrow = QMIN( rows() - 1 + (isInsertingEnabled()?1:0), newrow);
+ }
+ }
+ if (m_errorMessagePopup) {
+ m_errorMessagePopup->close();
+ }
+
+ if (m_curRow != newrow || forceSet) {//update current row info
+ m_navPanel->setCurrentRecordNumber(newrow+1);
+// setNavRowNumber(newrow);
+// d->navBtnPrev->setEnabled(newrow>0);
+// d->navBtnFirst->setEnabled(newrow>0);
+// d->navBtnNext->setEnabled(newrow<(rows()-1+(isInsertingEnabled()?1:0)));
+// d->navBtnLast->setEnabled(newrow!=(rows()-1));
+ }
+
+ // cursor moved to other row: end of row editing
+ bool newRowInserted = false;
+ if (m_rowEditing && m_curRow != newrow) {
+ newRowInserted = m_newRowEditing;
+ if (!acceptRowEdit()) {
+ //accepting failed: cancel setting the cursor
+ return;
+ }
+ //update row number, because number of rows changed
+ newrow = QMIN( rows() - 1 + (isInsertingEnabled()?1:0), newrow);
+
+ m_navPanel->setCurrentRecordNumber(newrow+1); //refresh
+ }
+
+ //change position
+ int oldRow = m_curRow;
+ int oldCol = m_curCol;
+ m_curRow = newrow;
+ m_curCol = newcol;
+
+// int cw = columnWidth( d->curCol );
+// int rh = rowHeight();
+// ensureVisible( columnPos( d->curCol ) + cw / 2, rowPos( d->curRow ) + rh / 2, cw / 2, rh / 2 );
+// center(columnPos(d->curCol) + cw / 2, rowPos(d->curRow) + rh / 2, cw / 2, rh / 2);
+// kdDebug(44021) << " contentsY() = "<< contentsY() << endl;
+
+//js if (oldRow > d->curRow)
+//js ensureVisible(columnPos(d->curCol), rowPos(d->curRow) + rh, columnWidth(d->curCol), rh);
+//js else// if (oldRow <= d->curRow)
+//js ensureVisible(columnPos(d->curCol), rowPos(d->curRow), columnWidth(d->curCol), rh);
+
+
+ //show editor-dependent focus, if we're changing the current column
+ if (oldCol>=0 && oldCol<columns() && m_curCol!=oldCol) {
+ //find the editor for this column
+ KexiDataItemInterface *edit = editor( oldCol );
+ if (edit) {
+ edit->hideFocus();
+ }
+ }
+
+ // position changed, so subsequent searching should be started from scratch
+ // (e.g. from the current cell or the top-left cell)
+ m_positionOfRecentlyFoundValue.exists = false;
+
+ //show editor-dependent focus, if needed
+ editorShowFocus( m_curRow, m_curCol );
+
+ if (m_updateEntireRowWhenMovingToOtherRow)
+ updateRow( oldRow );
+ else
+ updateCell( oldRow, oldCol );
+
+// //quite clever: ensure the cell is visible:
+// ensureCellVisible(m_curRow, m_curCol);
+
+// QPoint pcenter = QRect( columnPos(d->curCol), rowPos(d->curRow), columnWidth(d->curCol), rh).center();
+// ensureVisible(pcenter.x(), pcenter.y(), columnWidth(d->curCol)/2, rh/2);
+
+// ensureVisible(columnPos(d->curCol), rowPos(d->curRow) - contentsY(), columnWidth(d->curCol), rh);
+ if (m_verticalHeader && (oldRow != m_curRow || forceSet))
+ m_verticalHeader->setCurrentRow(m_curRow);
+
+ if (m_updateEntireRowWhenMovingToOtherRow)
+ updateRow( m_curRow );
+ else
+ updateCell( m_curRow, m_curCol );
+
+ if (m_curCol != oldCol || m_curRow != oldRow || forceSet) {//ensure this is also refreshed
+ if (!m_updateEntireRowWhenMovingToOtherRow) //only if entire row has not been updated
+ updateCell( oldRow, m_curCol );
+ }
+ //update row
+ if (forceSet || m_curRow != oldRow) {
+ if (isInsertingEnabled() && m_curRow == rows()) {
+ kdDebug(44021) << "NOW insert item is current" << endl;
+ m_currentItem = m_insertItem;
+ }
+ else {
+ kdDebug(44021) << QString("NOW item at %1 (%2) is current")
+ .arg(m_curRow).arg((ulong)itemAt(m_curRow)) << endl;
+ //NOT EFFECTIVE!!!!!!!!!!!
+ //set item iterator
+ if (!newRowInserted && isInsertingEnabled() && m_currentItem == m_insertItem && m_curRow == (rows()-1)) {
+ //moving from 'insert item' to last item
+ m_itemIterator->toLast();
+ }
+ else if (!newRowInserted && !forceSet && m_currentItem != m_insertItem && 0==m_curRow)
+ m_itemIterator->toFirst();
+ else if (!newRowInserted && !forceSet && m_currentItem != m_insertItem && oldRow>=0 && (oldRow+1)==m_curRow) //just move next
+ ++(*m_itemIterator);
+ else if (!newRowInserted && !forceSet && m_currentItem != m_insertItem && oldRow>=0 && (oldRow-1)==m_curRow) //just move back
+ --(*m_itemIterator);
+ else { //move at:
+ m_itemIterator->toFirst();
+ (*m_itemIterator)+=m_curRow;
+ }
+ if (!**m_itemIterator) { //sanity
+ m_itemIterator->toFirst();
+ (*m_itemIterator)+=m_curRow;
+ }
+ m_currentItem = **m_itemIterator;
+ //itemAt(m_curRow);
+ }
+ }
+
+ //quite clever: ensure the cell is visible:
+ ensureCellVisible(m_curRow, m_curCol);
+
+ if (m_horizontalHeader && (oldCol != m_curCol || forceSet))
+ m_horizontalHeader->setSelectedSection(m_curCol);
+
+ /*emit*/ itemSelected(m_currentItem);
+ /*emit*/ cellSelected(m_curCol, m_curRow);
+ /* only needed for forms */
+ selectCellInternal();
+ }
+ else {
+ kexidbg << "setCursorPosition(): NO CHANGE" << endl;
+ }
+
+ if(m_initDataContentsOnShow) {
+ m_cursorPositionSetExplicityBeforeShow = true;
+ }
+}
+
+bool KexiDataAwareObjectInterface::acceptRowEdit()
+{
+ if (!m_rowEditing || /*sanity*/!m_data->rowEditBuffer())
+ return true;
+ if (m_inside_acceptEditor) {
+ m_internal_acceptsRowEditAfterCellAccepting = true;
+ return true;
+ }
+ m_internal_acceptsRowEditAfterCellAccepting = false;
+
+ const int columnEditedBeforeAccepting = m_editor ? currentColumn() : -1;
+ if (!acceptEditor())
+ return false;
+ kdDebug() << "EDIT ROW ACCEPTING..." << endl;
+
+ bool success = true;
+// bool allow = true;
+// int faultyColumn = -1; // will be !=-1 if cursor has to be moved to that column
+ const bool inserting = m_newRowEditing;
+// QString msg, desc;
+// bool inserting = d->pInsertItem && d->pInsertItem==d->pCurrentItem;
+
+ if (m_data->rowEditBuffer()->isEmpty() && !m_newRowEditing) {
+/* if (d->newRowEditing) {
+ cancelRowEdit();
+ kdDebug() << "-- NOTHING TO INSERT!!!" << endl;
+ return true;
+ }
+ else {*/
+ kdDebug() << "-- NOTHING TO ACCEPT!!!" << endl;
+// }
+ }
+ else {//not empty edit buffer or new row to insert:
+ if (m_newRowEditing) {
+// emit aboutToInsertRow(d->pCurrentItem, m_data->rowEditBuffer(), success, &faultyColumn);
+// if (success) {
+ kdDebug() << "-- INSERTING: " << endl;
+ m_data->rowEditBuffer()->debug();
+ success = m_data->saveNewRow(*m_currentItem);
+// if (!success) {
+// }
+// }
+ }
+ else {
+// emit aboutToUpdateRow(d->pCurrentItem, m_data->rowEditBuffer(), success, &faultyColumn);
+ if (success) {
+ //accept changes for this row:
+ kdDebug() << "-- UPDATING: " << endl;
+ m_data->rowEditBuffer()->debug();
+ kdDebug() << "-- BEFORE: " << endl;
+ m_currentItem->debug();
+ success = m_data->saveRowChanges(*m_currentItem);//, &msg, &desc, &faultyColumn);
+ kdDebug() << "-- AFTER: " << endl;
+ m_currentItem->debug();
+
+// if (!success) {
+// }
+ }
+ }
+ }
+
+ if (success) {
+ //editing is finished:
+ if (m_newRowEditing) {
+ //update current-item-iterator
+ m_itemIterator->toLast();
+ m_currentItem = **m_itemIterator;
+ }
+ m_rowEditing = false;
+ m_newRowEditing = false;
+ //indicate on the vheader that we are not editing
+ if (m_verticalHeader)
+ m_verticalHeader->setEditRow(-1);
+
+ updateAfterAcceptRowEdit();
+
+ kdDebug() << "EDIT ROW ACCEPTED:" << endl;
+// /*debug*/itemAt(m_curRow);
+
+ if (inserting) {
+// emit rowInserted(d->pCurrentItem);
+ //update navigator's data
+ m_navPanel->setRecordCount(rows());
+ }
+ else {
+// emit rowUpdated(d->pCurrentItem);
+ }
+
+ /*emit*/ rowEditTerminated(m_curRow);
+ }
+ else {
+// if (!allow) {
+// kdDebug() << "INSERT/EDIT ROW - DISALLOWED by signal!" << endl;
+// }
+// else {
+// kdDebug() << "EDIT ROW - ERROR!" << endl;
+// }
+ int faultyColumn = -1;
+ if (m_data->result()->column >= 0 && m_data->result()->column < columns())
+ faultyColumn = m_data->result()->column;
+ else if (columnEditedBeforeAccepting >= 0)
+ faultyColumn = columnEditedBeforeAccepting;
+ if (faultyColumn >= 0) {
+ setCursorPosition(m_curRow, faultyColumn);
+ }
+
+ const int button = showErrorMessageForResult( m_data->result() );
+ if (KMessageBox::No == button) {
+ //discard changes
+ cancelRowEdit();
+ }
+ else {
+ if (faultyColumn >= 0) {
+ //edit this cell
+ startEditCurrentCell();
+ }
+ }
+ }
+
+ return success;
+}
+
+bool KexiDataAwareObjectInterface::cancelRowEdit()
+{
+ if (!hasData())
+ return false;
+ if (!m_rowEditing)
+ return false;
+ cancelEditor();
+ m_rowEditing = false;
+ //indicate on the vheader that we are not editing
+ if (m_verticalHeader)
+ m_verticalHeader->setEditRow(-1);
+ m_alsoUpdateNextRow = m_newRowEditing;
+ if (m_newRowEditing) {
+ m_newRowEditing = false;
+ //remove current edited row (it is @ the end of list)
+ m_data->removeLast();
+ //current item is now empty, last row
+ m_currentItem = m_insertItem;
+ //update visibility
+ if (m_verticalHeader)
+ m_verticalHeader->removeLabel(false); //-1 label
+// updateContents(columnPos(0), rowPos(rows()),
+// viewport()->width(), d->rowHeight*3 + (m_navPanel ? m_navPanel->height() : 0)*3 );
+// updateContents(); //js: above did not work well so we do that dirty
+ updateWidgetContents();
+//TODO: still doesn't repaint properly!!
+// QSize s(tableSize());
+// resizeContents(s.width(), s.height());
+ updateWidgetContentsSize();
+// m_verticalHeader->update();
+ //--no cancel action is needed for datasource,
+ // because the row was not yet stored.
+ }
+
+ m_data->clearRowEditBuffer();
+ updateAfterCancelRowEdit();
+
+//! \todo (js): cancel changes for this row!
+ kexidbg << "EDIT ROW CANCELLED." << endl;
+
+ /*emit*/ rowEditTerminated(m_curRow);
+ return true;
+}
+
+void KexiDataAwareObjectInterface::updateAfterCancelRowEdit()
+{
+ updateRow(m_curRow);
+ if (m_alsoUpdateNextRow)
+ updateRow(m_curRow+1);
+ m_alsoUpdateNextRow = false;
+}
+
+void KexiDataAwareObjectInterface::updateAfterAcceptRowEdit()
+{
+ updateRow(m_curRow);
+}
+
+void KexiDataAwareObjectInterface::removeEditor()
+{
+ if (!m_editor)
+ return;
+ m_editor->hideWidget();
+ m_editor = 0;
+}
+
+bool KexiDataAwareObjectInterface::cancelEditor()
+{
+ if (m_errorMessagePopup) {
+ m_errorMessagePopup->close();
+ }
+ if (!m_editor)
+ return false;
+ removeEditor();
+ return true;
+}
+
+//! @internal
+class KexiDataAwareObjectInterfaceToolTip : public QToolTip {
+ public:
+ KexiDataAwareObjectInterfaceToolTip( const QString & text, const QPoint & pos, QWidget * widget )
+ : QToolTip(widget), m_text(text)
+ {
+ tip( QRect(pos, QSize(100, 100)), text );
+ }
+ virtual void maybeTip(const QPoint & p) {
+ tip( QRect(p, QSize(100, 100)), m_text);
+ }
+ QString m_text;
+};
+
+bool KexiDataAwareObjectInterface::acceptEditor()
+{
+ if (!hasData())
+ return true;
+ if (!m_editor || m_inside_acceptEditor)
+ return true;
+
+ m_inside_acceptEditor = true;//avoid recursion
+
+ QVariant newval;
+ Validator::Result res = Validator::Ok;
+ QString msg, desc;
+ bool setNull = false;
+// bool allow = true;
+// static const QString msg_NOT_NULL = i18n("\"%1\" column requires a value to be entered.");
+
+ //autoincremented field can be omitted (left as null or empty) if we're inserting a new row
+ const bool autoIncColumnCanBeOmitted = m_newRowEditing && m_editor->field()->isAutoIncrement();
+// const bool autoIncColumnCanBeOmitted = m_newRowEditing && m_editor->columnInfo()->field->isAutoIncrement();
+
+ bool valueChanged = m_editor->valueChanged();
+ bool editCurrentCellAgain = false;
+
+ if (valueChanged) {
+ if (!m_editor->valueIsValid()) {
+ //used e.g. for date or time values - the value can be null but not necessary invalid
+ res = Validator::Error;
+ editCurrentCellAgain = true;
+ QWidget *par = dynamic_cast<QScrollView*>(this) ? dynamic_cast<QScrollView*>(this)->viewport() :
+ dynamic_cast<QWidget*>(this);
+ QWidget *edit = dynamic_cast<QWidget*>(m_editor);
+ if (par && edit) {
+//! @todo allow displaying user-defined warning
+//! @todo also use for other error messages
+ if (!m_errorMessagePopup) {
+// m_errorMessagePopup->close();
+ m_errorMessagePopup = new KexiArrowTip(
+ i18n("Error: %1").arg(m_editor->columnInfo()->field->typeName())+"?",
+ dynamic_cast<QWidget*>(this));
+ m_errorMessagePopup->move(
+ par->mapToGlobal(edit->pos()) + QPoint(6, edit->height() + 0) );
+ m_errorMessagePopup->show();
+ }
+ m_editor->setFocus();
+ }
+ }
+ else if (m_editor->valueIsNull()) {//null value entered
+// if (m_editor->columnInfo()->field->isNotNull() && !autoIncColumnCanBeOmitted) {
+ if (m_editor->field()->isNotNull() && !autoIncColumnCanBeOmitted) {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NULL NOT ALLOWED!" << endl;
+ res = Validator::Error;
+// msg = Validator::msgColumnNotEmpty().arg(m_editor->columnInfo()->field->captionOrName())
+ msg = Validator::msgColumnNotEmpty().arg(m_editor->field()->captionOrName())
+ + "\n\n" + Kexi::msgYouCanImproveData();
+ desc = i18n("The column's constraint is declared as NOT NULL.");
+ editCurrentCellAgain = true;
+ // allow = false;
+ // removeEditor();
+ // return true;
+ }
+ else {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NULL VALUE WILL BE SET" << endl;
+ //ok, just leave newval as NULL
+ setNull = true;
+ }
+ }
+ else if (m_editor->valueIsEmpty()) {//empty value entered
+// if (m_editor->columnInfo()->field->hasEmptyProperty()) {
+ if (m_editor->field()->hasEmptyProperty()) {
+// if (m_editor->columnInfo()->field->isNotEmpty() && !autoIncColumnCanBeOmitted) {
+ if (m_editor->field()->isNotEmpty() && !autoIncColumnCanBeOmitted) {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): EMPTY NOT ALLOWED!" << endl;
+ res = Validator::Error;
+// msg = Validator::msgColumnNotEmpty().arg(m_editor->columnInfo()->field->captionOrName())
+ msg = Validator::msgColumnNotEmpty().arg(m_editor->field()->captionOrName())
+ + "\n\n" + Kexi::msgYouCanImproveData();
+ desc = i18n("The column's constraint is declared as NOT EMPTY.");
+ editCurrentCellAgain = true;
+ // allow = false;
+ // removeEditor();
+ // return true;
+ }
+ else {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): EMPTY VALUE WILL BE SET" << endl;
+ }
+ }
+ else {
+// if (m_editor->columnInfo()->field->isNotNull() && !autoIncColumnCanBeOmitted) {
+ if (m_editor->field()->isNotNull() && !autoIncColumnCanBeOmitted) {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NEITHER NULL NOR EMPTY VALUE CAN BE SET!" << endl;
+ res = Validator::Error;
+// msg = Validator::msgColumnNotEmpty().arg(m_editor->columnInfo()->field->captionOrName())
+ msg = Validator::msgColumnNotEmpty().arg(m_editor->field()->captionOrName())
+ + "\n\n" + Kexi::msgYouCanImproveData();
+ desc = i18n("The column's constraint is declared as NOT EMPTY and NOT NULL.");
+ editCurrentCellAgain = true;
+// allow = false;
+ // removeEditor();
+ // return true;
+ }
+ else {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NULL VALUE WILL BE SET BECAUSE EMPTY IS NOT ALLOWED" << endl;
+ //ok, just leave newval as NULL
+ setNull = true;
+ }
+ }
+ }
+ }//changed
+
+ const int realFieldNumber = fieldNumberForColumn(m_curCol);
+ if (realFieldNumber < 0) {
+ kdWarning() << "KexiDataAwareObjectInterface::acceptEditor(): fieldNumberForColumn(m_curCol) < 0" << endl;
+ m_inside_acceptEditor = false;
+ return false;
+ }
+
+ KexiTableViewColumn *currentTVColumn = column(m_curCol);
+
+ //try to get the value entered:
+ if (res == Validator::Ok) {
+ if ((!setNull && !valueChanged)
+ || (m_editor->field()->type()!=KexiDB::Field::Boolean && setNull && m_currentItem->at( realFieldNumber ).isNull())) {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): VALUE NOT CHANGED." << endl;
+ removeEditor();
+ if (m_acceptsRowEditAfterCellAccepting || m_internal_acceptsRowEditAfterCellAccepting)
+ acceptRowEdit();
+ m_inside_acceptEditor = false;
+ return true;
+ }
+ if (!setNull) {//get the new value
+// bool ok;
+ newval = m_editor->value();
+//! @todo validation rules for this value?
+/*
+ if (!ok) {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): INVALID VALUE - NOT CHANGED." << endl;
+ res = KexiValidator::Error;
+//js: TODO get detailed info on why m_editor->value() failed
+ msg = i18n("Entered value is invalid.")
+ + "\n\n" + KexiValidator::msgYouCanImproveData();
+ editCurrentCellAgain = true;
+// removeEditor();
+// return true;
+ }*/
+ }
+
+ //Check other validation rules:
+ //1. check using validator
+// KexiValidator *validator = m_data->column(m_curCol)->validator();
+ Validator *validator = currentTVColumn->validator();
+ if (validator) {
+// res = validator->check(m_data->column(m_curCol)->field()->captionOrName(),
+ res = validator->check(currentTVColumn->field()->captionOrName(),
+ newval, msg, desc);
+ }
+ }
+
+ //show the validation result if not OK:
+ if (res == Validator::Error) {
+ if (!msg.isEmpty()) {
+ if (desc.isEmpty())
+ KMessageBox::sorry(dynamic_cast<QWidget*>(this), msg);
+ else
+ KMessageBox::detailedSorry(dynamic_cast<QWidget*>(this), msg, desc);
+ }
+ editCurrentCellAgain = true;
+// allow = false;
+ }
+ else if (res == Validator::Warning) {
+ //js: todo: message!!!
+ KMessageBox::messageBox(dynamic_cast<QWidget*>(this), KMessageBox::Sorry, msg + "\n" + desc);
+ editCurrentCellAgain = true;
+ }
+
+ if (res == Validator::Ok) {
+ //2. check using signal
+ //bool allow = true;
+// emit aboutToChangeCell(d->pCurrentItem, newval, allow);
+// if (allow) {
+ //send changes to the backend
+ QVariant visibleValue;
+ if (!newval.isNull()/* visible value should be null if value is null */
+ && currentTVColumn->visibleLookupColumnInfo)
+ {
+ visibleValue = m_editor->visibleValue(); //visible value for lookup field
+ }
+ //should be also added to the buffer
+ if (m_data->updateRowEditBufferRef(m_currentItem, m_curCol, currentTVColumn,
+ newval, /*allowSignals*/true, currentTVColumn->visibleLookupColumnInfo ? &visibleValue : 0))
+ {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): ------ EDIT BUFFER CHANGED TO:" << endl;
+ m_data->rowEditBuffer()->debug();
+ } else {
+ kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): ------ CHANGE FAILED in KexiDataAwareObjectInterface::updateRowEditBuffer()" << endl;
+ res = Validator::Error;
+
+ //now: there might be called cancelEditor() in updateRowEditBuffer() handler,
+ //if this is true, d->pEditor is NULL.
+
+ if (m_editor && m_data->result()->column>=0 && m_data->result()->column<columns()) {
+ //move to faulty column (if m_editor is not cleared)
+ setCursorPosition(m_curRow, m_data->result()->column);
+ }
+ if (!m_data->result()->msg.isEmpty()) {
+ const int button = showErrorMessageForResult( m_data->result() );
+ if (KMessageBox::No == button) {
+ //discard changes
+ cancelEditor();
+ if (m_acceptsRowEditAfterCellAccepting)
+ cancelRowEdit();
+ m_inside_acceptEditor = false;
+ return false;
+ }
+ }
+ }
+ }
+
+ if (res == Validator::Ok) {
+ removeEditor();
+ /*emit*/ itemChanged(m_currentItem, m_curRow, m_curCol,
+ m_currentItem->at( realFieldNumber ));
+ /*emit*/ itemChanged(m_currentItem, m_curRow, m_curCol);
+ }
+ m_inside_acceptEditor = false;
+ if (res == Validator::Ok) {
+ if (m_acceptsRowEditAfterCellAccepting || m_internal_acceptsRowEditAfterCellAccepting)
+ acceptRowEdit();
+ return true;
+ }
+ if (m_editor) {
+ //allow to edit the cell again, (if m_pEditor is not cleared)
+
+ if (m_editor->hasFocusableWidget()) {
+ m_editor->showWidget();
+ m_editor->setFocus();
+ }
+// startEditCurrentCell(newval.type()==QVariant::String ? newval.toString() : QString::null);
+// m_editor->setFocus();
+ }
+ return false;
+}
+
+void KexiDataAwareObjectInterface::startEditCurrentCell(const QString &setText)
+{
+ kdDebug() << "** KexiDataAwareObjectInterface::startEditCurrentCell("<<setText<<")"<<endl;
+// if (columnType(d->curCol) == KexiDB::Field::Boolean)
+// return;
+ if (isReadOnly() || !columnEditable(m_curCol))
+ return;
+ if (m_editor) {
+ if (m_editor->hasFocusableWidget()) {
+ m_editor->showWidget();
+ m_editor->setFocus();
+ }
+ }
+// ensureVisible(columnPos(m_curCol), rowPos(m_curRow)+rowHeight(),
+// columnWidth(m_curCol), rowHeight());
+//OK?
+ //ensureCellVisible(m_curRow+1, m_curCol);
+ if (!m_editor)
+ createEditor(m_curRow, m_curCol, setText, !setText.isEmpty());
+}
+
+void KexiDataAwareObjectInterface::deleteAndStartEditCurrentCell()
+{
+ if (isReadOnly() || !columnEditable(m_curCol))
+ return;
+ if (m_editor) {//if we've editor - just clear it
+ m_editor->clear();
+ return;
+ }
+//js if (columnType(m_curCol) == KexiDB::Field::Boolean)
+//js return;
+// ensureVisible(columnPos(m_curCol), rowPos(m_curRow) + rowHeight(),
+// columnWidth(m_curCol), rowHeight());
+//OK?
+ ensureCellVisible(m_curRow+1, m_curCol);
+ createEditor(m_curRow, m_curCol, QString::null, false/*removeOld*/);
+ if (!m_editor)
+ return;
+ m_editor->clear();
+ if (m_editor->acceptEditorAfterDeleteContents())
+ acceptEditor();
+ if (!m_editor || !m_editor->hasFocusableWidget())
+ updateCell(m_curRow, m_curCol);
+}
+
+void KexiDataAwareObjectInterface::deleteCurrentRow()
+{
+ if (m_newRowEditing) {//we're editing fresh new row: just cancel this!
+ cancelRowEdit();
+ return;
+ }
+
+ if (!acceptRowEdit())
+ return;
+
+ if (!isDeleteEnabled() || !m_currentItem || m_currentItem == m_insertItem)
+ return;
+ switch (m_deletionPolicy) {
+ case NoDelete:
+ return;
+ case ImmediateDelete:
+ break;
+ case AskDelete:
+ if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(dynamic_cast<QWidget*>(this),
+ i18n("Do you want to delete selected row?"), 0,
+ KGuiItem(i18n("&Delete Row"),"editdelete"),
+ "dontAskBeforeDeleteRow"/*config entry*/,
+ KMessageBox::Notify|KMessageBox::Dangerous))
+ return;
+ break;
+ case SignalDelete:
+ /*emit*/ itemDeleteRequest(m_currentItem, m_curRow, m_curCol);
+ /*emit*/ currentItemDeleteRequest();
+ return;
+ default:
+ return;
+ }
+
+ if (!deleteItem(m_currentItem)) {//nothing
+ }
+}
+
+KexiTableItem *KexiDataAwareObjectInterface::insertEmptyRow(int row)
+{
+ if ( !acceptRowEdit() || !isEmptyRowInsertingEnabled()
+ || (row!=-1 && row >= ((int)rows()+(isInsertingEnabled()?1:0) ) ) )
+ return 0;
+
+ KexiTableItem *newItem = m_data->createItem(); //new KexiTableItem(m_data->columns.count());
+ insertItem(newItem, row);
+ return newItem;
+}
+
+void KexiDataAwareObjectInterface::insertItem(KexiTableItem *newItem, int row)
+{
+ const bool changeCurrentRow = row==-1 || row==m_curRow;
+ if (changeCurrentRow) {
+ //change current row
+ row = (m_curRow >= 0 ? m_curRow : 0);
+ m_currentItem = newItem;
+ m_curRow = row;
+ }
+ else if (m_curRow >= row) {
+ m_curRow++;
+ }
+
+ m_data->insertRow(*newItem, row, true /*repaint*/);
+
+ if (changeCurrentRow) {
+ //update iter...
+ m_itemIterator->toFirst();
+ (*m_itemIterator)+=m_curRow;
+ }
+/*
+ QSize s(tableSize());
+ resizeContents(s.width(),s.height());
+
+ //redraw only this row and below:
+ int leftcol = d->pTopHeader->sectionAt( d->pTopHeader->offset() );
+// updateContents( columnPos( leftcol ), rowPos(d->curRow),
+// clipper()->width(), clipper()->height() - (rowPos(d->curRow) - contentsY()) );
+ updateContents( columnPos( leftcol ), rowPos(row),
+ clipper()->width(), clipper()->height() - (rowPos(row) - contentsY()) );
+
+ m_verticalHeader->addLabel();
+
+ //update navigator's data
+ setNavRowCount(rows());
+
+ if (d->curRow >= row) {
+ //update
+ editorShowFocus( d->curRow, d->curCol );
+ }
+ */
+}
+
+void KexiDataAwareObjectInterface::slotRowInserted(KexiTableItem *item, bool repaint)
+{
+ int row = m_data->findRef(item);
+ slotRowInserted( item, row, repaint );
+}
+
+void KexiDataAwareObjectInterface::slotRowInserted(KexiTableItem * /*item*/, uint row, bool repaint)
+{
+ if (repaint && (int)row<rows()) {
+ updateWidgetContentsSize();
+
+/* updateAllVisibleRowsBelow() used instead
+ //redraw only this row and below:
+ int leftcol = d->pTopHeader->sectionAt( d->pTopHeader->offset() );
+ updateContents( columnPos( leftcol ), rowPos(row),
+ clipper()->width(), clipper()->height() - (rowPos(row) - contentsY()) );
+*/
+ updateAllVisibleRowsBelow(row);
+
+ if (!m_verticalHeaderAlreadyAdded) {
+ if (m_verticalHeader)
+ m_verticalHeader->addLabel();
+ }
+ else //it was added because this inserting was interactive
+ m_verticalHeaderAlreadyAdded = false;
+
+ //update navigator's data
+ m_navPanel->setRecordCount(rows());
+
+ if (m_curRow >= (int)row) {
+ //update
+ editorShowFocus( m_curRow, m_curCol );
+ }
+ }
+}
+
+tristate KexiDataAwareObjectInterface::deleteAllRows(bool ask, bool repaint)
+{
+ if (!hasData())
+ return true;
+ if (m_data->count()<1)
+ return true;
+
+ if (ask) {
+ QString tableName = m_data->dbTableName();
+ if (!tableName.isEmpty()) {
+ tableName.prepend(" \"");
+ tableName.append("\"");
+ }
+ if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(dynamic_cast<QWidget*>(this),
+ i18n("Do you want to clear the contents of table %1?").arg(tableName),
+ 0, KGuiItem(i18n("&Clear Contents")) ))
+ return cancelled;
+ }
+
+ cancelRowEdit();
+// acceptRowEdit();
+// m_verticalHeader->clear();
+ const bool repaintLater = repaint && m_spreadSheetMode;
+ const int oldRows = rows();
+
+ bool res = m_data->deleteAllRows(repaint && !repaintLater);
+
+ if (res) {
+ if (m_spreadSheetMode) {
+// const uint columns = m_data->columns.count();
+ for (int i=0; i<oldRows; i++) {
+ m_data->append(m_data->createItem());//new KexiTableItem(columns));
+ }
+ }
+ }
+ if (repaintLater)
+ m_data->reload();
+
+// d->clearVariables();
+// m_verticalHeader->setCurrentRow(-1);
+
+// d->pUpdateTimer->start(1,true);
+// if (repaint)
+// viewport()->repaint();
+ return res;
+}
+
+void KexiDataAwareObjectInterface::clearColumns(bool repaint)
+{
+ cancelRowEdit();
+ m_data->clearInternal();
+
+ clearColumnsInternal(repaint);
+ updateIndicesForVisibleValues();
+
+ if (repaint)
+// viewport()->repaint();
+//OK?
+ updateWidgetContents();
+
+/* for(int i=0; i < rows(); i++)
+ {
+ m_verticalHeader->removeLabel();
+ }
+
+ editorCancel();
+ m_contents->clear();
+
+ d->clearVariables();
+ d->numCols = 0;
+
+ while(d->pTopHeader->count()>0)
+ d->pTopHeader->removeLabel(0);
+
+ m_verticalHeader->setCurrentRow(-1);
+
+ viewport()->repaint();
+
+// d->pColumnTypes.resize(0);
+// d->pColumnModes.resize(0);
+// d->pColumnDefaults.clear();*/
+}
+
+void KexiDataAwareObjectInterface::reloadData()
+{
+// cancelRowEdit();
+ acceptRowEdit();
+ if (m_verticalHeader)
+ m_verticalHeader->clear();
+
+ if (m_curCol>=0 && m_curCol<columns()) {
+ //find the editor for this column
+ KexiDataItemInterface *edit = editor( m_curCol );
+ if (edit) {
+ edit->hideFocus();
+ }
+ }
+// setCursorPosition(-1, -1, true);
+ clearVariables();
+ if (m_verticalHeader)
+ m_verticalHeader->setCurrentRow(-1);
+
+ if (dynamic_cast<QWidget*>(this) && dynamic_cast<QWidget*>(this)->isVisible())
+ initDataContents();
+ else
+ m_initDataContentsOnShow = true;
+
+ if (m_verticalHeader)
+ m_verticalHeader->addLabels(m_data->count());
+
+ updateWidgetScrollBars();
+}
+
+int KexiDataAwareObjectInterface::columnType(int col)
+{
+ KexiTableViewColumn* c = m_data ? column(col) : 0;
+ return c ? c->field()->type() : KexiDB::Field::InvalidType;
+}
+
+bool KexiDataAwareObjectInterface::columnEditable(int col)
+{
+ KexiTableViewColumn* c = m_data ? column(col) : 0;
+ return c ? (! c->isReadOnly()) : false;
+}
+
+int KexiDataAwareObjectInterface::rows() const
+{
+ if (!hasData())
+ return 0;
+ return m_data->count();
+}
+
+int KexiDataAwareObjectInterface::dataColumns() const
+{
+ if (!hasData())
+ return 0;
+ return m_data->columns.count();
+}
+
+QVariant KexiDataAwareObjectInterface::columnDefaultValue(int /*col*/) const
+{
+ return QVariant(0);
+//TODO(js)
+// return m_data->columns[col].defaultValue;
+}
+
+void KexiDataAwareObjectInterface::setAcceptsRowEditAfterCellAccepting(bool set)
+{
+ m_acceptsRowEditAfterCellAccepting = set;
+}
+
+void KexiDataAwareObjectInterface::setDropsAtRowEnabled(bool set)
+{
+// const bool old = d->dropsAtRowEnabled;
+ if (!set)
+ m_dragIndicatorLine = -1;
+ if (m_dropsAtRowEnabled && !set) {
+ m_dropsAtRowEnabled = false;
+// update();
+ updateWidgetContents();
+ }
+ else {
+ m_dropsAtRowEnabled = set;
+ }
+}
+
+void KexiDataAwareObjectInterface::setEmptyRowInsertingEnabled(bool set)
+{
+ m_emptyRowInsertingEnabled = set;
+ /*emit*/ reloadActions();
+}
+
+void KexiDataAwareObjectInterface::slotAboutToDeleteRow(KexiTableItem& item,
+ KexiDB::ResultInfo* /*result*/, bool repaint)
+{
+ if (repaint) {
+ m_rowWillBeDeleted = m_data->findRef(&item);
+ }
+}
+
+void KexiDataAwareObjectInterface::slotRowDeleted()
+{
+ if (m_rowWillBeDeleted >= 0) {
+ if (m_rowWillBeDeleted > 0 && m_rowWillBeDeleted >= (rows()-1) && !m_spreadSheetMode)
+ m_rowWillBeDeleted = rows()-1; //move up if it's the last row
+ updateWidgetContentsSize();
+
+ if (! (m_spreadSheetMode && m_rowWillBeDeleted>=(rows()-1)))
+ setCursorPosition(m_rowWillBeDeleted, m_curCol, true/*forceSet*/);
+ if (m_verticalHeader)
+ m_verticalHeader->removeLabel();
+
+ updateAllVisibleRowsBelow(m_curRow); //needed for KexiTableView
+
+ //update navigator's data
+ m_navPanel->setRecordCount(rows());
+
+ m_rowWillBeDeleted = -1;
+ }
+}
+
+bool KexiDataAwareObjectInterface::beforeDeleteItem(KexiTableItem *)
+{
+ //always return
+ return true;
+}
+
+bool KexiDataAwareObjectInterface::deleteItem(KexiTableItem *item)/*, bool moveCursor)*/
+{
+ if (!item || !beforeDeleteItem(item))
+ return false;
+
+ QString msg, desc;
+// bool current = (item == d->pCurrentItem);
+ const bool lastRowDeleted = m_spreadSheetMode && m_data->last() == item; //we need to know this so we
+ //can return to the last row
+ //after reinserting it
+ if (!m_data->deleteRow(*item, true /*repaint*/)) {
+ /*const int button =*/
+ showErrorMessageForResult( m_data->result() );
+// if (KMessageBox::No == button) {
+ //discard changes
+ // }
+ return false;
+ }
+ else {
+//setCursorPosition() wil lset this! if (current)
+ //d->pCurrentItem = m_data->current();
+ }
+
+// repaintAfterDelete();
+ if (m_spreadSheetMode) { //append empty row for spreadsheet mode
+ m_data->append(m_data->createItem());//new KexiTableItem(m_data->columns.count()));
+ if (m_verticalHeader)
+ m_verticalHeader->addLabels(1);
+ if (lastRowDeleted) //back to the last row
+ setCursorPosition(rows()-1, m_curCol, true/*forceSet*/);
+ /*emit*/ newItemAppendedForAfterDeletingInSpreadSheetMode();
+ }
+ return true;
+}
+
+KexiTableViewColumn* KexiDataAwareObjectInterface::column(int col)
+{
+ return m_data->column(col);
+}
+
+bool KexiDataAwareObjectInterface::hasDefaultValueAt(const KexiTableViewColumn& tvcol)
+{
+ if (m_rowEditing && m_data->rowEditBuffer() && m_data->rowEditBuffer()->isDBAware()) {
+ return m_data->rowEditBuffer()->hasDefaultValueAt( *tvcol.columnInfo );
+ }
+ return false;
+}
+
+const QVariant* KexiDataAwareObjectInterface::bufferedValueAt(int col, bool useDefaultValueIfPossible)
+{
+ if (m_rowEditing && m_data->rowEditBuffer())
+ {
+ KexiTableViewColumn* tvcol = column(col);
+ if (tvcol->isDBAware) {
+ //get the stored value
+ const int realFieldNumber = fieldNumberForColumn(col);
+ if (realFieldNumber < 0) {
+ kdWarning() << "KexiDataAwareObjectInterface::bufferedValueAt(): "
+ "fieldNumberForColumn(m_curCol) < 0" << endl;
+ return 0;
+ }
+ QVariant *storedValue = &m_currentItem->at( realFieldNumber );
+
+ //db-aware data: now, try to find a buffered value (or default one)
+ const QVariant *cv = m_data->rowEditBuffer()->at( *tvcol->columnInfo,
+ storedValue->isNull() && useDefaultValueIfPossible);
+ if (cv)
+ return cv;
+ return storedValue;
+ }
+ //not db-aware data:
+ const QVariant *cv = m_data->rowEditBuffer()->at( tvcol->field()->name() );
+ if (cv)
+ return cv;
+ }
+ //not db-aware data:
+ const int realFieldNumber = fieldNumberForColumn(col);
+ if (realFieldNumber < 0) {
+ kdWarning() << "KexiDataAwareObjectInterface::bufferedValueAt(): "
+ "fieldNumberForColumn(m_curCol) < 0" << endl;
+ return 0;
+ }
+ return &m_currentItem->at( realFieldNumber );
+}
+
+void KexiDataAwareObjectInterface::startEditOrToggleValue()
+{
+ if ( !isReadOnly() && columnEditable(m_curCol) ) {
+ if (columnType(m_curCol) == KexiDB::Field::Boolean) {
+ boolToggled();
+ }
+ else {
+ startEditCurrentCell();
+ return;
+ }
+ }
+}
+
+void KexiDataAwareObjectInterface::boolToggled()
+{
+ startEditCurrentCell();
+ if (m_editor) {
+ m_editor->clickedOnContents();
+ }
+ acceptEditor();
+ updateCell(m_curRow, m_curCol);
+
+/* int s = m_currentItem->at(m_curCol).toInt();
+ QVariant oldValue=m_currentItem->at(m_curCol);
+ (*m_currentItem)[m_curCol] = QVariant(s ? 0 : 1);
+ updateCell(m_curRow, m_curCol);
+// emit itemChanged(m_currentItem, m_curRow, m_curCol, oldValue);
+// emit itemChanged(m_currentItem, m_curRow, m_curCol);*/
+}
+
+void KexiDataAwareObjectInterface::slotDataDestroying()
+{
+ m_data = 0;
+ m_itemIterator = 0;
+}
+
+void KexiDataAwareObjectInterface::addNewRecordRequested()
+{
+ if (!isInsertingEnabled())
+ return;
+ if (m_rowEditing) {
+ if (!acceptRowEdit())
+ return;
+ }
+// setFocus();
+ selectRow(rows());
+ startEditCurrentCell();
+ if (m_editor)
+ m_editor->setFocus();
+}
+
+bool KexiDataAwareObjectInterface::handleKeyPress(QKeyEvent *e, int &curRow, int &curCol,
+ bool fullRowSelection, bool *moveToFirstField, bool *moveToLastField)
+{
+ if (moveToFirstField)
+ *moveToFirstField = false;
+ if (moveToLastField)
+ *moveToLastField = false;
+
+ const bool nobtn = e->state()==Qt::NoButton;
+ const int k = e->key();
+ //kdDebug() << "-----------" << e->state() << " " << k << endl;
+
+ if ((k == Qt::Key_Up && nobtn) || (k == Qt::Key_PageUp && e->state()==Qt::ControlButton)) {
+ selectPrevRow();
+ e->accept();
+ }
+ else if ((k == Qt::Key_Down && nobtn) || (k == Qt::Key_PageDown && e->state()==Qt::ControlButton)) {
+ selectNextRow();
+ e->accept();
+ }
+ else if (k == Qt::Key_PageUp && nobtn) {
+ selectPrevPage();
+ e->accept();
+ }
+ else if (k == Qt::Key_PageDown && nobtn) {
+ selectNextPage();
+ e->accept();
+ }
+ else if (k == Qt::Key_Home) {
+ if (fullRowSelection) {
+ //we're in row-selection mode: home key always moves to 1st row
+ curRow = 0;//to 1st row
+ }
+ else {//cell selection mode: different actions depending on ctrl and shift keys state
+ if (nobtn) {
+ curCol = 0;//to 1st col
+ }
+ else if (e->state()==Qt::ControlButton) {
+ curRow = 0;//to 1st row and col
+ curCol = 0;
+ }
+ else
+ return false;
+ }
+ if (moveToFirstField)
+ *moveToFirstField = true;
+ //do not accept yet
+ e->ignore();
+ }
+ else if (k == Qt::Key_End) {
+ if (fullRowSelection) {
+ //we're in row-selection mode: home key always moves to last row
+ curRow = m_data->count()-1+(isInsertingEnabled()?1:0);//to last row
+ }
+ else {//cell selection mode: different actions depending on ctrl and shift keys state
+ if (nobtn) {
+ curCol = columns()-1;//to last col
+ }
+ else if (e->state()==Qt::ControlButton) {
+ curRow = m_data->count()-1 /*+(isInsertingEnabled()?1:0)*/; //to last row and col
+ curCol = columns()-1;//to last col
+ }
+ else
+ return false;
+ }
+ if (moveToLastField)
+ *moveToLastField = true;
+ //do not accept yet
+ e->ignore();
+ }
+ else if (isInsertingEnabled() && (e->state()==Qt::ControlButton && k == Qt::Key_Equal
+ || e->state()==(Qt::ControlButton|Qt::ShiftButton) && k == Qt::Key_Equal)) {
+ curRow = m_data->count(); //to the new row
+ curCol = 0;//to first col
+ if (moveToFirstField)
+ *moveToFirstField = true;
+ //do not accept yet
+ e->ignore();
+ }
+ else
+ return false;
+
+ return true;
+}
+
+void KexiDataAwareObjectInterface::vScrollBarValueChanged(int v)
+{
+ Q_UNUSED(v);
+ if (!m_vScrollBarValueChanged_enabled)
+ return;
+
+ if (m_scrollbarToolTipsEnabled) {
+ const QRect r( verticalScrollBar()->sliderRect() );
+ const int row = lastVisibleRow()+1;
+ if (row<=0) {
+ m_scrollBarTipTimer.stop();
+ m_scrollBarTip->hide();
+ return;
+ }
+ m_scrollBarTip->setText( i18n("Row: ") + QString::number(row) );
+ m_scrollBarTip->adjustSize();
+ QWidget* thisWidget = dynamic_cast<QWidget*>(this);
+ m_scrollBarTip->move(
+ thisWidget->mapToGlobal( r.topLeft() + verticalScrollBar()->pos() )
+ + QPoint( - m_scrollBarTip->width()-5,
+ r.height()/2 - m_scrollBarTip->height()/2) );
+ if (verticalScrollBar()->draggingSlider()) {
+ kdDebug(44021) << " draggingSlider() " << endl;
+ m_scrollBarTipTimer.stop();
+ m_scrollBarTip->show();
+ m_scrollBarTip->raise();
+ }
+ else {
+ m_scrollBarTipTimerCnt++;
+ if (m_scrollBarTipTimerCnt>4) {
+ m_scrollBarTipTimerCnt=0;
+ m_scrollBarTip->show();
+ m_scrollBarTip->raise();
+ m_scrollBarTipTimer.start(500, true);
+ }
+ }
+ }
+ //update bottom view region
+/* if (m_navPanel && (contentsHeight() - contentsY() - clipper()->height()) <= QMAX(d->rowHeight,m_navPanel->height())) {
+ slotUpdate();
+ triggerUpdate();
+ }*/
+}
+
+bool KexiDataAwareObjectInterface::scrollbarToolTipsEnabled() const
+{
+ return m_scrollbarToolTipsEnabled;
+}
+
+void KexiDataAwareObjectInterface::setScrollbarToolTipsEnabled(bool set)
+{
+ m_scrollbarToolTipsEnabled = set;
+}
+
+void KexiDataAwareObjectInterface::vScrollBarSliderReleased()
+{
+ kdDebug(44021) << "vScrollBarSliderReleased()" << endl;
+ m_scrollBarTip->hide();
+}
+
+void KexiDataAwareObjectInterface::scrollBarTipTimeout()
+{
+ if (m_scrollBarTip->isVisible()) {
+// kdDebug(44021) << "TIMEOUT! - hide" << endl;
+ if (m_scrollBarTipTimerCnt>0) {
+ m_scrollBarTipTimerCnt=0;
+ m_scrollBarTipTimer.start(500, true);
+ return;
+ }
+ m_scrollBarTip->hide();
+ }
+ m_scrollBarTipTimerCnt=0;
+}
+
+void KexiDataAwareObjectInterface::focusOutEvent(QFocusEvent* e)
+{
+ Q_UNUSED(e);
+ m_scrollBarTipTimer.stop();
+ m_scrollBarTip->hide();
+
+ updateCell(m_curRow, m_curCol);
+}
+
+int KexiDataAwareObjectInterface::showErrorMessageForResult(KexiDB::ResultInfo* resultInfo)
+{
+ QWidget *thisWidget = dynamic_cast<QWidget*>(this);
+ if (resultInfo->allowToDiscardChanges) {
+ return KMessageBox::questionYesNo(thisWidget, resultInfo->msg
+ + (resultInfo->desc.isEmpty() ? QString::null : ("\n"+resultInfo->desc)),
+ QString::null,
+ KGuiItem(i18n("Correct Changes", "Correct"), QString::null, i18n("Correct changes")),
+ KGuiItem(i18n("Discard Changes")) );
+ }
+
+ if (resultInfo->desc.isEmpty())
+ KMessageBox::sorry(thisWidget, resultInfo->msg);
+ else
+ KMessageBox::detailedSorry(thisWidget, resultInfo->msg, resultInfo->desc);
+
+ return KMessageBox::Ok;
+}
+
+void KexiDataAwareObjectInterface::updateIndicesForVisibleValues()
+{
+ m_indicesForVisibleValues.resize( m_data ? m_data->columnsCount() : 0 );
+ if (!m_data)
+ return;
+ for (uint i=0; i < m_data->columnsCount(); i++) {
+ KexiTableViewColumn* tvCol = m_data->column(i);
+ if (tvCol->columnInfo && tvCol->columnInfo->indexForVisibleLookupValue()!=-1)
+ // retrieve visible value from lookup field
+ m_indicesForVisibleValues[ i ] = tvCol->columnInfo->indexForVisibleLookupValue();
+ else
+ m_indicesForVisibleValues[ i ] = i;
+ }
+}
+
+/*! Performs searching \a stringValue in \a where string.
+ \a matchAnyPartOfField, \a matchWholeField, \a wholeWordsOnly options are used to control how to search.
+
+ If \a matchWholeField is true, \a wholeWordsOnly is not checked.
+ \a firstCharacter is in/out parameter. If \a matchAnyPartOfField is true and \a matchWholeField is false,
+ \a firstCharacter >= 0, the search will be performed after skipping first \a firstCharacter characters.
+
+ If \a forward is false, we are searching backwart from \a firstCharacter position. \a firstCharacter == -1
+ means then the last character. \a firstCharacter == INT_MAX means "before first" place, so searching fails
+ immediately.
+ On success, true is returned and \a firstCharacter is set to position of the matched string. */
+static inline bool findInString(const QString& stringValue, int stringLength, const QString& where,
+ int& firstCharacter, bool matchAnyPartOfField, bool matchWholeField,
+ bool caseSensitive, bool wholeWordsOnly, bool forward)
+{
+ if (where.isEmpty()) {
+ firstCharacter = -1;
+ return false;
+ }
+
+ if (matchAnyPartOfField) {
+ if (forward) {
+ int pos = firstCharacter == -1 ? 0 : firstCharacter;
+ if (wholeWordsOnly) {
+ const int whereLength = where.length();
+ while (true) {
+ pos = where.find( stringValue, pos, caseSensitive );
+ if (pos == -1)
+ break;
+ if ((pos > 0 && where.at(pos-1).isLetterOrNumber())
+ ||((pos+stringLength-1) < (whereLength-1) && where.at(pos+stringLength-1+1).isLetterOrNumber()))
+ {
+ pos++; // invalid match because before or after the string there is non-white space
+ }
+ else
+ break;
+ }//while
+ firstCharacter = pos;
+ }
+ else {// !wholeWordsOnly
+ firstCharacter = where.find( stringValue, pos, caseSensitive );
+ }
+ return firstCharacter != -1;
+ }
+ else { // !matchAnyPartOfField
+ if (firstCharacter == INT_MAX) {
+ firstCharacter = -1; //next time we'll be looking at different cell
+ return false;
+ }
+ int pos = firstCharacter;
+ if (wholeWordsOnly) {
+ const int whereLength = where.length();
+ while (true) {
+ pos = where.findRev( stringValue, pos, caseSensitive );
+ if (pos == -1)
+ break;
+ if ((pos > 0 && where.at(pos-1).isLetterOrNumber())
+ ||((pos+stringLength-1) < (whereLength-1) && where.at(pos+stringLength-1+1).isLetterOrNumber()))
+ {
+ // invalid match because before or after the string there is non-white space
+ pos--;
+ if (pos < 0) // it can make pos < 0
+ break;
+ }
+ else
+ break;
+ }//while
+ firstCharacter = pos;
+ }
+ else {// !wholeWordsOnly
+ firstCharacter = where.findRev( stringValue, pos, caseSensitive );
+ }
+ return firstCharacter != -1;
+ }
+ }
+ else if (matchWholeField) {
+ if (firstCharacter != -1 && firstCharacter != 0) { //we're not at 0-th char
+ firstCharacter = -1;
+ }
+ else if ( (caseSensitive ? where : where.lower()) == stringValue) {
+ firstCharacter = 0;
+ return true;
+ }
+ }
+ else {// matchStartOfField
+ if (firstCharacter != -1 && firstCharacter != 0) { //we're not at 0-th char
+ firstCharacter = -1;
+ }
+ else if (where.startsWith(stringValue, caseSensitive)) {
+ if (wholeWordsOnly) {
+ // If where.length() < stringValue.length(), true will be returned too - fine.
+ return !where.at( stringValue.length() ).isLetterOrNumber();
+ }
+ firstCharacter = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+tristate KexiDataAwareObjectInterface::find(const QVariant& valueToFind,
+ const KexiSearchAndReplaceViewInterface::Options& options, bool next)
+{
+ if (!hasData())
+ return cancelled;
+ const QVariant prevSearchedValue( m_recentlySearchedValue );
+ m_recentlySearchedValue = valueToFind;
+ const KexiSearchAndReplaceViewInterface::Options::SearchDirection prevSearchDirection = m_recentSearchDirection;
+ m_recentSearchDirection = options.searchDirection;
+ if (valueToFind.isNull() || valueToFind.toString().isEmpty())
+ return cancelled;
+
+ const bool forward = (options.searchDirection == KexiSearchAndReplaceViewInterface::Options::SearchUp)
+ ? !next : next; //direction can be reversed
+
+ if ((!prevSearchedValue.isNull() && prevSearchedValue!=valueToFind)
+ || (prevSearchDirection!=options.searchDirection && options.searchDirection==KexiSearchAndReplaceViewInterface::Options::SearchAllRows))
+ {
+ // restart searching when value has been changed or new direction is SearchAllRows
+ m_positionOfRecentlyFoundValue.exists = false;
+ }
+
+ const bool startFrom1stRowAndCol = !m_positionOfRecentlyFoundValue.exists && next
+ && options.searchDirection == KexiSearchAndReplaceViewInterface::Options::SearchAllRows;
+ const bool startFromLastRowAndCol =
+ (!m_positionOfRecentlyFoundValue.exists && !next && options.searchDirection == KexiSearchAndReplaceViewInterface::Options::SearchAllRows)
+ ||(m_curRow >= rows() && !forward); //we're at "insert" row, and searching backwards: move to the last cell
+
+ if (!startFrom1stRowAndCol && !startFromLastRowAndCol && m_curRow >= rows()) {
+ //we're at "insert" row, and searching forward: no chances to find something
+ return false;
+ }
+ KexiTableViewData::Iterator it( (startFrom1stRowAndCol || startFromLastRowAndCol)
+ ? m_data->iterator() : *m_itemIterator /*start from the current cell*/ );
+ if (startFromLastRowAndCol)
+ it.toLast();
+ int firstCharacter;
+ if (m_positionOfRecentlyFoundValue.exists) {// start after the next/prev char position
+ if (forward)
+ firstCharacter = m_positionOfRecentlyFoundValue.lastCharacter + 1;
+ else {
+ firstCharacter = (m_positionOfRecentlyFoundValue.firstCharacter > 0) ?
+ (m_positionOfRecentlyFoundValue.firstCharacter - 1) : INT_MAX /* this means 'before first'*/;
+ }
+ }
+ else {
+ firstCharacter = -1; //forward ? -1 : INT_MAX;
+ }
+
+ const int columnsCount = m_data->columnsCount();
+ int row, col;
+ if (startFrom1stRowAndCol) {
+ row = 0;
+ col = 0;
+ }
+ else if (startFromLastRowAndCol) {
+ row = rows()-1;
+ col = columnsCount-1;
+ }
+ else {
+ row = m_curRow;
+ col = m_curCol;
+ }
+
+ //sache some flags for efficiency
+ const bool matchAnyPartOfField
+ = options.textMatching == KexiSearchAndReplaceViewInterface::Options::MatchAnyPartOfField;
+ const bool matchWholeField
+ = options.textMatching == KexiSearchAndReplaceViewInterface::Options::MatchWholeField;
+ const bool caseSensitive = options.caseSensitive;
+ const bool wholeWordsOnly = options.wholeWordsOnly;
+//unused const bool promptOnReplace = options.promptOnReplace;
+ int columnNumber = (options.columnNumber == KexiSearchAndReplaceViewInterface::Options::CurrentColumn)
+ ? m_curCol : options.columnNumber;
+ if (columnNumber>=0)
+ col = columnNumber;
+ const bool lookInAllColumns = columnNumber == KexiSearchAndReplaceViewInterface::Options::AllColumns;
+ int firstColumn; // real number of the first column, can be smaller than lastColumn if forward==true
+ int lastColumn; // real number of the last column
+ if (lookInAllColumns) {
+ firstColumn = forward ? 0 : columnsCount-1;
+ lastColumn = forward ? columnsCount-1 : 0;
+ }
+ else {
+ firstColumn = columnNumber;
+ lastColumn = columnNumber;
+ }
+ const QString stringValue( caseSensitive ? valueToFind.toString() : valueToFind.toString().lower() );
+ const int stringLength = stringValue.length();
+
+ // search
+ const int prevRow = m_curRow;
+ KexiTableItem *item;
+ while ( (item = it.current()) ) {
+ for (; forward ? col <= lastColumn : col >= lastColumn;
+ col = forward ? (col+1) : (col-1))
+ {
+ const QVariant cell( item->at( m_indicesForVisibleValues[ col ] ) );
+ if (findInString(stringValue, stringLength, cell.toString(), firstCharacter,
+ matchAnyPartOfField, matchWholeField, caseSensitive, wholeWordsOnly, forward))
+ {
+ //*m_itemIterator = it;
+ //m_currentItem = *it;
+ //m_curRow = row;
+ //m_curCol = col;
+ setCursorPosition(row, col, true/*forceSet*/);
+ if (prevRow != m_curRow)
+ updateRow(prevRow);
+ // remember the exact position for the found value
+ m_positionOfRecentlyFoundValue.exists = true;
+ m_positionOfRecentlyFoundValue.firstCharacter = firstCharacter;
+//! @todo for regexp lastCharacter should be computed
+ m_positionOfRecentlyFoundValue.lastCharacter = firstCharacter + stringLength - 1;
+ return true;
+ }
+ }//for
+ if (forward) {
+ ++it;
+ ++row;
+ }
+ else {
+ --it;
+ --row;
+ }
+ col = firstColumn;
+ }//while
+ return false;
+}
+
+tristate KexiDataAwareObjectInterface::findNextAndReplace(
+ const QVariant& valueToFind, const QVariant& replacement,
+ const KexiSearchAndReplaceViewInterface::Options& options, bool replaceAll)
+{
+ Q_UNUSED(replacement);
+ Q_UNUSED(options);
+ Q_UNUSED(replaceAll);
+
+ if (isReadOnly())
+ return cancelled;
+ if (valueToFind.isNull() || valueToFind.toString().isEmpty())
+ return cancelled;
+ //! @todo implement KexiDataAwareObjectInterface::findAndReplace()
+ return false;
+}
diff --git a/kexi/widget/tableview/kexidataawareobjectiface.h b/kexi/widget/tableview/kexidataawareobjectiface.h
new file mode 100644
index 00000000..4cf2aa6a
--- /dev/null
+++ b/kexi/widget/tableview/kexidataawareobjectiface.h
@@ -0,0 +1,918 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ Based on KexiTableView code.
+ Copyright (C) 2002 Till Busch <till@bux.at>
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
+
+ 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 KEXIDATAAWAREOBJECTINTERFACE_H
+#define KEXIDATAAWAREOBJECTINTERFACE_H
+
+#include <qguardedptr.h>
+
+#include <qtimer.h>
+#include <kdebug.h>
+#include <widget/utils/kexiarrowtip.h>
+#include <kexisearchandreplaceiface.h>
+#include "kexitableviewdata.h"
+
+class QObject;
+class QScrollBar;
+class KPopupMenu;
+class KexiTableItem;
+class KexiTableViewData;
+class KexiRecordMarker;
+class KexiTableViewHeader;
+class KexiRecordNavigator;
+#include <kexidataiteminterface.h>
+
+namespace KexiDB {
+ class RowEditBuffer;
+}
+
+//! default column width in pixels
+#define KEXI_DEFAULT_DATA_COLUMN_WIDTH 120
+
+//! \brief The KexiDataAwareObjectInterface is an interface for record-based data object.
+/** This interface is implemented by KexiTableView and KexiFormView
+ and used by KexiDataAwareView. If yu're implementing this interface,
+ add KEXI_DATAAWAREOBJECTINTERFACE convenience macro just after Q_OBJECT.
+
+ You should add following code to your destructor so data is deleted:
+ \code
+ if (m_owner)
+ delete m_data;
+ m_data = 0;
+ \endcode
+ This is not performed in KexiDataAwareObjectInterface because you may need
+ to access m_data in your desctructor.
+*/
+class KEXIDATATABLE_EXPORT KexiDataAwareObjectInterface
+{
+ public:
+ KexiDataAwareObjectInterface();
+ virtual ~KexiDataAwareObjectInterface();
+
+ /*! Sets data for this object. if \a owner is true, the object will own
+ \a data and therefore will be destroyed when needed, else: \a data is (possibly) shared and
+ not owned by the widget.
+ If widget already has _different_ data object assigned (and owns this data),
+ old data is destroyed before new assignment.
+ */
+ void setData( KexiTableViewData *data, bool owner = true );
+
+ /*! \return data structure displayed for this object */
+ inline KexiTableViewData *data() const { return m_data; }
+
+ /*! \return currently selected column number or -1. */
+ inline int currentColumn() const { return m_curCol; }
+
+ /*! \return currently selected row number or -1. */
+ inline int currentRow() const { return m_curRow; }
+
+ /*! \return last row visible on the screen (counting from 0).
+ The returned value is guaranteed to be smaller or equal to currentRow() or -1
+ if there are no rows. */
+ virtual int lastVisibleRow() const = 0;
+
+ /*! \return currently selected item (row data) or null. */
+ KexiTableItem *selectedItem() const { return m_currentItem; }
+
+ /*! \return number of rows in this view. */
+ int rows() const;
+
+ /*! \return number of visible columns in this view.
+ By default returns dataColumns(), what is proper table view.
+ In case of form view, there can be a number of duplicated columns defined
+ (data-aware widgets, see KexiFormScrollView::columns()),
+ so columns() can return greater number than dataColumns(). */
+ virtual int columns() const { return dataColumns(); }
+
+ /*! Helper function.
+ \return number of columns of data. */
+ int dataColumns() const;
+
+ /*! \return true if data represented by this object
+ is not editable (it can be editable with other ways although,
+ outside of this object). */
+ virtual bool isReadOnly() const;
+
+ /*! Sets readOnly flag for this object.
+ Unless the flag is set, the widget inherits readOnly flag from it's data
+ structure assigned with setData(). The default value if false.
+
+ This method is useful when you need to switch on the flag indepentently
+ from the data structure.
+ Note: it is not allowed to force readOnly off
+ when internal data is readOnly - in that case the method does nothing.
+ You can check internal data flag calling data()->isReadOnly().
+
+ If \a set is true, insertingEnabled flag will be cleared automatically.
+ \sa isInsertingEnabled()
+ */
+ void setReadOnly(bool set);
+
+ /*! \return true if sorting is enabled. */
+ inline bool isSortingEnabled() const { return m_isSortingEnabled; }
+
+ /*! Sets sorting on column \a col, or (when \a col == -1) sets rows unsorted
+ this will do not work if sorting is disabled with setSortingEnabled() */
+ virtual void setSorting(int col, bool ascending=true);
+
+ /*! Enables or disables sorting for this object
+ This method is different that setSorting() because it prevents both user
+ and programmer from sorting by clicking a column's header or calling setSorting().
+ By default sorting is enabled.
+ */
+ virtual void setSortingEnabled(bool set);
+
+ /*! \return sorted column number or -1 if no column is sorted within data.
+ This does not mean that any sorting has been performed within GUI of this object,
+ because the data could be changed in the meantime outside of this GUI object. */
+ int dataSortedColumn() const;
+
+ /*! \return 1 if ascending order for data sorting data is set, -1 for descending,
+ 0 for no sorting.
+ This does not mean that any sorting has been performed within GUI of this objetct,
+ because the data could be changed in the meantime outside of this GUI object.
+ */
+ int dataSortingOrder() const;
+
+ /*! Sorts all rows by column selected with setSorting().
+ If there is currently row edited, it is accepted.
+ If acception failed, sort() will return false.
+ \return true on success. */
+ virtual bool sort();
+
+ /*! Sorts currently selected column in ascending order.
+ This slot is used typically for "data_sort_az" action. */
+ void sortAscending();
+
+ /*! Sorts currently selected column in descending order.
+ This slot is used typically for "data_sort_za" action. */
+ void sortDescending();
+
+ /*! \return true if data inserting is enabled (the default). */
+ virtual bool isInsertingEnabled() const;
+
+ /*! Sets insertingEnabled flag. If true, empty row is available
+ at the end of this widget for new entering new data.
+ Unless the flag is set, the widget inherits insertingEnabled flag from it's data
+ structure assigned with setData(). The default value if false.
+
+ Note: it is not allowed to force insertingEnabled on when internal data
+ has insertingEnabled set off - in that case the method does nothing.
+ You can check internal data flag calling data()->insertingEnabled().
+
+ Setting this flag to true will have no effect if read-only flag is true.
+ \sa setReadOnly()
+ */
+ void setInsertingEnabled(bool set);
+
+ /*! \return true if row deleting is enabled.
+ Equal to deletionPolicy() != NoDelete && !isReadOnly()). */
+ bool isDeleteEnabled() const;
+
+ /*! \return true if inserting empty rows are enabled (false by default).
+ Mostly usable for not db-aware objects (e.g. used in Kexi Alter Table).
+ Note, that if inserting is disabled, or the data set is read-only,
+ this flag will be ignored. */
+ bool isEmptyRowInsertingEnabled() const { return m_emptyRowInsertingEnabled; }
+
+ /*! Sets emptyRowInserting flag.
+ Note, that if inserting is disabled, this flag is ignored. */
+ void setEmptyRowInsertingEnabled(bool set);
+
+ /*! Enables or disables filtering. Filtering is enabled by default. */
+ virtual void setFilteringEnabled(bool set);
+
+ /*! \return true if filtering is enabled. */
+ inline bool isFilteringEnabled() const { return m_isFilteringEnabled; }
+
+ /*! Added for convenience: configure this object
+ to behave more like spreadsheet (it's used for things like alter-table view).
+ - hides navigator
+ - disables sorting, inserting and filtering
+ - enables accepting row after cell accepting; see setAcceptsRowEditAfterCellAccepting()
+ - enables inserting empty row; see setEmptyRowInsertingEnabled() */
+ virtual void setSpreadSheetMode();
+
+ /*! \return true id "spreadSheetMode" is enabled. It's false by default. */
+ bool spreadSheetMode() const { return m_spreadSheetMode; }
+
+ /*! \return true if currently selected row is edited. */
+ inline bool rowEditing() const { return m_rowEditing; }
+
+ enum DeletionPolicy
+ {
+ NoDelete = 0,
+ AskDelete = 1,
+ ImmediateDelete = 2,
+ SignalDelete = 3
+ };
+
+ /*! \return deletion policy for this object.
+ The default (after allocating) is AskDelete. */
+ DeletionPolicy deletionPolicy() const { return m_deletionPolicy; }
+
+ virtual void setDeletionPolicy(DeletionPolicy policy);
+
+ /*! Deletes currently selected record; does nothing if no record
+ is currently selected. If record is in edit mode, editing
+ is cancelled before deleting. */
+ virtual void deleteCurrentRow();
+
+ /*! Inserts one empty row above row \a row. If \a row is -1 (the default),
+ new row is inserted above the current row (or above 1st row if there is no current).
+ A new item becomes current if row is -1 or if row is equal currentRow().
+ This method does nothing if:
+ -inserting flag is disabled (see isInsertingEnabled())
+ -read-only flag is set (see isReadOnly())
+ \ return inserted row's data
+ */
+ virtual KexiTableItem *insertEmptyRow(int row = -1);
+
+ /*! For reimplementation: called by deleteItem(). If returns false, deleting is aborted.
+ Default implementation just returns true. */
+ virtual bool beforeDeleteItem(KexiTableItem *item);
+
+ /*! Deletes \a item. Used by deleteCurrentRow(). Calls beforeDeleteItem() before deleting,
+ to double-check if deleting is allowed.
+ \return true on success. */
+ bool deleteItem(KexiTableItem *item);//, bool moveCursor=true);
+
+ /*! Inserts newItem at \a row. -1 means current row. Used by insertEmptyRow(). */
+ void insertItem(KexiTableItem *newItem, int row = -1);
+
+ /*! Clears entire table data, its visible representation
+ and deletes data at database backend (if this is db-aware object).
+ Does not clear columns information.
+ Does not destroy KexiTableViewData object (if present) but only clears its contents.
+ Displays confirmation dialog if \a ask is true (the default is false).
+ Repaints widget if \a repaint is true (the default).
+ For empty tables, true is returned immediately.
+ If isDeleteEnabled() is false, false is returned.
+ For spreadsheet mode all current rows are just replaced by empty rows.
+ \return true on success, false on failure, and cancelled if user cancelled deletion
+ (only possible if \a ask is true).
+ */
+ tristate deleteAllRows(bool ask = false, bool repaint = true);
+
+ /*! \return maximum number of rows that can be displayed per one "page"
+ for current view's size. */
+ virtual int rowsPerPage() const = 0;
+
+ virtual void selectRow(int row);
+ virtual void selectNextRow();
+ virtual void selectPrevRow();
+ virtual void selectNextPage(); //!< page down action
+ virtual void selectPrevPage(); //!< page up action
+ virtual void selectFirstRow();
+ virtual void selectLastRow();
+ virtual void addNewRecordRequested();
+
+ /*! Clears current selection. Current row and column will be now unspecified:
+ currentRow(), currentColumn() will return -1, and selectedItem() will return null. */
+ virtual void clearSelection();
+
+ /*! Moves cursor to \a row and \a col. If \a col is -1, current column number is used.
+ If forceSet is true, cursor position is updated even if \a row and \a col doesn't
+ differ from actual position. */
+ virtual void setCursorPosition(int row, int col = -1, bool forceSet = false);
+
+ /*! Ensures that cell at \a row and \a col is visible.
+ If \a col is -1, current column number is used. \a row and \a col (if not -1) must
+ be between 0 and rows() (or cols() accordingly). */
+ virtual void ensureCellVisible(int row, int col/*=-1*/) = 0;
+
+ /*! Specifies, if this object automatically accepts
+ row editing (using acceptRowEdit()) on accepting any cell's edit
+ (i.e. after acceptEditor()). \sa acceptsRowEditAfterCellAccepting() */
+ virtual void setAcceptsRowEditAfterCellAccepting(bool set);
+
+ /*! \return true, if this object automatically accepts
+ row editing (using acceptRowEdit()) on accepting any cell's edit
+ (i.e. after acceptEditor()).
+ By default this flag is set to false.
+ Not that if the query for this table has given constraints defined,
+ like NOT NULL / NOT EMPTY for more than one field - editing a record would
+ be impossible for the flag set to true, because of constraints violation.
+ However, setting this flag to true can be useful especially for not-db-aware
+ data set (it's used e.g. in Kexi Alter Table's field editor). */
+ bool acceptsRowEditAfterCellAccepting() const { return m_acceptsRowEditAfterCellAccepting; }
+
+ /*! \return true, if this table accepts dropping data on the rows. */
+ bool dropsAtRowEnabled() const { return m_dropsAtRowEnabled; }
+
+ /*! Specifies, if this table accepts dropping data on the rows.
+ If enabled:
+ - dragging over row is indicated by drawing a line at bottom side of this row
+ - dragOverRow() signal will be emitted on dragging,
+ -droppedAtRow() will be emitted on dropping
+ By default this flag is set to false. */
+ virtual void setDropsAtRowEnabled(bool set);
+
+ /*! \return currently used data (field/cell) editor or 0 if there is no data editing. */
+ inline KexiDataItemInterface *editor() const { return m_editor; }
+
+ /*! Cancels row editing All changes made to the editing
+ row during this current session will be undone.
+ \return true on success or false on failure (e.g. when editor does not exist) */
+ virtual bool cancelRowEdit();
+
+ /*! Accepts row editing. All changes made to the editing
+ row during this current session will be accepted (saved).
+ \return true if accepting was successful, false otherwise
+ (e.g. when current row contain data that does not meet given constraints). */
+ virtual bool acceptRowEdit();
+
+ virtual void removeEditor();
+
+ /*! Cancels changes made to the currently active editor.
+ Reverts the editor's value to old one.
+ \return true on success or false on failure (e.g. when editor does not exist) */
+ virtual bool cancelEditor();
+
+ //! Accepst changes made to the currently active editor.
+ //! \return true on success or false on failure (e.g. when editor does not exist or there is data validation error)
+ virtual bool acceptEditor();
+
+ //! Creates editors and shows it, what usually means the beginning of a cell editing
+ virtual void createEditor(int row, int col, const QString& addText = QString::null,
+ bool removeOld = false) = 0;
+
+ /*! Used when Return key is pressed on cell, the cell has been double clicked
+ or "+" navigator's button is clicked.
+ Also used when we want to continue editing a cell after "invalid value" message
+ was displayed (in this case, \a setText is usually not empty, what means
+ that text will be set in the cell replacing previous value).
+ */
+ virtual void startEditCurrentCell(const QString& setText = QString::null);
+
+ /*! Deletes currently selected cell's contents, if allowed.
+ In most cases delete is not accepted immediately but "row editing" mode is just started. */
+ virtual void deleteAndStartEditCurrentCell();
+
+ inline KexiTableItem *itemAt(int row) const;
+
+ /*! \return column information for column number \a col.
+ Default implementation just returns column # col,
+ but for Kexi Forms column data
+ corresponding to widget number is used here
+ (see KexiFormScrollView::fieldNumberForColumn()). */
+ virtual KexiTableViewColumn* column(int col);
+
+ /*! \return field number within data model connected to a data-aware
+ widget at column \a col. Can return -1 if there's no such column. */
+ virtual int fieldNumberForColumn(int col) { return col; }
+
+ bool hasDefaultValueAt(const KexiTableViewColumn& tvcol);
+
+ const QVariant* bufferedValueAt(int col, bool useDefaultValueIfPossible = true);
+
+ //! \return a type of column \a col - one of KexiDB::Field::Type
+ int columnType(int col);
+
+ //! \return default value for column \a col
+ QVariant columnDefaultValue(int col) const;
+
+ /*! \return true is column \a col is editable.
+ Default implementation takes information about 'readOnly' flag from data member.
+ Within forms, this is reimplemented for checking 'readOnly' flag from a widget
+ ('readOnly' flag from data member is still checked though).
+ */
+ virtual bool columnEditable(int col);
+
+ /*! Redraws the current cell. To be implemented. */
+ virtual void updateCurrentCell() = 0;
+
+ inline KexiRecordMarker* verticalHeader() const { return m_verticalHeader; }
+
+ //! signals
+ virtual void itemChanged(KexiTableItem *, int row, int col) = 0;
+ virtual void itemChanged(KexiTableItem *, int row, int col, QVariant oldValue) = 0;
+ virtual void itemDeleteRequest(KexiTableItem *, int row, int col) = 0;
+ virtual void currentItemDeleteRequest() = 0;
+ //! Emitted for spreadsheet mode when an item was deleted and a new item has been appended
+ virtual void newItemAppendedForAfterDeletingInSpreadSheetMode() = 0;
+
+ /*! Data has been refreshed on-screen - emitted from initDataContents(). */
+ virtual void dataRefreshed() = 0;
+ virtual void dataSet( KexiTableViewData *data ) = 0;
+
+ /*! \return a pointer to context menu. This can be used to plug some actions there. */
+ KPopupMenu* contextMenu() const { return m_popupMenu; }
+
+ /*! \return true if the context menu is enabled (visible) for the view.
+ True by default. */
+ bool contextMenuEnabled() const { return m_contextMenuEnabled; }
+
+ /*! Enables or disables the context menu for the view. */
+ void setContextMenuEnabled(bool set) { m_contextMenuEnabled = set; }
+
+ /*! \return true if vertical scrollbar's tooltips are enabled (true by default). */
+ bool scrollbarToolTipsEnabled() const;
+
+ /*! Enables or disables vertical scrollbar's tooltip. */
+ void setScrollbarToolTipsEnabled(bool set);
+
+ /*! Typically handles pressing Enter or F2 key:
+ if current cell has boolean type, toggles it's value,
+ otherwise starts editing (startEditCurrentCell()). */
+ void startEditOrToggleValue();
+
+ /*! \return true if new row is edited; implies: rowEditing==true. */
+ inline bool newRowEditing() const { return m_newRowEditing; }
+
+ /*! Reaction on toggling a boolean value of a cell:
+ we're starting to edit the cell and inverting it's state. */
+ virtual void boolToggled();
+
+ virtual void connectCellSelectedSignal(const QObject* receiver,
+ const char* intIntMember) = 0;
+
+ virtual void connectRowEditStartedSignal(const QObject* receiver,
+ const char* intMember) = 0;
+
+ virtual void connectRowEditTerminatedSignal(const QObject* receiver,
+ const char* voidMember) = 0;
+
+ virtual void connectReloadActionsSignal(const QObject* receiver,
+ const char* voidMember) = 0;
+
+ virtual void connectDataSetSignal(const QObject* receiver,
+ const char* kexiTableViewDataMember) = 0;
+
+ virtual void connectToReloadDataSlot(const QObject* sender,
+ const char* voidSignal) = 0;
+
+ virtual void slotDataDestroying();
+
+ //! Copy current selection to a clipboard (e.g. cell)
+ virtual void copySelection() = 0;
+
+ //! Cut current selection to a clipboard (e.g. cell)
+ virtual void cutSelection() = 0;
+
+ //! Paste current clipboard contents (e.g. to a cell)
+ virtual void paste() = 0;
+
+ /*! Finds \a valueToFind within the data items
+ \a options are used to control the process. Selection is moved to found value.
+ If \a next is true, "find next" is performed, else "find previous" is performed.
+
+ Searching behaviour also depends on status of the previous search: for every search,
+ position of the cells containing the found value is stored internally
+ by the data-aware interface (not in options).
+ Moreover, position (start, end) of the found value is also stored.
+ Thus, the subsequent search will reuse this information to be able to start
+ searching exactly after the previously found value (or before for "find previous" option).
+ The flags can be zeroed, what will lead to seaching from the first character
+ of the current item (cell).
+
+ \return true if value has been found, false if value has not been found,
+ and cancelled if there is nothing to find or there is no data to search in. */
+ virtual tristate find(const QVariant& valueToFind,
+ const KexiSearchAndReplaceViewInterface::Options& options, bool next);
+
+ /*! Finds \a valueToFind within the data items and replaces with \a replacement
+ \a options are used to control the process.
+ \return true if value has been found and replaced, false if value
+ has not been found and replaced, and cancelled if there is nothing
+ to find or there is no data to search in or the data is read only.
+ If \a replaceAll is true, all found values are replaced. */
+ virtual tristate findNextAndReplace(const QVariant& valueToFind,
+ const QVariant& replacement,
+ const KexiSearchAndReplaceViewInterface::Options& options, bool replaceAll);
+
+ /*! \return vertical scrollbar */
+ virtual QScrollBar* verticalScrollBar() const = 0;
+
+ /*! Used in KexiTableView::keyPressEvent() (and in continuous forms).
+ \return true when the key press event \e was consumed.
+ You should also check e->isAccepted(), if it's true, nothing should be done;
+ if it is false, you should call setCursorPosition() for the altered \a curCol
+ and \c curRow variables.
+
+ If \a moveToFirstField is not 0, *moveToFirstField will be set to true
+ when the cursor should be moved to the first field (in tab order) and to false otherwise.
+ If \a moveToLastField is not 0, *moveToLastField will be set to true
+ when the cursor should be moved to the last field (in tab order) and to false otherwise.
+ Note for forms: if moveToFirstField and moveToLastField are not 0,
+ \a curCol is altered after calling this method, so setCursorPosition() will set to
+ the index of an appropriate column (field). This is needed because field widgets can be
+ inserted and ordered in custom tab order, so the very first field in the data source
+ can be other than the very first field in the form.
+
+ Used by KexiTableView::keyPressEvent() and KexiTableView::keyPressEvent(). */
+ virtual bool handleKeyPress(QKeyEvent *e, int &curRow, int &curCol, bool fullRowSelection,
+ bool *moveToFirstField = 0, bool *moveToLastField = 0);
+
+ protected:
+ /*! Reimplementation for KexiDataAwareObjectInterface.
+ Initializes data contents (resizes it, sets cursor at 1st row).
+ Sets record count for record navigator.
+ Sets cursor positin (using setCursorPosition()) to first row or sets
+ (-1, -1) position if no rows are available.
+ Called on setData(). Also called once on show event after
+ refreshRequested() signal was received from KexiTableViewData object. */
+ virtual void initDataContents();
+
+ /*! Clears columns information and thus all internal table data
+ and its visible representation. Repaints widget if \a repaint is true. */
+ virtual void clearColumns(bool repaint = true);
+
+ /*! Called by clearColumns() to clear internals of the object.
+ For example, KexiTableView removes contents of it's horizontal header. */
+ virtual void clearColumnsInternal(bool repaint) = 0;
+
+ /*! @internal for implementation
+ This should append another section within horizontal header or any sort of caption
+ for a field using provided names. \a width is a hint for new field's width. */
+ virtual void addHeaderColumn(const QString& caption, const QString& description,
+ const QIconSet& icon, int size) = 0;
+
+ /*! @internal for implementation
+ \return sorting order (within GUI): -1: descending, 1: ascending, 0: no sorting.
+ This does not mean that any sorting has been performed within GUI of this object,
+ because the data could be changed in the meantime outside of this GUI object.
+ @see dataSortingOrder()*/
+ virtual int currentLocalSortingOrder() const = 0;
+
+ /*! @internal for implementation
+ \return sorted column number for this widget or -1 if no column
+ is sorted witin GUI.
+ This does not mean that the same sorting is performed within data member
+ which is used by this widget, because the data could be changed in the meantime
+ outside of this GUI widget.
+ @see dataSortedColumn() */
+ virtual int currentLocalSortColumn() const = 0;
+
+ /*! @internal for implementation
+ Shows sorting indicator order within GUI: -1: descending, 1: ascending,
+ 0: no sorting. This should not perform any sorting within data member
+ which is used by this object.
+ col = -1 should mean "no sorting" as well. */
+ virtual void setLocalSortingOrder(int col, int order) = 0;
+
+ /*! @internal Sets order for \a column: -1: descending, 1: ascending,
+ 0: invert order */
+ virtual void sortColumnInternal(int col, int order = 0);
+
+ /*! @internal for implementation
+ Updates GUI after sorting.
+ After sorting you need to ensure current row and column
+ is visible to avoid user confusion. For exaple, in KexiTableView
+ implementation, current cell is centered (if possible)
+ and updateContents() is called. */
+ virtual void updateGUIAfterSorting() = 0;
+
+ /*! Emitted in initActions() to force reload actions
+ You should remove existing actions and add them again.
+ Define and emit reloadActions() signal here. */
+ virtual void reloadActions() = 0;
+
+ /*! Reloads data for this object. */
+ virtual void reloadData();
+
+ /*! for implementation as a signal */
+ virtual void itemSelected(KexiTableItem *) = 0;
+
+ /*! for implementation as a signal */
+ virtual void cellSelected(int col, int row) = 0;
+
+ /*! for implementation as a signal */
+ virtual void sortedColumnChanged(int col) = 0;
+
+ /*! for implementation as a signal */
+ virtual void rowEditTerminated(int row) = 0;
+
+ /*! Clear temporary members like the pointer to current editor.
+ If you reimplement this method, don't forget to call this one. */
+ virtual void clearVariables();
+
+ /*! @internal
+ Creates editor structure without filling it with data.
+ Used in createEditor() and few places to be able to display cell contents
+ dependending on its type. If \a ignoreMissingEditor is false (the default),
+ and editor cannot be instantiated, current row editing (if present) is cancelled.
+ */
+ virtual KexiDataItemInterface *editor( int col, bool ignoreMissingEditor = false ) = 0;
+
+ /*! Updates editor's position, size and shows its focus (not the editor!)
+ for \a row and \a col, using editor(). Does nothing if editor not found. */
+ virtual void editorShowFocus( int row, int col ) = 0;
+
+ /*! Redraws specified cell. */
+ virtual void updateCell(int row, int col) = 0;
+
+ /*! Redraws all cells of specified row. */
+ virtual void updateRow(int row) = 0;
+
+ /*! Updates contents of the widget. Just call update() here on your widget. */
+ virtual void updateWidgetContents() = 0;
+
+ /*! Updates widget's contents size e.g. using QScrollView::resizeContents(). */
+ virtual void updateWidgetContentsSize() = 0;
+
+ /*! Updates scrollbars of the widget.
+ QScrollView::updateScrollbars() will be usually called here. */
+ virtual void updateWidgetScrollBars() = 0;
+
+ /*! @internal
+ Updates row appearance after canceling row edit.
+ Used by cancelRowEdit(). By default just calls updateRow(m_curRow).
+ Reimplemented by KexiFormScrollView. */
+ virtual void updateAfterCancelRowEdit();
+
+ /*! @internal
+ Updates row appearance after accepting row edit.
+ Used by acceptRowEdit(). By default just calls updateRow(m_curRow).
+ Reimplemented by KexiFormScrollView. */
+ virtual void updateAfterAcceptRowEdit();
+
+ //! Handles KexiTableViewData::rowRepaintRequested() signal
+ virtual void slotRowRepaintRequested(KexiTableItem& item) { Q_UNUSED( item ); }
+
+ //! Handles KexiTableViewData::aboutToDeleteRow() signal. Prepares info for slotRowDeleted().
+ virtual void slotAboutToDeleteRow(KexiTableItem& item, KexiDB::ResultInfo* result,
+ bool repaint);
+
+ //! Handles KexiTableViewData::rowDeleted() signal to repaint when needed.
+ virtual void slotRowDeleted();
+
+ //! Handles KexiTableViewData::rowInserted() signal to repaint when needed.
+ virtual void slotRowInserted(KexiTableItem *item, bool repaint);
+
+ //! Like above, not db-aware version
+ virtual void slotRowInserted(KexiTableItem *item, uint row, bool repaint);
+
+ virtual void slotRowsDeleted( const QValueList<int> & ) {}
+
+ //! for sanity checks (return true if m_data is present; else: outputs warning)
+ inline bool hasData() const;
+
+ /*! Only needed for forms: called by KexiDataAwareObjectInterface::setCursorPosition()
+ if cursor's position is really changed. */
+ virtual void selectCellInternal() {}
+
+ /*! Used in KexiDataAwareObjectInterface::slotRowDeleted()
+ to repaint tow \a row and all visible below.
+ Implemented if there is more than one row displayed, i.e. currently for KexiTableView. */
+ virtual void updateAllVisibleRowsBelow(int row) { Q_UNUSED( row ); }
+
+ //! Call this from the subclass. */
+ virtual void focusOutEvent(QFocusEvent* e);
+
+ /*! Handles verticalScrollBar()'s valueChanged(int) signal.
+ Called when vscrollbar's value has been changed.
+ Call this method from the subclass. */
+ virtual void vScrollBarValueChanged(int v);
+
+ /*! Handles sliderReleased() signal of the verticalScrollBar(). Used to hide the "row number" tooltip. */
+ virtual void vScrollBarSliderReleased();
+
+ /*! Handles timeout() signal of the m_scrollBarTipTimer. If the tooltip is visible,
+ m_scrollBarTipTimerCnt is set to 0 and m_scrollBarTipTimerCnt is restarted;
+ else the m_scrollBarTipTimerCnt is just set to 0.*/
+ virtual void scrollBarTipTimeout();
+
+ /*! Shows error message box suitable for \a resultInfo. This can be "sorry" or "detailedSorry"
+ message box or "queryYesNo" if resultInfo->allowToDiscardChanges is true.
+ \return code of button clicked: KMessageBox::Ok in case of "sorry" or "detailedSorry" messages
+ and KMessageBox::Yes or KMessageBox::No in case of "queryYesNo" message. */
+ int showErrorMessageForResult(KexiDB::ResultInfo* resultInfo);
+
+ /*! Prepares array of indices of visible values to search within.
+ This is per-interface global cache.
+ Needed for faster lookup because there could be lookup values.
+ Called whenever columns definition changes, i.e. in setData() and clearColumns().
+ @see find() */
+ void updateIndicesForVisibleValues();
+
+ //! data structure displayed for this object
+ KexiTableViewData *m_data;
+
+ //! true if m_data member is owned by this object
+ bool m_owner : 1;
+
+ //! cursor position
+ int m_curRow, m_curCol;
+
+ //! current data item
+ KexiTableItem *m_currentItem;
+
+ //! data item's iterator
+ KexiTableViewData::Iterator *m_itemIterator;
+
+ //! item data for inserting
+ KexiTableItem *m_insertItem;
+
+ //! when (current or new) row is edited - changed field values are temporary stored here
+// KexiDB::RowEditBuffer *m_rowEditBuffer;
+
+ /*! true if currently selected row is edited */
+ bool m_rowEditing : 1;
+
+ /*! true if new row is edited; implies: rowEditing==true. */
+ bool m_newRowEditing : 1;
+
+ /*! 'sorting by column' availability flag for widget */
+ bool m_isSortingEnabled : 1;
+
+ /*! true if filtering is enabled for the view. */
+ bool m_isFilteringEnabled : 1;
+
+ /*! Public version of 'acceptsRowEditAfterCellAcceptin' flag (available for a user).
+ It's OR'es together with above flag.
+ */
+ bool m_acceptsRowEditAfterCellAccepting : 1;
+
+ /*! Used in acceptEditor() to avoid infinite recursion,
+ eg. when we're calling acceptRowEdit() during cell accepting phase. */
+ bool m_inside_acceptEditor : 1;
+
+ /*! @internal if true, this object automatically accepts
+ row editing (using acceptRowEdit()) on accepting any cell's edit
+ (i.e. after acceptEditor()). */
+ bool m_internal_acceptsRowEditAfterCellAccepting : 1;
+
+ /*! true, if inserting empty rows are enabled (false by default) */
+ bool m_emptyRowInsertingEnabled : 1;
+
+ /*! Contains 1 if the object is readOnly, 0 if not;
+ otherwise (-1 means "do not know") the 'readOnly' flag from object's
+ internal data structure (KexiTableViewData *KexiTableView::m_data) is reused.
+ */
+ int m_readOnly;
+
+//! @todo really keep this here and not in KexiTableView?
+ /*! true if currently double click action was is performed
+ (so accept/cancel editor shoudn't be executed) */
+ bool m_contentsMousePressEvent_dblClick : 1;
+
+ /*! like for readOnly: 1 if inserting is enabled */
+ int m_insertingEnabled;
+
+ /*! true, if initDataContents() should be called on show event. */
+ bool m_initDataContentsOnShow : 1;
+
+ /*! Set to true in setCursorPosition() to indicate that cursor position was set
+ before show() and it shouldn't be changed on show().
+ Only used if initDataContentsOnShow is true. */
+ bool m_cursorPositionSetExplicityBeforeShow : 1;
+
+ /*! true if spreadSheetMode is enabled. False by default.
+ @see KexiTableView::setSpreadSheetMode() */
+ bool m_spreadSheetMode : 1;
+
+ /*! true, if this table accepts dropping data on the rows (false by default). */
+ bool m_dropsAtRowEnabled : 1;
+
+ /*! true, if this entire (visible) row should be updated when boving to other row.
+ False by default. For table view with 'row highlighting' flag enabled, it is true. */
+ bool m_updateEntireRowWhenMovingToOtherRow : 1;
+
+ DeletionPolicy m_deletionPolicy;
+
+//! @todo make generic interface out of KexiRecordMarker
+ KexiRecordMarker *m_verticalHeader;
+
+//! @todo make generic interface out of KexiTableViewHeader
+ KexiTableViewHeader *m_horizontalHeader;
+
+ KexiDataItemInterface *m_editor;
+// KexiTableEdit *m_editor;
+
+ /*! Navigation panel, used if navigationPanelEnabled is true. */
+ KexiRecordNavigator *m_navPanel; //!< main navigation widget
+
+ bool m_navPanelEnabled : 1;
+
+ /*! true, if certical header shouldn't be increased in
+ KexiTableView::slotRowInserted() because it was already done
+ in KexiTableView::createEditor(). */
+ bool m_verticalHeaderAlreadyAdded : 1;
+
+ /*! Row number that over which user drags a mouse pointer.
+ Used to indicate dropping possibility for that row.
+ Equal -1 if no indication is needed. */
+ int m_dragIndicatorLine;
+
+ /*! Context menu widget. */
+ KPopupMenu *m_popupMenu;
+
+ bool m_contextMenuEnabled : 1;
+
+ //! Used by updateAfterCancelRowEdit()
+ bool m_alsoUpdateNextRow : 1;
+
+ /*! Row number (>=0 or -1 == no row) that will be deleted in deleteRow().
+ It is set in slotAboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)) slot
+ received from KexiTableViewData member.
+ This value will be used in slotRowDeleted() after rowDeleted() signal
+ is received from KexiTableViewData member and then cleared (set to -1). */
+ int m_rowWillBeDeleted;
+
+ /*! Displays passive error popup label used when invalid data has been entered. */
+ QGuardedPtr<KexiArrowTip> m_errorMessagePopup;
+
+ /*! Used to enable/disable execution of vScrollBarValueChanged()
+ when users navigate through rows using keyboard, so vscrollbar tooltips are not visible. */
+ bool m_vScrollBarValueChanged_enabled : 1;
+
+ /*! True, if vscrollbar tooltips are enabled (true by default). */
+ bool m_scrollbarToolTipsEnabled : 1;
+
+ QLabel* m_scrollBarTip; //!< scrollbar tooltip
+ QTimer m_scrollBarTipTimer; //!< scrollbar tooltip's timer
+ uint m_scrollBarTipTimerCnt; //!< helper for timeout counting (scrollbar tooltip)
+
+ //! Used to mark recently found value
+ class PositionOfValue {
+ public:
+ PositionOfValue() : firstCharacter(0), lastCharacter(0), exists(false)
+ {}
+ uint firstCharacter;
+ uint lastCharacter;
+ bool exists : 1;
+ };
+
+ /*! Used to mark recently found value. Updated on succesful execution of find().
+ If the current cursor's position changes, or data in the current cell changes,
+ positionOfRecentlyFoundValue.exists is set to false. */
+ PositionOfValue m_positionOfRecentlyFoundValue;
+
+ /*! Used to compare whether we're looking for new value. */
+ QVariant m_recentlySearchedValue;
+
+ /*! Used to compare whether the search direction has changed. */
+ KexiSearchAndReplaceViewInterface::Options::SearchDirection m_recentSearchDirection;
+
+ //! Setup by updateIndicesForVisibleValues() and used by find()
+ QValueVector<uint> m_indicesForVisibleValues;
+};
+
+inline bool KexiDataAwareObjectInterface::hasData() const
+{
+ if (!m_data)
+ kdDebug() << "KexiDataAwareObjectInterface: No data assigned!" << endl;
+ return m_data!=0;
+}
+
+inline KexiTableItem *KexiDataAwareObjectInterface::itemAt(int row) const
+{
+ KexiTableItem *item = m_data->at(row);
+ if (!item)
+ kdDebug() << "KexiTableView::itemAt(" << row << "): NO ITEM!!" << endl;
+ else {
+/* kdDebug() << "KexiTableView::itemAt(" << row << "):" << endl;
+ int i=1;
+ for (KexiTableItem::Iterator it = item->begin();it!=item->end();++it,i++)
+ kdDebug() << i<<": " << (*it).toString()<< endl;*/
+ }
+ return item;
+}
+
+//! Convenience macro used for KexiDataAwareObjectInterface implementations.
+#define KEXI_DATAAWAREOBJECTINTERFACE \
+public: \
+ void connectCellSelectedSignal(const QObject* receiver, const char* intIntMember) { \
+ connect(this, SIGNAL(cellSelected(int,int)), receiver, intIntMember); \
+ } \
+ void connectRowEditStartedSignal(const QObject* receiver, const char* intMember) { \
+ connect(this, SIGNAL(rowEditStarted(int)), receiver, intMember); \
+ } \
+ void connectRowEditTerminatedSignal(const QObject* receiver, const char* voidMember) { \
+ connect(this, SIGNAL(rowEditTerminated(int)), receiver, voidMember); \
+ } \
+ void connectReloadActionsSignal(const QObject* receiver, const char* voidMember) { \
+ connect(this, SIGNAL(reloadActions()), receiver, voidMember); \
+ } \
+ void connectDataSetSignal(const QObject* receiver, \
+ const char* kexiTableViewDataMember) { \
+ connect(this, SIGNAL(dataSet(KexiTableViewData*)), receiver, kexiTableViewDataMember); \
+ } \
+ void connectToReloadDataSlot(const QObject* sender, const char* voidSignal) { \
+ connect(sender, voidSignal, this, SLOT(reloadData())); \
+ }
+
+#endif
diff --git a/kexi/widget/tableview/kexidataawarepropertyset.cpp b/kexi/widget/tableview/kexidataawarepropertyset.cpp
new file mode 100644
index 00000000..92fda11e
--- /dev/null
+++ b/kexi/widget/tableview/kexidataawarepropertyset.cpp
@@ -0,0 +1,260 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-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 "kexidataawarepropertyset.h"
+#include "kexitableviewdata.h"
+#include "kexidataawareobjectiface.h"
+
+#include <koproperty/property.h>
+#include <kexiviewbase.h>
+
+#define MAX_FIELDS 101 //nice prime number (default prop. set vector size)
+
+KexiDataAwarePropertySet::KexiDataAwarePropertySet(KexiViewBase *view,
+ KexiDataAwareObjectInterface* dataObject)
+ : QObject( view, QCString(view->name())+"KexiDataAwarePropertySet" )
+ , m_view(view)
+ , m_dataObject(dataObject)
+ , m_row(-99)
+{
+ m_sets.setAutoDelete(true);
+
+// connect(m_dataObject, SIGNAL(dataSet(KexiTableViewData*)),
+// this, SLOT(slotDataSet(KexiTableViewData*)));
+ m_dataObject->connectDataSetSignal(this, SLOT(slotDataSet(KexiTableViewData*)));
+// connect(m_dataObject, SIGNAL(cellSelected(int,int)),
+// this, SLOT(slotCellSelected(int,int)));
+ m_dataObject->connectCellSelectedSignal(this, SLOT(slotCellSelected(int,int)));
+//
+ slotDataSet( m_dataObject->data() );
+ const bool wasDirty = view->dirty();
+ clear();
+ if (!wasDirty)
+ view->setDirty(false);
+}
+
+KexiDataAwarePropertySet::~KexiDataAwarePropertySet()
+{
+}
+
+void KexiDataAwarePropertySet::slotDataSet( KexiTableViewData *data )
+{
+ if (!m_currentTVData.isNull()) {
+ m_currentTVData->disconnect( this );
+ clear();
+ }
+ m_currentTVData = data;
+ if (!m_currentTVData.isNull()) {
+ connect(m_currentTVData, SIGNAL(rowDeleted()), this, SLOT(slotRowDeleted()));
+ connect(m_currentTVData, SIGNAL(rowsDeleted( const QValueList<int> & )),
+ this, SLOT(slotRowsDeleted( const QValueList<int> & )));
+ connect(m_currentTVData, SIGNAL(rowInserted(KexiTableItem*,uint,bool)),
+ this, SLOT(slotRowInserted(KexiTableItem*,uint,bool)));
+ connect(m_currentTVData, SIGNAL(reloadRequested()),
+ this, SLOT(slotReloadRequested()));
+ }
+}
+
+void KexiDataAwarePropertySet::removeCurrentPropertySet()
+{
+ remove( m_dataObject->currentRow() );
+}
+
+void KexiDataAwarePropertySet::remove(uint row)
+{
+ KoProperty::Set *set = m_sets.at(row);
+ if (!set)
+ return;
+ set->debug();
+ m_sets.remove(row);
+ m_view->setDirty();
+ m_view->propertySetSwitched();
+}
+
+uint KexiDataAwarePropertySet::size() const
+{
+ return m_sets.size();
+}
+
+void KexiDataAwarePropertySet::clear(uint minimumSize)
+{
+ m_sets.clear();
+ m_sets.resize(QMAX(minimumSize, MAX_FIELDS));
+ m_view->setDirty(true);
+ m_view->propertySetSwitched();
+}
+
+void KexiDataAwarePropertySet::slotReloadRequested()
+{
+ clear();
+}
+
+void KexiDataAwarePropertySet::insert(uint row, KoProperty::Set* set, bool newOne)
+{
+ if (!set || row >= m_sets.size()) {
+ kexiwarn << "KexiDataAwarePropertySet::insert() invalid args: rew="<< row<< " propertyset="<< set<< endl;
+ return;
+ }
+ if (set->parent() && set->parent()!=this) {
+ kexiwarn << "KexiDataAwarePropertySet::insert() propertyset's parent must be NULL or this KexiDataAwarePropertySet" << endl;
+ return;
+ }
+
+ m_sets.insert(row, set);
+
+ connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)), m_view, SLOT(setDirty()));
+
+ if (newOne) {
+ //add a special property indicating that this is brand new set,
+ //not just changed
+ KoProperty::Property* prop = new KoProperty::Property("newrow");
+ prop->setVisible(false);
+ set->addProperty( prop );
+ m_view->setDirty();
+ }
+}
+
+KoProperty::Set* KexiDataAwarePropertySet::currentPropertySet() const
+{
+ return (m_dataObject->currentRow() >= 0) ? m_sets.at( m_dataObject->currentRow() ) : 0;
+}
+
+uint KexiDataAwarePropertySet::currentRow() const
+{
+ return m_dataObject->currentRow();
+}
+
+void KexiDataAwarePropertySet::slotRowDeleted()
+{
+ m_view->setDirty();
+ removeCurrentPropertySet();
+
+ //let's move up all property sets that are below that deleted
+ m_sets.setAutoDelete(false);//to avoid auto deleting in insert()
+ const int r = m_dataObject->currentRow();
+ for (int i=r;i<int(m_sets.size()-1);i++) {
+ KoProperty::Set *set = m_sets[i+1];
+ m_sets.insert( i , set );
+ }
+ m_sets.insert( m_sets.size()-1, 0 );
+ m_sets.setAutoDelete(true);//revert the flag
+
+ m_view->propertySetSwitched();
+ emit rowDeleted();
+}
+
+void KexiDataAwarePropertySet::slotRowsDeleted( const QValueList<int> &rows )
+{
+ //let's move most property sets up & delete unwanted
+ m_sets.setAutoDelete(false);//to avoid auto deleting in insert()
+ const int orig_size = size();
+ int prev_r = -1;
+ int num_removed = 0, cur_r = -1;
+ for (QValueList<int>::ConstIterator r_it = rows.constBegin(); r_it!=rows.constEnd() && *r_it < orig_size; ++r_it) {
+ cur_r = *r_it;// - num_removed;
+ if (prev_r>=0) {
+// kdDebug() << "move " << prev_r+num_removed-1 << ".." << cur_r-1 << " to " << prev_r+num_removed-1 << ".." << cur_r-2 << endl;
+ int i=prev_r;
+ KoProperty::Set *set = m_sets.take(i+num_removed);
+ kdDebug() << "property set " << i+num_removed << " deleted" << endl;
+ delete set;
+ num_removed++;
+ for (; (i+num_removed)<cur_r; i++) {
+ m_sets.insert( i, m_sets[i+num_removed] );
+ kdDebug() << i << " <- " << i+num_removed << endl;
+ }
+ }
+ prev_r = cur_r - num_removed;
+ }
+ //move remaining property sets up
+ if (cur_r>=0) {
+ KoProperty::Set *set = m_sets.take(cur_r);
+ kdDebug() << "property set " << cur_r << " deleted" << endl;
+ delete set;
+ num_removed++;
+ for (int i=prev_r; (i+num_removed)<orig_size; i++) {
+ m_sets.insert( i, m_sets[i+num_removed] );
+ kdDebug() << i << " <- " << i+num_removed << endl;
+ }
+ }
+ //finally: clear last rows
+ for (int i=orig_size-num_removed; i<orig_size; i++) {
+ kdDebug() << i << " <- zero" << endl;
+ m_sets.insert( i, 0 );
+ }
+ m_sets.setAutoDelete(true);//revert the flag
+
+ if (num_removed>0)
+ m_view->setDirty();
+ m_view->propertySetSwitched();
+}
+
+//void KexiDataAwarePropertySet::slotEmptyRowInserted(KexiTableItem*, uint /*index*/)
+void KexiDataAwarePropertySet::slotRowInserted(KexiTableItem*, uint row, bool /*repaint*/)
+{
+ m_view->setDirty();
+
+ //let's move down all property set that are below
+ m_sets.setAutoDelete(false);//to avoid auto deleting in insert()
+// const int r = m_dataObject->currentRow();
+ m_sets.resize(m_sets.size()+1);
+ for (int i=int(m_sets.size())-1; i>(int)row; i--) {
+ KoProperty::Set *set = m_sets[i-1];
+ m_sets.insert( i , set );
+ }
+ m_sets.insert( row, 0 );
+ m_sets.setAutoDelete(true);//revert the flag
+
+ m_view->propertySetSwitched();
+
+ emit rowInserted();
+}
+
+void KexiDataAwarePropertySet::slotCellSelected(int, int row)
+{
+ if(row == m_row)
+ return;
+ m_row = row;
+ m_view->propertySetSwitched();
+}
+
+KoProperty::Set* KexiDataAwarePropertySet::findPropertySetForItem(KexiTableItem& item)
+{
+ if (m_currentTVData.isNull())
+ return 0;
+ int idx = m_currentTVData->findRef(&item);
+ if (idx<0)
+ return 0;
+ return m_sets[idx];
+}
+
+int KexiDataAwarePropertySet::findRowForPropertyValue(const QCString& propertyName, const QVariant& value)
+{
+ const int size = m_sets.size();
+ for (int i=0; i<size; i++) {
+ KoProperty::Set *set = m_sets[i];
+ if (!set || !set->contains(propertyName))
+ continue;
+ if (set->property(propertyName).value() == value)
+ return i;
+ }
+ return -1;
+}
+
+#include "kexidataawarepropertyset.moc"
diff --git a/kexi/widget/tableview/kexidataawarepropertyset.h b/kexi/widget/tableview/kexidataawarepropertyset.h
new file mode 100644
index 00000000..cee55da0
--- /dev/null
+++ b/kexi/widget/tableview/kexidataawarepropertyset.h
@@ -0,0 +1,149 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-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 KEXIDATAAWAREPROPERTYSET_H
+#define KEXIDATAAWAREPROPERTYSET_H
+
+#include <qguardedptr.h>
+#include <qptrvector.h>
+#include <koproperty/set.h>
+
+typedef QPtrVector<KoProperty::Set> SetVector;
+
+class KexiViewBase;
+class KexiTableItem;
+class KexiTableViewData;
+class KexiDataAwareObjectInterface;
+
+/*! This helper class handles data changes of a single
+ object implementing KexiDataAwareObjectInterface (e.g. KexiTableView) inside
+ a KexiViewBase container.
+
+ It is currently used in KexiAlterTableDialog and KexiQueryDesignerGuiEditor,
+ and may be used for similar purposes, when each KexiDataAwareObjectInterface's
+ row can be associated with single KoProperty::Set object, and given
+ KexiDataAwareObjectInterface object has to inform the world about currently
+ selected row/property set.
+
+ Following functionality is built-in:
+ - auto-initializing after resetting of table view's data
+ - destroying single property set that is associated with deleted row
+ - inserting single property set that and associating it with new row
+ - all property sets are cleared when view's data is cleared (using clear())
+ - setting view's 'dirty' flag when needed
+ - signalling via KexiViewBase::propertySetSwitched() that current property
+ set has changed (e.g. on moving to other row)
+*/
+class KEXIDATATABLE_EXPORT KexiDataAwarePropertySet : public QObject
+{
+ Q_OBJECT
+
+ public:
+ /*! You can instantiate KexiDataAwarePropertySet object
+ for existing \a tableView and \a view. \a tableView can have data assigned
+ (KexiDataAwareObjectInterface::setData()) now but it can be done later as well
+ (but assigning data is needed for proper functionality).
+ Any changed reassignments of table view's data will be handled automatically. */
+ KexiDataAwarePropertySet(KexiViewBase *view, KexiDataAwareObjectInterface* dataObject);
+
+ virtual ~KexiDataAwarePropertySet();
+
+ uint size() const;
+
+ KoProperty::Set* currentPropertySet() const;
+
+ uint currentRow() const;
+
+ inline KoProperty::Set* at(uint row) const { return m_sets[row]; }
+
+ /*! \return a pointer to property set assigned for \a item or null if \a item has no
+ property set assigned or it's not owned by assigned table view or
+ if assigned table view has no data set. */
+ KoProperty::Set* findPropertySetForItem(KexiTableItem& item);
+
+ /*! \return number of the first row containing \a propertyName property equal to \a value.
+ This is used e.g. in the Table Designer to find a row by field name.
+ If no such row has been found, -1 is returned. */
+ int findRowForPropertyValue(const QCString& propertyName, const QVariant& value);
+
+ signals:
+ /*! Emmited when row is deleted.
+ KexiDataAwareObjectInterface::rowDeleted() signal is usually used but when you're using
+ KexiDataAwarePropertySet, you never know if currentPropertySet() is updated.
+ So use this signal instead. */
+ void rowDeleted();
+
+ /*! Emmited when row is inserted.
+ Purpose of this signal is similar to rowDeleted() signal. */
+ void rowInserted();
+
+ public slots:
+ void removeCurrentPropertySet();
+
+ void clear(uint minimumSize = 0);
+
+ /*! Inserts \a set property set at \a row position.
+ If there was a buffer at this position before, it will be destroyed.
+ If \a newOne is true, the property set will be marked as newly created,
+ simply by adding "newrow" property.
+
+ The property set \a set will be owned by this object, so you should not
+ delete this property set by hand but call removeCurrentPropertySet()
+ or remove(uint) instead.
+ Note that property set's parent (QObject::parent()) must be null
+ or qual to this KexiDataAwarePropertySet object, otherwise this method
+ will fail with a warning.
+ */
+ void insert(uint row, KoProperty::Set* set, bool newOne = false);
+
+ /*! Removes a property set at \a row position. */
+ void remove(uint row);
+
+ protected slots:
+ /*! Handles table view's data source changes. */
+ void slotDataSet( KexiTableViewData *data );
+
+ //! Called on row delete in a tableview.
+ void slotRowDeleted();
+
+ //! Called on multiple rows delete in a tableview.
+ void slotRowsDeleted( const QValueList<int> &rows );
+
+ //! Called on \a row insertion in a tableview.
+ void slotRowInserted(KexiTableItem* item, uint row, bool repaint);
+
+ //! Called on selecting another cell in a tableview.
+ void slotCellSelected(int, int row);
+
+ //! Called on clearing tableview's data: just clears all property sets.
+ void slotReloadRequested();
+
+ protected:
+ SetVector m_sets; //!< prop. sets vector
+
+ QGuardedPtr<KexiViewBase> m_view;
+ KexiDataAwareObjectInterface* m_dataObject;
+// QGuardedPtr<KexiTableView> m_tableView;
+ QGuardedPtr<KexiTableViewData> m_currentTVData;
+
+ int m_row; //!< used to know if a new row is selected in slotCellSelected()
+};
+
+#endif
+
diff --git a/kexi/widget/tableview/kexidatatableview.cpp b/kexi/widget/tableview/kexidatatableview.cpp
new file mode 100644
index 00000000..9248e890
--- /dev/null
+++ b/kexi/widget/tableview/kexidatatableview.cpp
@@ -0,0 +1,121 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@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 <qtimer.h>
+#include <qapplication.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kaction.h>
+
+#include <kexidb/connection.h>
+#include <kexidb/cursor.h>
+
+#include "kexidatatableview.h"
+
+
+KexiDataTableView::KexiDataTableView(QWidget *parent, const char *name)
+ : KexiTableView(0, parent, name)
+{
+ init();
+}
+
+KexiDataTableView::KexiDataTableView(QWidget *parent, const char *name, KexiDB::Cursor *cursor)
+ : KexiTableView(0, parent, name)
+{
+ init();
+ setData(cursor);
+}
+
+KexiDataTableView::~KexiDataTableView()
+{
+}
+
+void
+KexiDataTableView::init()
+{
+ m_cursor = 0;
+
+// m_maxRecord = 0;
+// m_records = 0;
+// m_first = false;
+
+// connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotMoving(int)));
+// connect(verticalScrollBar(), SIGNAL(sliderMoved(int)), this, SLOT(slotMoving(int)));
+}
+
+/*void KexiDataTableView::initActions(KActionCollection *col)
+{
+ KexiTableView::initActions(col);
+ new KAction(i18n("Filter"), "filter", 0, this, SLOT(filter()), col, "tablepart_filter");
+}*/
+
+bool KexiDataTableView::setData(KexiDB::Cursor *cursor)
+{
+//js if (!m_first)
+//js clearColumns();
+ if (!cursor) {
+ clearColumns();
+ m_cursor = 0;
+ return true;
+ }
+ if (cursor!=m_cursor) {
+ clearColumns();
+ }
+ m_cursor = cursor;
+
+ if (!m_cursor->query()) {
+ kdDebug() << "KexiDataTableView::setData(): WARNING: cursor should have query schema defined!\n--aborting setData()." << endl;
+ m_cursor->debug();
+ clearColumns();
+ return false;
+ }
+
+ if (m_cursor->fieldCount()<1) {
+ clearColumns();
+ return true;
+ }
+
+ if (!m_cursor->isOpened() && !m_cursor->open()) {
+ kdDebug() << "KexiDataTableView::setData(): WARNING: cannot open cursor\n--aborting setData(). \n" <<
+ m_cursor->serverErrorMsg() << endl;
+ m_cursor->debug();
+ clearColumns();
+ return false;
+ }
+
+ KexiTableViewData *tv_data = new KexiTableViewData(m_cursor);
+
+ QString caption = m_cursor->query()->caption();
+ if (caption.isEmpty())
+ caption = m_cursor->query()->name();
+
+ setCaption( caption );
+
+ //PRIMITIVE!! data setting:
+ tv_data->preloadAllRows();
+
+ KexiTableView::setData(tv_data);
+ return true;
+}
+
+#include "kexidatatableview.moc"
diff --git a/kexi/widget/tableview/kexidatatableview.h b/kexi/widget/tableview/kexidatatableview.h
new file mode 100644
index 00000000..6f421894
--- /dev/null
+++ b/kexi/widget/tableview/kexidatatableview.h
@@ -0,0 +1,94 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@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 KEXIDATATABLEVIEW_H
+#define KEXIDATATABLEVIEW_H
+
+#include "kexitableview.h"
+
+class KexiTableItem;
+class QVariant;
+class KXMLGUIClient;
+
+namespace KexiDB {
+ class Cursor;
+}
+
+/**
+ * Database aware table widget.
+ */
+class KEXIDATATABLE_EXPORT KexiDataTableView : public KexiTableView
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * creates a blank widget
+ */
+ KexiDataTableView(QWidget *parent, const char *name =0);
+
+ /*! Creates a table widget and fills it using data from \a cursor.
+ Cursor will be opened (with open()) if it is not yet opened.
+ Cursor must be defined on query schema, not raw statement (see Connection::prepareQuery()
+ and Connection::executeQuery()), otherwise the table view remain not filled with data.
+ Cursor \a cursor will not be owned by this object.
+ */
+ KexiDataTableView(QWidget *parent, const char *name, KexiDB::Cursor *cursor);
+
+ ~KexiDataTableView();
+
+// virtual void initActions(KActionCollection *col);
+
+ /*! Fills table view with data using \a cursor. \return true on success.
+ Cursor \a cursor will not be owned by this object. */
+ bool setData(KexiDB::Cursor *cursor);
+
+ /*! \return cursor used as data source for this table view,
+ or NULL if no valid cursor is defined. */
+ KexiDB::Cursor *cursor() { return m_cursor; }
+
+ /**
+ * @returns the number of records in the data set, (if data set is present)
+ * @note not all of the records have to be processed
+ */
+ int recordCount() { return m_data->count(); }
+
+ #ifndef KEXI_NO_PRINT
+// virtual void print(KPrinter &printer);
+ #endif
+
+ protected:
+ void init();
+
+ /*! Reimplemented: called by deleteItem() - we are deleting data associated with \a item. */
+// virtual bool beforeDeleteItem(KexiTableItem *item);
+
+ protected slots:
+// void slotClearData();
+
+ private:
+ //db stuff
+ KexiDB::Cursor *m_cursor;
+
+// QMap<KexiDBUpdateRecord*,KexiTableItem*> m_insertMapping;
+};
+
+#endif
diff --git a/kexi/widget/tableview/kexidatetableedit.cpp b/kexi/widget/tableview/kexidatetableedit.cpp
new file mode 100644
index 00000000..8a1fbcae
--- /dev/null
+++ b/kexi/widget/tableview/kexidatetableedit.cpp
@@ -0,0 +1,290 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003-2004,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 "kexidatetableedit.h"
+
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qvariant.h>
+#include <qrect.h>
+#include <qpalette.h>
+#include <qcolor.h>
+#include <qfontmetrics.h>
+#include <qdatetime.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qlayout.h>
+#include <qtoolbutton.h>
+#include <qdatetimeedit.h>
+#include <qclipboard.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>
+
+#include <kexiutils/utils.h>
+
+
+KexiDateTableEdit::KexiDateTableEdit(KexiTableViewColumn &column, QWidget *parent)
+ : KexiInputTableEdit(column, parent)
+{
+ setName("KexiDateTableEdit");
+
+//! @todo add QValidator so date like "2006-59-67" cannot be even entered
+
+ m_lineedit->setInputMask( m_formatter.inputMask() );
+}
+
+KexiDateTableEdit::~KexiDateTableEdit()
+{
+}
+
+void KexiDateTableEdit::setValueInInternalEditor(const QVariant &value)
+{
+ if (value.isValid() && value.toDate().isValid())
+ m_lineedit->setText( m_formatter.dateToString( value.toDate() ) );
+ else
+ m_lineedit->setText( QString::null );
+}
+
+void KexiDateTableEdit::setValueInternal(const QVariant& add_, bool removeOld)
+{
+ if (removeOld) {
+ //new date entering... just fill the line edit
+//! @todo cut string if too long..
+ QString add(add_.toString());
+ m_lineedit->setText(add);
+ m_lineedit->setCursorPosition(add.length());
+ return;
+ }
+ setValueInInternalEditor( m_origValue );
+ m_lineedit->setCursorPosition(0); //ok?
+}
+
+void KexiDateTableEdit::setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h )
+{
+ Q_UNUSED(p);
+ Q_UNUSED(focused);
+ Q_UNUSED(x);
+ Q_UNUSED(w);
+ Q_UNUSED(h);
+#ifdef Q_WS_WIN
+ y_offset = -1;
+#else
+ y_offset = 0;
+#endif
+ if (val.toDate().isValid())
+ txt = m_formatter.dateToString(val.toDate());
+// txt = val.toDate().toString(Qt::LocalDate);
+ align |= AlignLeft;
+}
+
+bool KexiDateTableEdit::valueIsNull()
+{
+// if (m_lineedit->text().replace(m_formatter.separator(),"").stripWhiteSpace().isEmpty())
+ if (m_formatter.isEmpty(m_lineedit->text())) //empty date is null
+ return true;
+ return dateValue().isNull();
+}
+
+bool KexiDateTableEdit::valueIsEmpty()
+{
+ return valueIsNull();//js OK? TODO (nonsense?)
+}
+
+QDate KexiDateTableEdit::dateValue() const
+{
+ return m_formatter.stringToDate( m_lineedit->text() );
+}
+
+QVariant KexiDateTableEdit::value()
+{
+ return m_formatter.stringToVariant( m_lineedit->text() );
+}
+
+bool KexiDateTableEdit::valueIsValid()
+{
+ if (m_formatter.isEmpty(m_lineedit->text())) //empty date is valid
+ return true;
+ return m_formatter.stringToDate( m_lineedit->text() ).isValid();
+}
+
+void KexiDateTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
+{
+ Q_UNUSED(visibleValue);
+ if (!value.isNull() && value.toDate().isValid())
+ qApp->clipboard()->setText( m_formatter.dateToString(value.toDate()) );
+ else
+ qApp->clipboard()->setText( QString::null );
+}
+
+void KexiDateTableEdit::handleAction(const QString& actionName)
+{
+ const bool alreadyVisible = m_lineedit->isVisible();
+
+ if (actionName=="edit_paste") {
+ const QVariant newValue( m_formatter.stringToDate(qApp->clipboard()->text()) );
+ if (!alreadyVisible) { //paste as the entire text if the cell was not in edit mode
+ emit editRequested();
+ m_lineedit->clear();
+ }
+ setValueInInternalEditor( newValue );
+ }
+ else
+ KexiInputTableEdit::handleAction(actionName);
+}
+
+/*
+void
+KexiDateTableEdit::slotDateChanged(QDate date)
+{
+ m_edit->setDate(date);
+ repaint();
+}
+
+void
+KexiDateTableEdit::slotShowDatePicker()
+{
+ QDate date = m_edit->date();
+
+ m_datePicker->setDate(date);
+ m_datePicker->setFocus();
+ m_datePicker->show();
+ m_datePicker->setFocus();
+}
+
+//! @internal helper
+void KexiDateTableEdit::moveToFirstSection()
+{
+ if (!m_dte_date_obj)
+ return;
+#ifdef QDateTimeEditor_HACK
+ if (m_dte_date)
+ m_dte_date->setFocusSection(0);
+#else
+#ifdef Q_WS_WIN //tmp
+ QKeyEvent ke_left(QEvent::KeyPress, Qt::Key_Left, 0, 0);
+ for (int i=0; i<8; i++)
+ QApplication::sendEvent( m_dte_date_obj, &ke_left );
+#endif
+#endif
+}
+
+bool KexiDateTableEdit::eventFilter( QObject *o, QEvent *e )
+{
+ if (o==m_datePicker) {
+ kdDebug() << e->type() << endl;
+ switch (e->type()) {
+ case QEvent::Hide:
+ m_datePickerPopupMenu->hide();
+ break;
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease: {
+ kdDebug() << "ok!" << endl;
+ QKeyEvent *ke = (QKeyEvent *)e;
+ if (ke->key()==Key_Enter || ke->key()==Key_Return) {
+ //accepting picker
+ acceptDate();
+ return true;
+ }
+ else if (ke->key()==Key_Escape) {
+ //canceling picker
+ m_datePickerPopupMenu->hide();
+ kdDebug() << "reject" << endl;
+ return true;
+ }
+ else m_datePickerPopupMenu->setFocus();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+#ifdef Q_WS_WIN //tmp
+ else if (e->type()==QEvent::FocusIn && o->parent() && o->parent()->parent()==m_edit
+ && m_setNumberOnFocus >= 0 && m_dte_date_obj)
+ {
+ // there was a number character passed as 'add' parameter in init():
+ moveToFirstSection();
+ QKeyEvent ke(QEvent::KeyPress, int(Qt::Key_0)+m_setNumberOnFocus,
+ '0'+m_setNumberOnFocus, 0, QString::number(m_setNumberOnFocus));
+ QApplication::sendEvent( m_dte_date_obj, &ke );
+ m_setNumberOnFocus = -1;
+ }
+#endif
+#ifdef QDateTimeEditor_HACK
+ else if (e->type()==QEvent::KeyPress && m_dte_date) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ if ((ke->key()==Qt::Key_Right && !m_sentEvent && cursorAtEnd())
+ || (ke->key()==Qt::Key_Left && !m_sentEvent && cursorAtStart()))
+ {
+ //the editor should send this key event:
+ m_sentEvent = true; //avoid recursion
+ QApplication::sendEvent( this, ke );
+ m_sentEvent = false;
+ ke->ignore();
+ return true;
+ }
+ }
+#endif
+ return false;
+}
+
+void KexiDateTableEdit::acceptDate()
+{
+ m_edit->setDate(m_datePicker->date());
+ m_datePickerPopupMenu->hide();
+ kdDebug() << "accept" << endl;
+}
+
+bool KexiDateTableEdit::cursorAtStart()
+{
+#ifdef QDateTimeEditor_HACK
+ return m_dte_date && m_edit->hasFocus() && m_dte_date->focusSection()==0;
+#else
+ return false;
+#endif
+}
+
+bool KexiDateTableEdit::cursorAtEnd()
+{
+#ifdef QDateTimeEditor_HACK
+ return m_dte_date && m_edit->hasFocus()
+ && m_dte_date->focusSection()==int(m_dte_date->sectionCount()-1);
+#else
+ return false;
+#endif
+}
+
+void KexiDateTableEdit::clear()
+{
+ m_edit->setDate(QDate());
+}*/
+
+KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiDateEditorFactoryItem, KexiDateTableEdit)
+
+#include "kexidatetableedit.moc"
diff --git a/kexi/widget/tableview/kexidatetableedit.h b/kexi/widget/tableview/kexidatetableedit.h
new file mode 100644
index 00000000..4f2a4f59
--- /dev/null
+++ b/kexi/widget/tableview/kexidatetableedit.h
@@ -0,0 +1,66 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003-2004,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 KEXIDATETABLEEDIT_H
+#define KEXIDATETABLEEDIT_H
+
+#include "kexiinputtableedit.h"
+#include <widget/utils/kexidatetimeformatter.h>
+
+/*! @short Editor class for Date type.
+ It is a replacement QDateEdit due to usability problems:
+ people are accustomed to use single-character cursor.
+ Date format is retrieved from the KDE global settings.
+ and input/output is performed using KLineEdit (from KexiInputTableEdit).
+*/
+class KexiDateTableEdit : public KexiInputTableEdit
+{
+ Q_OBJECT
+
+ public:
+ KexiDateTableEdit(KexiTableViewColumn &column, QWidget *parent=0);
+ virtual ~KexiDateTableEdit();
+ virtual void setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h );
+ virtual QVariant value();
+ virtual bool valueIsNull();
+ virtual bool valueIsEmpty();
+ virtual bool valueIsValid();
+
+ /*! Reimplemented after KexiInputTableEdit. */
+ virtual void handleAction(const QString& actionName);
+
+ /*! Reimplemented after KexiInputTableEdit. */
+ virtual void handleCopyAction(const QVariant& value, const QVariant& visibleValue);
+
+ protected:
+ //! helper
+ void setValueInInternalEditor(const QVariant &value);
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+ QDate dateValue() const;
+
+ //! Used to format and convert date values
+ KexiDateFormatter m_formatter;
+};
+
+KEXI_DECLARE_CELLEDITOR_FACTORY_ITEM(KexiDateEditorFactoryItem)
+
+#endif
diff --git a/kexi/widget/tableview/kexidatetimetableedit.cpp b/kexi/widget/tableview/kexidatetimetableedit.cpp
new file mode 100644
index 00000000..fbca7cd6
--- /dev/null
+++ b/kexi/widget/tableview/kexidatetimetableedit.cpp
@@ -0,0 +1,165 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003-2004,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 "kexidatetimetableedit.h"
+
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qvariant.h>
+#include <qrect.h>
+#include <qpalette.h>
+#include <qcolor.h>
+#include <qfontmetrics.h>
+#include <qdatetime.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qlayout.h>
+#include <qtoolbutton.h>
+#include <qdatetimeedit.h>
+#include <qclipboard.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>
+
+#include <kexiutils/utils.h>
+
+KexiDateTimeTableEdit::KexiDateTimeTableEdit(KexiTableViewColumn &column, QWidget *parent)
+ : KexiInputTableEdit(column, parent)
+{
+ setName("KexiDateTimeTableEdit");
+
+//! @todo add QValidator so time like "99:88:77" cannot be even entered
+
+ m_lineedit->setInputMask(
+ dateTimeInputMask( m_dateFormatter, m_timeFormatter ) );
+}
+
+KexiDateTimeTableEdit::~KexiDateTimeTableEdit()
+{
+}
+
+void KexiDateTimeTableEdit::setValueInInternalEditor(const QVariant &value)
+{
+ if (value.isValid() && value.toDateTime().isValid())
+ m_lineedit->setText(
+ m_dateFormatter.dateToString( value.toDateTime().date() ) + " " +
+ m_timeFormatter.timeToString( value.toDateTime().time() ) );
+ else
+ m_lineedit->setText( QString::null );
+}
+
+void KexiDateTimeTableEdit::setValueInternal(const QVariant& add_, bool removeOld)
+{
+ if (removeOld) {
+ //new time entering... just fill the line edit
+//! @todo cut string if too long..
+ QString add(add_.toString());
+ m_lineedit->setText(add);
+ m_lineedit->setCursorPosition(add.length());
+ return;
+ }
+ setValueInInternalEditor( m_origValue );
+ m_lineedit->setCursorPosition(0); //ok?
+}
+
+void KexiDateTimeTableEdit::setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h )
+{
+ Q_UNUSED(p);
+ Q_UNUSED(focused);
+ Q_UNUSED(x);
+ Q_UNUSED(w);
+ Q_UNUSED(h);
+#ifdef Q_WS_WIN
+ y_offset = -1;
+#else
+ y_offset = 0;
+#endif
+ if (val.toDateTime().isValid())
+ txt = m_dateFormatter.dateToString(val.toDateTime().date()) + " "
+ + m_timeFormatter.timeToString(val.toDateTime().time());
+ align |= AlignLeft;
+}
+
+bool KexiDateTimeTableEdit::valueIsNull()
+{
+ if (textIsEmpty())
+ return true;
+ return !stringToDateTime(m_dateFormatter, m_timeFormatter, m_lineedit->text()).isValid();
+}
+
+bool KexiDateTimeTableEdit::valueIsEmpty()
+{
+ return valueIsNull();//js OK? TODO (nonsense?)
+}
+
+QVariant KexiDateTimeTableEdit::value()
+{
+ if (textIsEmpty())
+ return QVariant();
+ return stringToDateTime(m_dateFormatter, m_timeFormatter, m_lineedit->text());
+}
+
+bool KexiDateTimeTableEdit::valueIsValid()
+{
+ return dateTimeIsValid( m_dateFormatter, m_timeFormatter, m_lineedit->text() );
+}
+
+bool KexiDateTimeTableEdit::textIsEmpty() const
+{
+ return dateTimeIsEmpty( m_dateFormatter, m_timeFormatter, m_lineedit->text() );
+}
+
+void KexiDateTimeTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
+{
+ Q_UNUSED(visibleValue);
+ if (!value.isNull() && value.toDateTime().isValid())
+ qApp->clipboard()->setText( m_dateFormatter.dateToString(value.toDateTime().date()) + " "
+ + m_timeFormatter.timeToString(value.toDateTime().time()) );
+ else
+ qApp->clipboard()->setText( QString::null );
+}
+
+void KexiDateTimeTableEdit::handleAction(const QString& actionName)
+{
+ const bool alreadyVisible = m_lineedit->isVisible();
+
+ if (actionName=="edit_paste") {
+ const QVariant newValue( stringToDateTime(m_dateFormatter, m_timeFormatter, qApp->clipboard()->text()) );
+ if (!alreadyVisible) { //paste as the entire text if the cell was not in edit mode
+ emit editRequested();
+ m_lineedit->clear();
+ }
+ setValueInInternalEditor( newValue );
+ }
+ else
+ KexiInputTableEdit::handleAction(actionName);
+}
+
+KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiDateTimeEditorFactoryItem, KexiDateTimeTableEdit)
+
+#include "kexidatetimetableedit.moc"
diff --git a/kexi/widget/tableview/kexidatetimetableedit.h b/kexi/widget/tableview/kexidatetimetableedit.h
new file mode 100644
index 00000000..c2f9eba8
--- /dev/null
+++ b/kexi/widget/tableview/kexidatetimetableedit.h
@@ -0,0 +1,69 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003-2004,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 KEXIDATETIMETABLEEDIT_H
+#define KEXIDATETIMETABLEEDIT_H
+
+#include "kexidatetableedit.h"
+#include "kexitimetableedit.h"
+
+/*! @short Editor class for Date/Time type.
+ It is a replacement QDateTimeEdit due to usability problems:
+ people are accustomed to use single-character cursor.
+ Date and Time format is retrieved from the KDE global settings
+ and input/output is performed using KLineEdit (from KexiInputTableEdit).
+*/
+class KexiDateTimeTableEdit : public KexiInputTableEdit
+{
+ Q_OBJECT
+
+ public:
+ KexiDateTimeTableEdit(KexiTableViewColumn &column, QWidget *parent=0);
+ virtual ~KexiDateTimeTableEdit();
+ virtual void setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h );
+ virtual QVariant value();
+ virtual bool valueIsNull();
+ virtual bool valueIsEmpty();
+ virtual bool valueIsValid();
+
+ /*! Reimplemented after KexiInputTableEdit. */
+ virtual void handleAction(const QString& actionName);
+
+ /*! Reimplemented after KexiInputTableEdit. */
+ virtual void handleCopyAction(const QVariant& value, const QVariant& visibleValue);
+
+ protected:
+ //! helper
+ void setValueInInternalEditor(const QVariant &value);
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+ bool textIsEmpty() const;
+
+ //! Used to format and convert date values
+ KexiDateFormatter m_dateFormatter;
+
+ //! Used to format and convert time values
+ KexiTimeFormatter m_timeFormatter;
+};
+
+KEXI_DECLARE_CELLEDITOR_FACTORY_ITEM(KexiDateTimeEditorFactoryItem)
+
+#endif
diff --git a/kexi/widget/tableview/kexiinputtableedit.cpp b/kexi/widget/tableview/kexiinputtableedit.cpp
new file mode 100644
index 00000000..9af5c627
--- /dev/null
+++ b/kexi/widget/tableview/kexiinputtableedit.cpp
@@ -0,0 +1,395 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-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 "kexiinputtableedit.h"
+
+#include <qregexp.h>
+#include <qevent.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qpainter.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qtooltip.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+#include <kcompletionbox.h>
+#include <knumvalidator.h>
+#include <kexiutils/longlongvalidator.h>
+#include <kexidb/field.h>
+#include <kexidb/fieldvalidator.h>
+
+//! @internal
+class MyLineEdit : public KLineEdit
+{
+ public:
+ MyLineEdit(QWidget *parent, const char *name) : KLineEdit(parent,name)
+ {}
+ protected:
+ virtual void drawFrame ( QPainter * p ) {
+ p->setPen( QPen( colorGroup().text() ) );
+ QRect r = rect();
+ p->moveTo( r.topLeft() );
+ p->lineTo( r.topRight() );
+ p->lineTo( r.bottomRight() );
+ p->lineTo( r.bottomLeft() );
+ if (pos().x() == 0) //draw left side only when it is @ the edge
+ p->lineTo( r.topLeft() );
+ }
+};
+
+//======================================================
+
+KexiInputTableEdit::KexiInputTableEdit(KexiTableViewColumn &column, QWidget *parent)
+ : KexiTableEdit(column, parent)
+{
+ setName("KexiInputTableEdit");
+// m_type = f.type(); //copied because the rest of code uses m_type
+// m_field = &f;
+// m_origValue = value;//original value
+ init();
+}
+
+KexiInputTableEdit::~KexiInputTableEdit()
+{
+}
+
+void KexiInputTableEdit::init()
+{
+// kdDebug() << "KexiInputTableEdit: m_origValue.typeName()==" << m_origValue.typeName() << endl;
+// kdDebug() << "KexiInputTableEdit: type== " << field()->typeName() << endl;
+// kdDebug() << "KexiInputTableEdit: displayed type== " << displayedField()->typeName() << endl;
+
+ m_textFormatter.setField( field() );
+
+ //init settings
+ m_decsym = KGlobal::locale()->decimalSymbol();
+ if (m_decsym.isEmpty())
+ m_decsym=".";//default
+
+ const bool align_right = displayedField()->isNumericType();
+
+ if (!align_right) {
+ //create layer for internal editor
+ QHBoxLayout *lyr = new QHBoxLayout(this);
+ lyr->addSpacing(4);
+ lyr->setAutoAdd(true);
+ }
+
+ //create internal editor
+ m_lineedit = new MyLineEdit(this, "KexiInputTableEdit-KLineEdit");
+ setViewWidget(m_lineedit);
+ if (align_right)
+ m_lineedit->setAlignment(AlignRight);
+// m_cview->setFrame(false);
+// m_cview->setFrameStyle( QFrame::Plain | QFrame::Box );
+// m_cview->setLineWidth( 1 );
+ m_calculatedCell = false;
+
+#if 0 //js TODO
+ connect(m_cview->completionBox(), SIGNAL(activated(const QString &)),
+ this, SLOT(completed(const QString &)));
+ connect(m_cview->completionBox(), SIGNAL(highlighted(const QString &)),
+ this, SLOT(completed(const QString &)));
+ m_cview->completionBox()->setTabHandling(true);
+#endif
+
+}
+
+void KexiInputTableEdit::setValueInternal(const QVariant& add, bool removeOld)
+{
+ QString text( m_textFormatter.valueToText(removeOld ? QVariant() : m_origValue, add.toString()) );
+ if (text.isEmpty()) {
+ if (m_origValue.toString().isEmpty()) {
+ //we have to set NULL initial value:
+ m_lineedit->setText(QString::null);
+ }
+ }
+ else {
+ m_lineedit->setText(text);
+ }
+
+#if 0
+//move to end is better by default
+ m_cview->selectAll();
+#else
+//js TODO: by default we're moving to the end of editor, ADD OPTION allowing "select all chars"
+ m_lineedit->end(false);
+#endif
+
+ if (!m_lineedit->validator()) {
+ QValidator *validator = new KexiDB::FieldValidator(
+ *field(), m_lineedit, "KexiInputTableEdit-validator");
+ m_lineedit->setValidator( validator );
+ }
+}
+
+#if 0
+//moved to KexiTextFormatter
+QString KexiInputTableEdit::valueToText(KexiDB::Field* field, const QVariant& value, const QString& add)
+{
+ QString text; //result
+
+ if (field->isFPNumericType()) {
+//! @todo precision!
+//! @todo support 'g' format
+ text = QString::number(value.toDouble(), 'f',
+ QMAX(field->visibleDecimalPlaces(), 10)); //<-- 10 is quite good maximum for fractional digits
+ //! @todo add command line settings?
+ if (value.toDouble() == 0.0) {
+ text = add.isEmpty() ? "0" : add; //eat 0
+ }
+ else {
+//! @todo (js): get decimal places settings here...
+ QStringList sl = QStringList::split(".", text);
+ if (text.isEmpty()) {
+ //nothing
+ }
+ else if (sl.count()==2) {
+// kdDebug() << "sl.count()=="<<sl.count()<< " " <<sl[0] << " | " << sl[1] << endl;
+ const QString sl1 = sl[1];
+ int pos = sl1.length()-1;
+ if (pos>=1) {
+ for (;pos>=0 && sl1[pos]=='0';pos--)
+ ;
+ pos++;
+ }
+ if (pos>0)
+ text = sl[0] + m_decsym + sl1.left(pos);
+ else
+ text = sl[0]; //no decimal point
+ }
+ text += add;
+ }
+/*moved to KexiDB::FieldValidator
+ if (setValidator && !m_lineedit->validator()) {
+ QValidator *validator = new KDoubleValidator(m_lineedit);
+ m_lineedit->setValidator( validator );
+ }*/
+ }
+ else {
+ text = value.toString();
+ if (field->isIntegerType()) {
+ if (value.toInt() == 0) {
+ text = add; //eat 0
+ }
+ else {
+ text += add;
+ }
+/*moved to KexiDB::FieldValidator
+//! @todo implement ranges here!
+ if (setValidator && !m_lineedit->validator()) {
+ QValidator *validator;
+ if (KexiDB::Field::BigInteger == field()->type()) {
+//! @todo use field->isUnsigned() for KexiUtils::ULongLongValidator
+ validator = new KexiUtils::LongLongValidator(m_lineedit);
+ }
+ else {
+ validator = new KIntValidator(m_lineedit);
+ }
+ m_lineedit->setValidator( validator );
+ }*/
+ }
+ else {//default: text
+ text += add;
+ }
+ }
+
+ return text;
+}
+#endif
+
+void KexiInputTableEdit::paintEvent ( QPaintEvent * /*e*/ )
+{
+ QPainter p(this);
+ p.setPen( QPen( colorGroup().text() ) );
+ p.drawRect( rect() );
+}
+
+void
+KexiInputTableEdit::setRestrictedCompletion()
+{
+#if 0 //js TODO
+kdDebug() << "KexiInputTableEdit::setRestrictedCompletion()" << endl;
+// KLineEdit *content = static_cast<KLineEdit*>(m_view);
+ if(m_cview->text().isEmpty())
+ return;
+
+ kdDebug() << "KexiInputTableEdit::setRestrictedCompletion(): something to do" << endl;
+
+ m_cview->useGlobalKeyBindings();
+
+ QStringList newC;
+ QStringList::ConstIterator it, end( m_comp.constEnd() );
+ for( it = m_comp.constBegin(); it != end; ++it)
+ {
+ if((*it).startsWith(m_cview->text()))
+ newC.append(*it);
+ }
+ m_cview->setCompletedItems(newC);
+#endif
+}
+
+void
+KexiInputTableEdit::completed(const QString &s)
+{
+// kdDebug() << "KexiInputTableEdit::completed(): " << s << endl;
+ m_lineedit->setText(s);
+}
+
+bool KexiInputTableEdit::valueChanged()
+{
+ //not needed? if (m_lineedit->text()!=m_origValue.toString())
+ //not needed? return true;
+ return KexiTableEdit::valueChanged();
+}
+
+bool KexiInputTableEdit::valueIsNull()
+{
+ return m_lineedit->text().isNull();
+}
+
+bool KexiInputTableEdit::valueIsEmpty()
+{
+ return !m_lineedit->text().isNull() && m_lineedit->text().isEmpty();
+}
+
+QVariant KexiInputTableEdit::value()
+{
+ if (field()->isFPNumericType()) {//==KexiDB::Field::Double || m_type==KexiDB::Field::Float) {
+ //! js @todo PRESERVE PRECISION!
+ QString txt = m_lineedit->text();
+ if (m_decsym!=".")
+ txt = txt.replace(m_decsym,".");//convert back
+ bool ok;
+ const double result = txt.toDouble(&ok);
+ return ok ? QVariant(result) : QVariant();
+ }
+ else if (field()->isIntegerType()) {
+//! @todo check constraints
+ bool ok;
+ if (KexiDB::Field::BigInteger == field()->type()) {
+ if (field()->isUnsigned()) {
+ const Q_ULLONG result = m_lineedit->text().toULongLong(&ok);
+ return ok ? QVariant(result) : QVariant();
+ }
+ else {
+ const Q_LLONG result = m_lineedit->text().toLongLong(&ok);
+ return ok ? QVariant(result) : QVariant();
+ }
+ }
+ if (KexiDB::Field::Integer == field()->type()) {
+ if (field()->isUnsigned()) {
+ const uint result = m_lineedit->text().toUInt(&ok);
+ return ok ? QVariant(result) : QVariant();
+ }
+ }
+ //default: signed int
+ const int result = m_lineedit->text().toInt(&ok);
+ return ok ? QVariant(result) : QVariant();
+ }
+ //default: text
+ return m_lineedit->text();
+}
+
+void
+KexiInputTableEdit::clear()
+{
+ m_lineedit->clear();
+}
+
+bool KexiInputTableEdit::cursorAtStart()
+{
+ return m_lineedit->cursorPosition()==0;
+}
+
+bool KexiInputTableEdit::cursorAtEnd()
+{
+ return m_lineedit->cursorPosition()==(int)m_lineedit->text().length();
+}
+
+QSize KexiInputTableEdit::totalSize()
+{
+ if (!m_lineedit)
+ return size();
+ return m_lineedit->size();
+}
+
+void KexiInputTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
+{
+ Q_UNUSED(visibleValue);
+//! @todo handle rich text?
+ qApp->clipboard()->setText( m_textFormatter.valueToText(value, QString::null) );
+}
+
+void KexiInputTableEdit::handleAction(const QString& actionName)
+{
+ const bool alreadyVisible = m_lineedit->isVisible();
+
+ if (actionName=="edit_paste") {
+ if (!alreadyVisible) { //paste as the entire text if the cell was not in edit mode
+ emit editRequested();
+ m_lineedit->clear();
+ }
+ m_lineedit->paste();
+ }
+ else if (actionName=="edit_cut") {
+//! @todo handle rich text?
+ if (!alreadyVisible) { //cut the entire text if the cell was not in edit mode
+ emit editRequested();
+ m_lineedit->selectAll();
+ }
+ m_lineedit->cut();
+ }
+}
+
+bool KexiInputTableEdit::showToolTipIfNeeded(const QVariant& value, const QRect& rect,
+ const QFontMetrics& fm, bool focused)
+{
+ QString text( value.type()==QVariant::String ? value.toString()
+ : m_textFormatter.valueToText(value, QString::null) );
+ QRect internalRect(rect);
+ internalRect.setLeft(rect.x()+leftMargin());
+ internalRect.setWidth(internalRect.width()-rightMargin(focused)-2*3);
+ kexidbg << rect << " " << internalRect << " " << fm.width(text) << endl;
+ return fm.width(text) > internalRect.width();
+}
+
+void KexiInputTableEdit::moveCursorToEnd()
+{
+ m_lineedit->end(false/*!mark*/);
+}
+
+void KexiInputTableEdit::moveCursorToStart()
+{
+ m_lineedit->home(false/*!mark*/);
+}
+
+void KexiInputTableEdit::selectAll()
+{
+ m_lineedit->selectAll();
+}
+
+KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiInputEditorFactoryItem, KexiInputTableEdit)
+
+#include "kexiinputtableedit.moc"
diff --git a/kexi/widget/tableview/kexiinputtableedit.h b/kexi/widget/tableview/kexiinputtableedit.h
new file mode 100644
index 00000000..df770287
--- /dev/null
+++ b/kexi/widget/tableview/kexiinputtableedit.h
@@ -0,0 +1,126 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003-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 KEXIINPUTTABLEEDIT_H
+#define KEXIINPUTTABLEEDIT_H
+
+#include <klineedit.h>
+#include <qvariant.h>
+
+#include "kexitableedit.h"
+#include "kexicelleditorfactory.h"
+#include "kexitextformatter.h"
+
+/*! @short General purpose cell editor using line edit widget.
+*/
+class KEXIDATATABLE_EXPORT KexiInputTableEdit : public KexiTableEdit
+{
+ Q_OBJECT
+
+ public:
+ KexiInputTableEdit(KexiTableViewColumn &column, QWidget *parent=0);
+
+ virtual ~KexiInputTableEdit();
+
+#if 0
+//moved to KexiTextFormatter
+ /*! \return text for \a value and \a field.
+ \a add is a text that should be added to the value if possible.
+ Used in setValueInternal(), by form widgets and for reporting/printing. */
+ static QString valueToText(KexiDB::Field* field, const QVariant& value, const QString& add);
+#endif
+
+ virtual bool valueChanged();
+
+ //! \return true if editor's value is null (not empty)
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not null).
+ //! Only few field types can accept "EMPTY" property
+ //! (check this with KexiDB::Field::hasEmptyProperty()),
+ virtual bool valueIsEmpty();
+
+ virtual QVariant value();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+
+// virtual bool eventFilter(QObject* watched, QEvent* e);
+//js void end(bool mark);
+//js void backspace();
+ virtual void clear();
+
+ /*! \return total size of this editor, including any buttons, etc. (if present). */
+ virtual QSize totalSize();
+
+ /*! Handles action having standard name \a actionName.
+ Action could be: "edit_cut", "edit_paste", etc. */
+ virtual void handleAction(const QString& actionName);
+
+ /*! Handles copy action for value. The \a value is copied to clipboard in format appropriate
+ for the editor's impementation, e.g. for image cell it can be a pixmap.
+ \a visibleValue is unused here. Reimplemented after KexiTableEdit. */
+ virtual void handleCopyAction(const QVariant& value, const QVariant& visibleValue);
+
+ /*! Shows a special tooltip for \a value if needed, i.e. if the value could not fit inside \a rect
+ for a given font metrics \a fm.
+ \return true a normal tooltip should be displayed (using QToolTip,) and false if
+ no tooltip should be displayed or a custom tooltip was displayed internally (not yet supported).
+ This implementation converts the value to text using valueToText() if \a calue is not string to see
+ whether it can fit inside the cell's \a rect.
+ If the cell is currentl focused (selected), \a focused is true. */
+ virtual bool showToolTipIfNeeded(const QVariant& value, const QRect& rect, const QFontMetrics& fm,
+ bool focused);
+
+ public slots:
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToEnd();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToStart();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void selectAll();
+
+ protected slots:
+ void setRestrictedCompletion();
+ void completed(const QString &);
+
+ protected:
+ //! initializes this editor with \a add value
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ void showHintButton();
+ void init();
+ virtual void paintEvent( QPaintEvent *e );
+
+ KexiTextFormatter m_textFormatter;
+ bool m_calculatedCell;
+ QString m_decsym; //! decimal symbol
+ QString m_origText; //! orig. Line Edit's text after conversion - for easy comparing
+ KLineEdit *m_lineedit;
+
+ signals:
+ void hintClicked();
+};
+
+KEXI_DECLARE_CELLEDITOR_FACTORY_ITEM(KexiInputEditorFactoryItem)
+
+#endif
diff --git a/kexi/widget/tableview/kexitableedit.cpp b/kexi/widget/tableview/kexitableedit.cpp
new file mode 100644
index 00000000..8c3f5612
--- /dev/null
+++ b/kexi/widget/tableview/kexitableedit.cpp
@@ -0,0 +1,237 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Peter Simonsson <psn@linux.se>
+ 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 "kexitableedit.h"
+#include "kexidataawareobjectiface.h"
+#include <kexidb/field.h>
+#include <kexidb/utils.h>
+
+#include <qpalette.h>
+#include <qpainter.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+KexiTableEdit::KexiTableEdit(KexiTableViewColumn &column, QWidget* parent)
+: QWidget(dynamic_cast<QScrollView*>(parent) ? dynamic_cast<QScrollView*>(parent)->viewport() : parent)
+ ,m_column(&column)
+// ,m_field(&f)
+// ,m_type(f.type()) //copied because the rest of code uses m_type
+ ,m_scrollView(dynamic_cast<QScrollView*>(parent))
+ ,m_usesSelectedTextColor(true)
+ ,m_view(0)
+// ,m_hasFocusableWidget(true)
+// ,m_acceptEditorAfterDeleteContents(false)
+{
+ setPaletteBackgroundColor( palette().color(QPalette::Active, QColorGroup::Base) );
+ installEventFilter(this);
+
+ //margins
+ if (displayedField()->isFPNumericType()) {
+#ifdef Q_WS_WIN
+ m_leftMargin = 0;
+#else
+ m_leftMargin = 0;
+#endif
+ }
+ else if (displayedField()->isIntegerType()) {
+#ifdef Q_WS_WIN
+ m_leftMargin = 1;
+#else
+ m_leftMargin = 0;
+#endif
+ }
+ else {//default
+#ifdef Q_WS_WIN
+ m_leftMargin = 5;
+#else
+ m_leftMargin = 5;
+#endif
+ }
+
+ m_rightMargin = 0;
+ m_rightMarginWhenFocused = 0;
+}
+
+KexiTableEdit::~KexiTableEdit()
+{
+}
+
+KexiDB::Field *KexiTableEdit::displayedField() const
+{
+ if (m_column->visibleLookupColumnInfo)
+ return m_column->visibleLookupColumnInfo->field; //mainly for lookup field in KexiComboBoxTableEdit:
+
+ return m_column->field(); //typical case
+}
+
+void KexiTableEdit::setViewWidget(QWidget *v)
+{
+ m_view = v;
+ m_view->move(0,0);
+ m_view->installEventFilter(this);
+ setFocusProxy(m_view);
+}
+
+void KexiTableEdit::moveChild( QWidget * child, int x, int y )
+{
+ if (m_scrollView)
+ m_scrollView->moveChild(child, x, y);
+}
+
+void KexiTableEdit::resize(int w, int h)
+{
+ QWidget::resize(w, h);
+ if (m_view) {
+ if (!layout()) { //if there is layout (eg. KexiInputTableEdit), resize is automatic
+ m_view->move(0,0);
+ m_view->resize(w, h);
+ }
+ }
+}
+
+bool
+KexiTableEdit::eventFilter(QObject* watched, QEvent* e)
+{
+/* if (watched == m_view) {
+ if(e->type() == QEvent::KeyPress) {
+ QKeyEvent* ev = static_cast<QKeyEvent*>(e);
+// if (ev->key()==Key_Tab) {
+
+// }
+ }
+ }*/
+
+ if(watched == this)
+ {
+ if(e->type() == QEvent::KeyPress)
+ {
+ QKeyEvent* ev = static_cast<QKeyEvent*>(e);
+
+ if(ev->key() == Key_Escape)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return false;
+// return QWidget::eventFilter(watched, e);
+}
+
+void KexiTableEdit::paintFocusBorders( QPainter *p, QVariant &, int x, int y, int w, int h )
+{
+ p->drawRect(x, y, w, h);
+}
+
+void KexiTableEdit::setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &/*x*/, int &y_offset, int &w, int &h )
+{
+ Q_UNUSED(p);
+ Q_UNUSED(focused);
+ Q_UNUSED(h);
+ KexiDB::Field *realField = displayedField();
+
+#ifdef Q_WS_WIN
+// x = 1;
+ y_offset = -1;
+#else
+// x = 1;
+ y_offset = 0;
+#endif
+
+ if (realField->isFPNumericType()) {
+//! @todo ADD OPTION to displaying NULL VALUES as e.g. "(null)"
+ if (!val.isNull()) {
+ txt = KexiDB::formatNumberForVisibleDecimalPlaces(
+ val.toDouble(), realField->visibleDecimalPlaces());
+ }
+ w -= 6;
+ align |= AlignRight;
+ }
+ else if (realField->isIntegerType()) {
+ Q_LLONG num = val.toLongLong();
+ w -= 6;
+ align |= AlignRight;
+ if (!val.isNull())
+ txt = QString::number(num);
+ }
+ else {//default:
+ if (!val.isNull()) {
+ txt = val.toString();
+ }
+ align |= AlignLeft;
+ }
+}
+
+void KexiTableEdit::paintSelectionBackground( QPainter *p, bool /*focused*/,
+ const QString& txt, int align, int x, int y_offset, int w, int h, const QColor& fillColor,
+ const QFontMetrics &fm, bool readOnly, bool fullRowSelection )
+{
+ if (!readOnly && !fullRowSelection && !txt.isEmpty()) {
+ QRect bound=fm.boundingRect(x, y_offset, w - (x+x), h, align, txt);
+ bound.setY(0);
+ bound.setWidth( QMIN( bound.width()+2, w - (x+x)+1 ) );
+ if (align & Qt::AlignLeft) {
+ bound.setX(bound.x()-1);
+ }
+ else if (align & Qt::AlignRight) {
+ bound.moveLeft( w - bound.width() ); //move to left, if too wide
+ }
+//TODO align center
+ bound.setHeight(h-1);
+ p->fillRect(bound, fillColor);
+ }
+ else if (fullRowSelection) {
+ p->fillRect(0, 0, w, h, fillColor);
+ }
+}
+
+int KexiTableEdit::widthForValue( QVariant &val, const QFontMetrics &fm )
+{
+ return fm.width( val.toString() );
+}
+
+void KexiTableEdit::repaintRelatedCell()
+{
+ if (dynamic_cast<KexiDataAwareObjectInterface*>(m_scrollView))
+ dynamic_cast<KexiDataAwareObjectInterface*>(m_scrollView)->updateCurrentCell();
+}
+
+bool KexiTableEdit::showToolTipIfNeeded(const QVariant& value, const QRect& rect, const QFontMetrics& fm,
+ bool focused)
+{
+ Q_UNUSED(value);
+ Q_UNUSED(rect);
+ Q_UNUSED(fm);
+ Q_UNUSED(focused);
+ return false;
+}
+
+int KexiTableEdit::rightMargin(bool focused) const
+{
+ return focused ? m_rightMarginWhenFocused : m_rightMargin;
+}
+
+#include "kexitableedit.moc"
diff --git a/kexi/widget/tableview/kexitableedit.h b/kexi/widget/tableview/kexitableedit.h
new file mode 100644
index 00000000..ef38a11f
--- /dev/null
+++ b/kexi/widget/tableview/kexitableedit.h
@@ -0,0 +1,233 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Peter Simonsson <psn@linux.se>
+ 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 _KEXITABLEEDIT_H_
+#define _KEXITABLEEDIT_H_
+
+#include <kexidataiteminterface.h>
+
+#include <qvariant.h>
+#include <qscrollview.h>
+
+#include "kexitableviewdata.h"
+
+namespace KexiDB {
+ class Field;
+ class QueryColumnInfo;
+}
+
+/*! @short Abstract class for a cell editor.
+ Handles cell painting and displaying the editor widget.
+*/
+class KEXIDATATABLE_EXPORT KexiTableEdit : public QWidget, public KexiDataItemInterface
+{
+ Q_OBJECT
+
+ public:
+ KexiTableEdit(KexiTableViewColumn &column, QWidget* parent = 0);
+
+ virtual ~KexiTableEdit();
+
+ //! Implemented for KexiDataItemInterface.
+ //! \return field information for this item
+ virtual KexiDB::Field *field() const { return m_column->field(); }
+
+ /*! A rich field information for db-aware data.
+ For not-db-aware data it is always 0 (use field() instead. */
+ virtual KexiDB::QueryColumnInfo *columnInfo() const { return m_column->columnInfo; }
+
+ //! Implemented for KexiDataItemInterface.
+ //! Does nothing because instead KexiTableViewColumn is used to get field's schema.
+ virtual void setColumnInfo(KexiDB::QueryColumnInfo *) { }
+
+ //! \return column information for this item
+ //! (extended information, comparing to field()).
+ inline KexiTableViewColumn *column() const { return m_column; }
+
+ /*! \return displayed field. This is equal to field() in typical case but can return a different field
+ definition if the column contains a lookup field. This distiction is especially used for
+ displaying data dependent on the type and specifics of the field definition
+ (e.g. text type versus integer type). Note that to compute the editor's value
+ we still use field(). */
+ KexiDB::Field *displayedField() const;
+
+ /*! Reimplemented: resizes a view(). */
+ virtual void resize(int w, int h);
+
+ /*! \return the view widget of this editor, e.g. line edit widget. */
+ virtual QWidget* widget() { return m_view; }
+
+ /*! Hides item's widget, if available. */
+ inline virtual void hideWidget() { hide(); }
+
+ /*! Shows item's widget, if available. */
+ inline virtual void showWidget() { show(); }
+
+ /*! Paints a border for the cell described by \a x, \a y, \a w, \a h on \a p painter.
+ The cell's value is \a val (may be useful if you want to reimplement this method).
+ */
+ virtual void paintFocusBorders( QPainter *p, QVariant &cal, int x, int y, int w, int h );
+
+ /*! For reimplementation.
+ Sets up and paints cell's contents using context of \a val value.
+ \a focused is true if the cell is focused. \a align is set using Qt::AlignmentFlags.
+ Some additional things may be painted using \a p,
+ but it is not needed to paint the text (this is done automatically outside of this method).
+
+ Before calling, \a x, \a y_offset, \a w, \a h parameters are initialized,
+ but you can tune these values depending on the context.
+ You should set \a txt to a text representation of \a val,
+ otherwise no text will be painted.
+
+ \a p can be 0 - in this case no painting should be performed, becasue caller only expects
+ that \a x, \a y_offset, \a w, \a h, \a txt parameters are tuned, if needed.
+ \a p painter's pen is set to foreground color (usually black) that should be used to paint
+ foreground information, if needed. For example boolean editor widget paints
+ a rectangle using this color. */
+ virtual void setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h );
+
+ /*! \return true if "selected text" color should be used to paint contents of the editor.
+ True by default. It's false e.g. in boolean editor, where no selection is painted
+ using paintSelectionBackground().
+ This flag is set in editor's constructor and checked in KexiTableView::paintCell().
+ Depending on it, appropriate ("text" or "selected text" color is set for painter) before
+ setupContents() is called. */
+ bool usesSelectedTextColor() const { return m_usesSelectedTextColor; }
+
+ /*! For reimplementation.
+ Paints selection's background using \a p. Most parameters are similar to these from
+ setupContents(). */
+ virtual void paintSelectionBackground( QPainter *p, bool focused, const QString& txt,
+ int align, int x, int y_offset, int w, int h, const QColor& fillColor,
+ const QFontMetrics &fm, bool readOnly, bool fullRowSelection );
+
+ /*! Sometimes, editor can contain non-standard margin, for example combobox editor contains
+ dropdown button at the right side. \return left margin's size;
+ 0 by default. For reimplementation. */
+ int leftMargin() const { return m_leftMargin; }
+
+ /*! Sometimes, editor can contain non-standard margin, for example combobox editor contains
+ dropdown button at the right side. THe dropdown button's width is counted only if \a focused is true.
+ \return right margin's size; 0 by default. For reimplementation. */
+ int rightMargin(bool focused) const;
+
+ /*! Handles \a ke key event that came over the column that is bound to this editor.
+ For implementation: true should be returned if \a ke should be accepted.
+ If \a editorActive is true, this editor is currently active, i.e. the table view is in edit mode.
+ By default false is returned. */
+ virtual bool handleKeyPress( QKeyEvent* ke, bool editorActive ) {
+ Q_UNUSED(ke); Q_UNUSED(editorActive); return false; }
+
+ /*! Handles double click request coming from the table view.
+ \return true if it has been consumed.
+ Reimplemented in KexiBlobTableEdit (to execute "insert file" action. */
+ virtual bool handleDoubleClick() { return false; }
+
+ /*! Handles copy action for value. The \a value is copied to clipboard in format appropriate
+ for the editor's impementation, e.g. for image cell it can be a pixmap.
+ For a special case (combo box), \a visibleValue can be provided,
+ so it can be copied to the clipboard instead of unreadable \a value.
+ For reimplementation. */
+ virtual void handleCopyAction(const QVariant& value, const QVariant& visibleValue) = 0;
+
+ /*! \return width of \a value. For the default implementation \a val is converted to a string
+ and width of this string is returned. */
+ virtual int widthForValue( QVariant &val, const QFontMetrics &fm );
+
+ /*! \return total size of this editor, including any buttons, etc. (if present).
+ Reimpelment this if you want to return more appropriate size. This impelmentation just
+ returns QWidget::size(). */
+ virtual QSize totalSize() { return QWidget::size(); }
+
+ /*! Shows a special tooltip for \a value if needed, i.e. if the value could not fit inside \a rect
+ for a given font metrics \a fm.
+ \return true a normal tooltip should be displayed (using QToolTip,) and false if
+ no tooltip should be displayed or a custom tooltip was displayed internally (not yet supported).
+ Default implementation does nothing and returns false.
+ If the cell is currentl focused (selected), \a focused is true. */
+ virtual bool showToolTipIfNeeded(const QVariant& value, const QRect& rect, const QFontMetrics& fm,
+ bool focused);
+
+ /*! Created internal editor for this editor is needed. This method is only implemented
+ in KexiComboBoxTableEdit since it's visible value differs from internal value,
+ so a different KexiTableEdit object is used to displaying the data. */
+ virtual void createInternalEditor(KexiDB::QuerySchema& schema) { Q_UNUSED(schema); }
+
+ signals:
+ void editRequested();
+ void cancelRequested();
+ void acceptRequested();
+
+ protected:
+ virtual bool eventFilter(QObject* watched, QEvent* e);
+
+ /*! Sets \a v as view widget for this editor. The view will be assigned as focus proxy
+ for the editor, its events will be filtered, it will be resized when neede, and so on. */
+ void setViewWidget(QWidget *v);
+
+ /*! Moves child widget within the viewport if the parent is scrollview (otherwise does nothing).
+ Use this for child widgets that are outside of this editor widget, instead of calling QWidget::move(). */
+ void moveChild( QWidget * child, int x, int y );
+
+ /*! Allows to force redrawing the related cell by the editor itself. Usable when the editor is not
+ displayed by a QWidget but rather by table view cell itself, for example KexiBlobTableEdit. */
+ void repaintRelatedCell();
+
+ KexiTableViewColumn *m_column;
+ int m_leftMargin;
+ int m_rightMargin, m_rightMarginWhenFocused;
+ QScrollView* m_scrollView; //!< may be 0 if the parent is not a scrollview
+ bool m_usesSelectedTextColor : 1; //!< set in ctor, @see usesSelectedTextColor()
+
+ private:
+ QWidget* m_view;
+};
+
+//! Declaration of cell editor factory
+#define KEXI_DECLARE_CELLEDITOR_FACTORY_ITEM(factoryclassname) \
+ class factoryclassname : public KexiCellEditorFactoryItem \
+ { \
+ public: \
+ factoryclassname(); \
+ virtual ~factoryclassname(); \
+ \
+ protected: \
+ virtual KexiTableEdit* createEditor(KexiTableViewColumn &column, QWidget* parent = 0); \
+ };
+
+//! Implementation of cell editor factory
+#define KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(factoryclassname, itemclassname) \
+factoryclassname::factoryclassname() \
+ : KexiCellEditorFactoryItem() \
+{ \
+ m_className = "" #itemclassname ""; \
+} \
+\
+factoryclassname::~factoryclassname() \
+{} \
+\
+KexiTableEdit* factoryclassname::createEditor( \
+ KexiTableViewColumn &column, QWidget* parent) \
+{ \
+ return new itemclassname(column, parent); \
+}
+
+#endif
diff --git a/kexi/widget/tableview/kexitableitem.cpp b/kexi/widget/tableview/kexitableitem.cpp
new file mode 100644
index 00000000..e1669a35
--- /dev/null
+++ b/kexi/widget/tableview/kexitableitem.cpp
@@ -0,0 +1,62 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003 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.
+
+ Original Author: Till Busch <till@bux.at>
+ Original Project: buX (www.bux.at)
+*/
+
+#include "kexitableitem.h"
+
+#include <kdebug.h>
+
+KexiTableItem::KexiTableItem(int numCols)
+: KexiTableItemBase(numCols)
+{
+//js m_userData=0;
+//js m_columns.resize(numCols);
+//js m_insertItem = false;
+//js m_pTable = 0;
+}
+
+KexiTableItem::~KexiTableItem()
+{
+}
+
+void
+KexiTableItem::init(int numCols)
+{
+ clear();
+ resize(numCols);
+}
+
+void
+KexiTableItem::clearValues()
+{
+ init(count());
+}
+
+void
+KexiTableItem::debug() const
+{
+ QString s = QString("KexiTableItem (%1 items)").arg(size());
+ for (uint i = 0; i < size(); i++)
+ s.append( QString::number(i)+":"+at(i).toString()+" " );
+ kexidbg << s << endl;
+}
diff --git a/kexi/widget/tableview/kexitableitem.h b/kexi/widget/tableview/kexitableitem.h
new file mode 100644
index 00000000..00074e82
--- /dev/null
+++ b/kexi/widget/tableview/kexitableitem.h
@@ -0,0 +1,58 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003 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.
+
+ Original Author: Till Busch <till@bux.at>
+ Original Project: buX (www.bux.at)
+*/
+
+#ifndef KEXITABLEITEM_H
+#define KEXITABLEITEM_H
+
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qvaluevector.h>
+#include <qvariant.h>
+
+#include <kexidb/connection.h>
+
+typedef KexiDB::RowData KexiTableItemBase;
+
+class KEXIDATATABLE_EXPORT KexiTableItem : public KexiTableItemBase
+{
+ public:
+ ~KexiTableItem();
+
+ /*! Clears existing column values and inits new \a numCols
+ columns with empty values. ist of values is resized to \a numCols. */
+ void init(int numCols);
+
+ /*! Clears existing column values, current number of columns is preserved. */
+ void clearValues();
+
+ /*! Prints debug string for this item. */
+ void debug() const;
+
+ protected:
+ KexiTableItem(int numCols);
+
+ friend class KexiTableViewData;
+};
+
+#endif
diff --git a/kexi/widget/tableview/kexitableview.cpp b/kexi/widget/tableview/kexitableview.cpp
new file mode 100644
index 00000000..108b1696
--- /dev/null
+++ b/kexi/widget/tableview/kexitableview.cpp
@@ -0,0 +1,2607 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Till Busch <till@bux.at>
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@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.
+
+ Original Author: Till Busch <till@bux.at>
+ Original Project: buX (www.bux.at)
+*/
+
+#include <qpainter.h>
+#include <qkeycode.h>
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qwmatrix.h>
+#include <qtimer.h>
+#include <qpopupmenu.h>
+#include <qcursor.h>
+#include <qstyle.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qwhatsthis.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+
+#ifndef KEXI_NO_PRINT
+# include <kprinter.h>
+#endif
+
+#include "kexitableview.h"
+#include <kexiutils/utils.h>
+#include <kexiutils/validator.h>
+
+#include "kexicelleditorfactory.h"
+#include "kexitableviewheader.h"
+#include "kexitableview_p.h"
+#include <widget/utils/kexirecordmarker.h>
+#include <widget/utils/kexidisplayutils.h>
+#include <kexidb/cursor.h>
+
+KexiTableView::Appearance::Appearance(QWidget *widget)
+ : alternateBackgroundColor( KGlobalSettings::alternateBackgroundColor() )
+{
+ //set defaults
+ if (qApp) {
+ QPalette p = widget ? widget->palette() : qApp->palette();
+ baseColor = p.active().base();
+ textColor = p.active().text();
+ borderColor = QColor(200,200,200);
+ emptyAreaColor = p.active().color(QColorGroup::Base);
+ rowHighlightingColor = KexiUtils::blendedColors(p.active().highlight(), baseColor, 33, 66);
+ rowMouseOverHighlightingColor = KexiUtils::blendedColors(p.active().highlight(), baseColor, 10, 90);
+ rowMouseOverAlternateHighlightingColor = KexiUtils::blendedColors(p.active().highlight(), alternateBackgroundColor, 10, 90);
+ rowHighlightingTextColor = textColor;
+ rowMouseOverHighlightingTextColor = textColor;
+ }
+ backgroundAltering = true;
+ rowMouseOverHighlightingEnabled = true;
+ rowHighlightingEnabled = true;
+ persistentSelections = true;
+ navigatorEnabled = true;
+ fullRowSelection = false;
+ gridEnabled = true;
+}
+
+//-----------------------------------------
+
+//! @internal A special What's This class displaying information about a given column
+class KexiTableView::WhatsThis : public QWhatsThis
+{
+ public:
+ WhatsThis(KexiTableView* tv) : QWhatsThis(tv), m_tv(tv)
+ {
+ Q_ASSERT(tv);
+ }
+ virtual ~WhatsThis()
+ {
+ }
+ virtual QString text( const QPoint & pos)
+ {
+ const int leftMargin = m_tv->verticalHeaderVisible() ? m_tv->verticalHeader()->width() : 0;
+ //const int topMargin = m_tv->horizontalHeaderVisible() ? m_tv->d->pTopHeader->height() : 0;
+ //const int bottomMargin = m_tv->d->appearance.navigatorEnabled ? m_tv->m_navPanel->height() : 0;
+ if (KexiUtils::hasParent(m_tv->verticalHeader(), m_tv->childAt(pos))) {
+ return i18n("Contains a pointer to the currently selected row");
+ }
+ else if (KexiUtils::hasParent(m_tv->m_navPanel, m_tv->childAt(pos))) {
+ return i18n("Row navigator");
+// return QWhatsThis::textFor(m_tv->m_navPanel, QPoint( pos.x(), pos.y() - m_tv->height() + bottomMargin ));
+ }
+ KexiDB::Field *f = m_tv->field( m_tv->columnAt(pos.x()-leftMargin) );
+ if (!f)
+ return QString::null;
+ return f->description().isEmpty() ? f->captionOrName() : f->description();
+ }
+ protected:
+ KexiTableView *m_tv;
+};
+
+//-----------------------------------------
+
+KexiTableViewCellToolTip::KexiTableViewCellToolTip( KexiTableView * tableView )
+ : QToolTip(tableView->viewport())
+ , m_tableView(tableView)
+{
+}
+
+KexiTableViewCellToolTip::~KexiTableViewCellToolTip()
+{
+ remove(parentWidget());
+}
+
+void KexiTableViewCellToolTip::maybeTip( const QPoint & p )
+{
+ const QPoint cp( m_tableView->viewportToContents( p ) );
+ const int row = m_tableView->rowAt( cp.y(), true/*ignoreEnd*/ );
+ const int col = m_tableView->columnAt( cp.x() );
+
+ //show tooltip if needed
+ if (col>=0 && row>=0) {
+ KexiTableEdit *editor = m_tableView->tableEditorWidget( col );
+ const bool insertRowSelected = m_tableView->isInsertingEnabled() && row==m_tableView->rows();
+ KexiTableItem *item = insertRowSelected ? m_tableView->m_insertItem : m_tableView->itemAt( row );
+ if (editor && item && (col < (int)item->count())) {
+ int w = m_tableView->columnWidth( col );
+ int h = m_tableView->rowHeight();
+ int x = 0;
+ int y_offset = 0;
+ int align = Qt::SingleLine | Qt::AlignVCenter;
+ QString txtValue;
+ QVariant cellValue;
+ KexiTableViewColumn *tvcol = m_tableView->column(col);
+ if (!m_tableView->getVisibleLookupValue(cellValue, editor, item, tvcol))
+ cellValue = insertRowSelected ? editor->displayedField()->defaultValue() : item->at(col); //display default value if available
+ const bool focused = m_tableView->selectedItem() == item && col == m_tableView->currentColumn();
+ editor->setupContents( 0, focused, cellValue, txtValue, align, x, y_offset, w, h );
+ QRect realRect(m_tableView->columnPos(col)-m_tableView->contentsX(),
+ m_tableView->rowPos(row)-m_tableView->contentsY(), w, h); //m_tableView->cellGeometry( row, col ));
+ if (editor->showToolTipIfNeeded(
+ txtValue.isEmpty() ? item->at(col) : QVariant(txtValue),
+ realRect, m_tableView->fontMetrics(), focused))
+ {
+ QString squeezedTxtValue;
+ if (txtValue.length() > 50)
+ squeezedTxtValue = txtValue.left(100) + "...";
+ else
+ squeezedTxtValue = txtValue;
+ tip( realRect, squeezedTxtValue );
+ }
+ }
+ }
+}
+
+//-----------------------------------------
+
+KexiTableView::KexiTableView(KexiTableViewData* data, QWidget* parent, const char* name)
+: QScrollView(parent, name, /*Qt::WRepaintNoErase | */Qt::WStaticContents /*| Qt::WResizeNoErase*/)
+, KexiRecordNavigatorHandler()
+, KexiSharedActionClient()
+, KexiDataAwareObjectInterface()
+{
+//not needed KexiTableView::initCellEditorFactories();
+
+ d = new KexiTableViewPrivate(this);
+
+ connect( kapp, SIGNAL( settingsChanged(int) ), SLOT( slotSettingsChanged(int) ) );
+ slotSettingsChanged(KApplication::SETTINGS_SHORTCUTS);
+
+ m_data = new KexiTableViewData(); //to prevent crash because m_data==0
+ m_owner = true; //-this will be deleted if needed
+
+ setResizePolicy(Manual);
+ viewport()->setBackgroundMode(Qt::NoBackground);
+// viewport()->setFocusPolicy(StrongFocus);
+ viewport()->setFocusPolicy(WheelFocus);
+ setFocusPolicy(WheelFocus); //<--- !!!!! important (was NoFocus),
+ // otherwise QApplication::setActiveWindow() won't activate
+ // this widget when needed!
+// setFocusProxy(viewport());
+ viewport()->installEventFilter(this);
+
+ //setup colors defaults
+ setBackgroundMode(Qt::PaletteBackground);
+// setEmptyAreaColor(d->appearance.baseColor);//palette().active().color(QColorGroup::Base));
+
+// d->baseColor = colorGroup().base();
+// d->textColor = colorGroup().text();
+
+// d->altColor = KGlobalSettings::alternateBackgroundColor();
+// d->grayColor = QColor(200,200,200);
+ d->diagonalGrayPattern = QBrush(d->appearance.borderColor, Qt::BDiagPattern);
+
+ setLineWidth(1);
+ horizontalScrollBar()->installEventFilter(this);
+ horizontalScrollBar()->raise();
+ verticalScrollBar()->raise();
+
+ //context menu
+ m_popupMenu = new KPopupMenu(this, "contextMenu");
+#if 0 //moved to mainwindow's actions
+ d->menu_id_addRecord = m_popupMenu->insertItem(i18n("Add Record"), this, SLOT(addRecord()), Qt::CTRL+Qt::Key_Insert);
+ d->menu_id_removeRecord = m_popupMenu->insertItem(
+ kapp->iconLoader()->loadIcon("button_cancel", KIcon::Small),
+ i18n("Remove Record"), this, SLOT(removeRecord()), Qt::CTRL+Qt::Key_Delete);
+#endif
+
+#ifdef Q_WS_WIN
+ d->rowHeight = fontMetrics().lineSpacing() + 4;
+#else
+ d->rowHeight = fontMetrics().lineSpacing() + 1;
+#endif
+
+ if(d->rowHeight < 17)
+ d->rowHeight = 17;
+
+ d->pUpdateTimer = new QTimer(this);
+
+// setMargins(14, fontMetrics().height() + 4, 0, 0);
+
+ // Create headers
+ m_horizontalHeader = new KexiTableViewHeader(this, "topHeader");
+ m_horizontalHeader->setSelectionBackgroundColor( palette().active().highlight() );
+ m_horizontalHeader->setOrientation(Qt::Horizontal);
+ m_horizontalHeader->setTracking(false);
+ m_horizontalHeader->setMovingEnabled(false);
+ connect(m_horizontalHeader, SIGNAL(sizeChange(int,int,int)), this, SLOT(slotTopHeaderSizeChange(int,int,int)));
+
+ m_verticalHeader = new KexiRecordMarker(this, "rm");
+ m_verticalHeader->setSelectionBackgroundColor( palette().active().highlight() );
+ m_verticalHeader->setCellHeight(d->rowHeight);
+// m_verticalHeader->setFixedWidth(d->rowHeight);
+ m_verticalHeader->setCurrentRow(-1);
+
+ setMargins(
+ QMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight),
+ m_horizontalHeader->sizeHint().height(), 0, 0);
+
+ setupNavigator();
+
+// setMinimumHeight(horizontalScrollBar()->height() + d->rowHeight + topMargin());
+
+// navPanelLyr->addStretch(25);
+// enableClipper(true);
+
+ if (data)
+ setData( data );
+
+#if 0//(js) doesn't work!
+ d->scrollTimer = new QTimer(this);
+ connect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(slotAutoScroll()));
+#endif
+
+// setBackgroundAltering(true);
+// setFullRowSelectionEnabled(false);
+
+ setAcceptDrops(true);
+ viewport()->setAcceptDrops(true);
+
+ // Connect header, table and scrollbars
+ connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), m_horizontalHeader, SLOT(setOffset(int)));
+ connect(verticalScrollBar(), SIGNAL(valueChanged(int)), m_verticalHeader, SLOT(setOffset(int)));
+ connect(m_horizontalHeader, SIGNAL(sizeChange(int, int, int)), this, SLOT(slotColumnWidthChanged(int, int, int)));
+ connect(m_horizontalHeader, SIGNAL(sectionHandleDoubleClicked(int)), this, SLOT(slotSectionHandleDoubleClicked(int)));
+ connect(m_horizontalHeader, SIGNAL(clicked(int)), this, SLOT(sortColumnInternal(int)));
+
+ connect(d->pUpdateTimer, SIGNAL(timeout()), this, SLOT(slotUpdate()));
+
+// horizontalScrollBar()->show();
+ updateScrollBars();
+// resize(sizeHint());
+// updateContents();
+// setMinimumHeight(horizontalScrollBar()->height() + d->rowHeight + topMargin());
+
+//TMP
+//setVerticalHeaderVisible(false);
+//setHorizontalHeaderVisible(false);
+
+//will be updated by setAppearance: updateFonts();
+ setAppearance(d->appearance); //refresh
+
+ d->cellToolTip = new KexiTableViewCellToolTip(this);
+ new WhatsThis(this);
+}
+
+KexiTableView::~KexiTableView()
+{
+ cancelRowEdit();
+
+ KexiTableViewData *data = m_data;
+ m_data = 0;
+ if (m_owner) {
+ if (data)
+ data->deleteLater();
+ }
+ delete d;
+}
+
+void KexiTableView::clearVariables()
+{
+ KexiDataAwareObjectInterface::clearVariables();
+ d->clearVariables();
+}
+
+/*void KexiTableView::initActions(KActionCollection *ac)
+{
+ emit reloadActions(ac);
+}*/
+
+void KexiTableView::setupNavigator()
+{
+ updateScrollBars();
+
+ m_navPanel = new KexiRecordNavigator(this, leftMargin(), "navPanel");
+ m_navPanel->setRecordHandler(this);
+ m_navPanel->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Preferred);
+}
+
+void KexiTableView::initDataContents()
+{
+ updateWidgetContentsSize();
+
+ KexiDataAwareObjectInterface::initDataContents();
+
+ m_navPanel->showEditingIndicator(false);
+}
+
+void KexiTableView::addHeaderColumn(const QString& caption, const QString& description,
+ const QIconSet& icon, int width)
+{
+ const int nr = m_horizontalHeader->count();
+ if (icon.isNull())
+ m_horizontalHeader->addLabel(caption, width);
+ else
+ m_horizontalHeader->addLabel(icon, caption, width);
+
+ if (!description.isEmpty())
+ m_horizontalHeader->setToolTip(nr, description);
+}
+
+void KexiTableView::updateWidgetContentsSize()
+{
+ QSize s(tableSize());
+ resizeContents(s.width(), s.height());
+}
+
+void KexiTableView::slotRowsDeleted( const QValueList<int> &rows )
+{
+ viewport()->repaint();
+ updateWidgetContentsSize();
+ setCursorPosition(QMAX(0, (int)m_curRow - (int)rows.count()), -1, true);
+}
+
+
+/*void KexiTableView::addDropFilter(const QString &filter)
+{
+ d->dropFilters.append(filter);
+ viewport()->setAcceptDrops(true);
+}*/
+
+void KexiTableView::setFont( const QFont &font )
+{
+ QScrollView::setFont(font);
+ updateFonts(true);
+}
+
+void KexiTableView::updateFonts(bool repaint)
+{
+#ifdef Q_WS_WIN
+ d->rowHeight = fontMetrics().lineSpacing() + 4;
+#else
+ d->rowHeight = fontMetrics().lineSpacing() + 1;
+#endif
+ if (d->appearance.fullRowSelection) {
+ d->rowHeight -= 1;
+ }
+ if(d->rowHeight < 17)
+ d->rowHeight = 17;
+// if(d->rowHeight < 22)
+// d->rowHeight = 22;
+ setMargins(
+ QMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight),
+ m_horizontalHeader->sizeHint().height(), 0, 0);
+// setMargins(14, d->rowHeight, 0, 0);
+ m_verticalHeader->setCellHeight(d->rowHeight);
+
+ KexiDisplayUtils::initDisplayForAutonumberSign(d->autonumberSignDisplayParameters, this);
+ KexiDisplayUtils::initDisplayForDefaultValue(d->defaultValueDisplayParameters, this);
+
+ if (repaint)
+ updateContents();
+}
+
+void KexiTableView::updateAllVisibleRowsBelow(int row)
+{
+ //get last visible row
+ int r = rowAt(clipper()->height()+contentsY());
+ if (r==-1) {
+ r = rows()+1+(isInsertingEnabled()?1:0);
+ }
+ //update all visible rows below
+ int leftcol = m_horizontalHeader->sectionAt( m_horizontalHeader->offset() );
+// int row = m_curRow;
+ updateContents( columnPos( leftcol ), rowPos(row),
+ clipper()->width(), clipper()->height() - (rowPos(row) - contentsY()) );
+}
+
+void KexiTableView::clearColumnsInternal(bool /*repaint*/)
+{
+ while(m_horizontalHeader->count()>0)
+ m_horizontalHeader->removeLabel(0);
+}
+
+void KexiTableView::slotUpdate()
+{
+// kdDebug(44021) << " KexiTableView::slotUpdate() -- " << endl;
+// QSize s(tableSize());
+// viewport()->setUpdatesEnabled(false);
+/// resizeContents(s.width(), s.height());
+// viewport()->setUpdatesEnabled(true);
+
+ updateContents();
+ updateScrollBars();
+ if (m_navPanel)
+ m_navPanel->updateGeometry(leftMargin());
+// updateNavPanelGeometry();
+
+ updateWidgetContentsSize();
+// updateContents(0, contentsY()+clipper()->height()-2*d->rowHeight, clipper()->width(), d->rowHeight*3);
+
+ //updateGeometries();
+// updateContents(0, 0, viewport()->width(), contentsHeight());
+// updateGeometries();
+}
+
+int KexiTableView::currentLocalSortingOrder() const
+{
+ if (m_horizontalHeader->sortIndicatorSection()==-1)
+ return 0;
+ return (m_horizontalHeader->sortIndicatorOrder() == Qt::Ascending) ? 1 : -1;
+}
+
+void KexiTableView::setLocalSortingOrder(int col, int order)
+{
+ if (order == 0)
+ col = -1;
+ if (col>=0)
+ m_horizontalHeader->setSortIndicator(col, (order==1) ? Qt::Ascending : Qt::Descending);
+}
+
+int KexiTableView::currentLocalSortColumn() const
+{
+ return m_horizontalHeader->sortIndicatorSection();
+}
+
+void KexiTableView::updateGUIAfterSorting()
+{
+ int cw = columnWidth(m_curCol);
+ int rh = rowHeight();
+
+// m_verticalHeader->setCurrentRow(m_curRow);
+ center(columnPos(m_curCol) + cw / 2, rowPos(m_curRow) + rh / 2);
+// updateCell(oldRow, m_curCol);
+// updateCell(m_curRow, m_curCol);
+// slotUpdate();
+
+ updateContents();
+// d->pUpdateTimer->start(1,true);
+}
+
+QSizePolicy KexiTableView::sizePolicy() const
+{
+ // this widget is expandable
+ return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+}
+
+QSize KexiTableView::sizeHint() const
+{
+ const QSize &ts = tableSize();
+ int w = QMAX( ts.width() + leftMargin()+ verticalScrollBar()->sizeHint().width() + 2*2,
+ (m_navPanel->isVisible() ? m_navPanel->width() : 0) );
+ int h = QMAX( ts.height()+topMargin()+horizontalScrollBar()->sizeHint().height(),
+ minimumSizeHint().height() );
+ w = QMIN( w, qApp->desktop()->width()*3/4 ); //stretch
+ h = QMIN( h, qApp->desktop()->height()*3/4 ); //stretch
+
+// kexidbg << "KexiTableView::sizeHint()= " <<w <<", " <<h << endl;
+
+ return QSize(w, h);
+ /*QSize(
+ QMAX( ts.width() + leftMargin() + 2*2, (m_navPanel ? m_navPanel->width() : 0) ),
+ //+ QMIN(m_verticalHeader->width(),d->rowHeight) + margin()*2,
+ QMAX( ts.height()+topMargin()+horizontalScrollBar()->sizeHint().height(),
+ minimumSizeHint().height() )
+ );*/
+// QMAX(ts.height() + topMargin(), minimumSizeHint().height()) );
+}
+
+QSize KexiTableView::minimumSizeHint() const
+{
+ return QSize(
+ leftMargin() + ((columns()>0)?columnWidth(0):KEXI_DEFAULT_DATA_COLUMN_WIDTH) + 2*2,
+ d->rowHeight*5/2 + topMargin() + (m_navPanel && m_navPanel->isVisible() ? m_navPanel->height() : 0)
+ );
+}
+
+void KexiTableView::createBuffer(int width, int height)
+{
+ if(!d->pBufferPm)
+ d->pBufferPm = new QPixmap(width, height);
+ else
+ if(d->pBufferPm->width() < width || d->pBufferPm->height() < height)
+ d->pBufferPm->resize(width, height);
+// d->pBufferPm->fill();
+}
+
+//internal
+inline void KexiTableView::paintRow(KexiTableItem *item,
+ QPainter *pb, int r, int rowp, int cx, int cy,
+ int colfirst, int collast, int maxwc)
+{
+ if (!item)
+ return;
+ // Go through the columns in the row r
+ // if we know from where to where, go through [colfirst, collast],
+ // else go through all of them
+ if (colfirst==-1)
+ colfirst=0;
+ if (collast==-1)
+ collast=columns()-1;
+
+ int transly = rowp-cy;
+
+ if (d->appearance.rowHighlightingEnabled && r == m_curRow && !d->appearance.fullRowSelection) {
+ pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.rowHighlightingColor);
+ }
+ else if (d->appearance.rowMouseOverHighlightingEnabled && r == d->highlightedRow) {
+ if(d->appearance.backgroundAltering && (r%2 != 0))
+ pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.rowMouseOverAlternateHighlightingColor);
+ else
+ pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.rowMouseOverHighlightingColor);
+ }
+ else {
+ if(d->appearance.backgroundAltering && (r%2 != 0))
+ pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.alternateBackgroundColor);
+ else
+ pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.baseColor);
+ }
+
+ for(int c = colfirst; c <= collast; c++)
+ {
+ // get position and width of column c
+ int colp = columnPos(c);
+ if (colp==-1)
+ continue; //invisible column?
+ int colw = columnWidth(c);
+ int translx = colp-cx;
+
+ // Translate painter and draw the cell
+ pb->saveWorldMatrix();
+ pb->translate(translx, transly);
+ paintCell( pb, item, c, r, QRect(colp, rowp, colw, d->rowHeight));
+ pb->restoreWorldMatrix();
+ }
+
+ if (m_dragIndicatorLine>=0) {
+ int y_line = -1;
+ if (r==(rows()-1) && m_dragIndicatorLine==rows()) {
+ y_line = transly+d->rowHeight-3; //draw at last line
+ }
+ if (m_dragIndicatorLine==r) {
+ y_line = transly+1;
+ }
+ if (y_line>=0) {
+ RasterOp op = pb->rasterOp();
+ pb->setRasterOp(XorROP);
+ pb->setPen( QPen(Qt::white, 3) );
+ pb->drawLine(0, y_line, maxwc, y_line);
+ pb->setRasterOp(op);
+ }
+ }
+}
+
+void KexiTableView::drawContents( QPainter *p, int cx, int cy, int cw, int ch)
+{
+ if (d->disableDrawContents)
+ return;
+ int colfirst = columnAt(cx);
+ int rowfirst = rowAt(cy);
+ int collast = columnAt(cx + cw-1);
+ int rowlast = rowAt(cy + ch-1);
+ bool inserting = isInsertingEnabled();
+ bool plus1row = false; //true if we should show 'inserting' row at the end
+ bool paintOnlyInsertRow = false;
+
+/* kdDebug(44021) << QString(" KexiTableView::drawContents(cx:%1 cy:%2 cw:%3 ch:%4)")
+ .arg(cx).arg(cy).arg(cw).arg(ch) << endl;*/
+
+ if (rowlast == -1) {
+ rowlast = rows() - 1;
+ plus1row = inserting;
+ if (rowfirst == -1) {
+ if (rowAt(cy - d->rowHeight) != -1) {
+ paintOnlyInsertRow = true;
+// kdDebug(44021) << "-- paintOnlyInsertRow --" << endl;
+ }
+ }
+ }
+// kdDebug(44021) << "rowfirst="<<rowfirst<<" rowlast="<<rowlast<<" rows()="<<rows()<<endl;
+// kdDebug(44021)<<" plus1row=" << plus1row<<endl;
+
+ if ( collast == -1 )
+ collast = columns() - 1;
+
+ if (colfirst>collast) {
+ int tmp = colfirst;
+ colfirst = collast;
+ collast = tmp;
+ }
+ if (rowfirst>rowlast) {
+ int tmp = rowfirst;
+ rowfirst = rowlast;
+ rowlast = tmp;
+ }
+
+// qDebug("cx:%3d cy:%3d w:%3d h:%3d col:%2d..%2d row:%2d..%2d tsize:%4d,%4d",
+// cx, cy, cw, ch, colfirst, collast, rowfirst, rowlast, tableSize().width(), tableSize().height());
+// triggerUpdate();
+
+ if (rowfirst == -1 || colfirst == -1) {
+ if (!paintOnlyInsertRow && !plus1row) {
+ paintEmptyArea(p, cx, cy, cw, ch);
+ return;
+ }
+ }
+
+ createBuffer(cw, ch);
+ if(d->pBufferPm->isNull())
+ return;
+ QPainter *pb = new QPainter(d->pBufferPm, this);
+// pb->fillRect(0, 0, cw, ch, colorGroup().base());
+
+// int maxwc = QMIN(cw, (columnPos(d->numCols - 1) + columnWidth(d->numCols - 1)));
+ int maxwc = columnPos(columns() - 1) + columnWidth(columns() - 1);
+// kdDebug(44021) << "KexiTableView::drawContents(): maxwc: " << maxwc << endl;
+
+ pb->fillRect(cx, cy, cw, ch, d->appearance.baseColor);
+
+ int rowp;
+ int r;
+ if (paintOnlyInsertRow) {
+ r = rows();
+ rowp = rowPos(r); // 'insert' row's position
+ }
+ else {
+ QPtrListIterator<KexiTableItem> it = m_data->iterator();
+ it += rowfirst;//move to 1st row
+ rowp = rowPos(rowfirst); // row position
+ for (r = rowfirst;r <= rowlast; r++, ++it, rowp+=d->rowHeight) {
+ paintRow(it.current(), pb, r, rowp, cx, cy, colfirst, collast, maxwc);
+ }
+ }
+
+ if (plus1row) { //additional - 'insert' row
+ paintRow(m_insertItem, pb, r, rowp, cx, cy, colfirst, collast, maxwc);
+ }
+
+ delete pb;
+
+ p->drawPixmap(cx,cy,*d->pBufferPm, 0,0,cw,ch);
+
+ //(js)
+ paintEmptyArea(p, cx, cy, cw, ch);
+}
+
+bool KexiTableView::isDefaultValueDisplayed(KexiTableItem *item, int col, QVariant* value)
+{
+ const bool cursorAtInsertRowOrEditingNewRow = (item == m_insertItem || (m_newRowEditing && m_currentItem == item));
+ KexiTableViewColumn *tvcol;
+ if (cursorAtInsertRowOrEditingNewRow
+ && (tvcol = m_data->column(col))
+ && hasDefaultValueAt(*tvcol)
+ && !tvcol->field()->isAutoIncrement())
+ {
+ if (value)
+ *value = tvcol->field()->defaultValue();
+ return true;
+ }
+ return false;
+}
+
+void KexiTableView::paintCell(QPainter* p, KexiTableItem *item, int col, int row, const QRect &cr, bool print)
+{
+ p->save();
+ Q_UNUSED(print);
+ int w = cr.width();
+ int h = cr.height();
+ int x2 = w - 1;
+ int y2 = h - 1;
+
+/* if (0==qstrcmp("KexiComboBoxPopup",parentWidget()->className())) {
+ kexidbg << parentWidget()->className() << " >>>>>> KexiTableView::paintCell(col=" << col <<"row="<<row<<") w="<<w<<endl;
+ }*/
+
+ // Draw our lines
+ QPen pen(p->pen());
+
+ if (d->appearance.gridEnabled) {
+ p->setPen(d->appearance.borderColor);
+ p->drawLine( x2, 0, x2, y2 ); // right
+ p->drawLine( 0, y2, x2, y2 ); // bottom
+ }
+ p->setPen(pen);
+
+ if (m_editor && row == m_curRow && col == m_curCol //don't paint contents of edited cell
+ && m_editor->hasFocusableWidget() //..if it's visible
+ ) {
+ p->restore();
+ return;
+ }
+
+ KexiTableEdit *edit = tableEditorWidget( col, /*ignoreMissingEditor=*/true );
+// if (!edit)
+// return;
+
+ int x = edit ? edit->leftMargin() : 0;
+ int y_offset=0;
+
+ int align = Qt::SingleLine | Qt::AlignVCenter;
+ QString txt; //text to draw
+
+ KexiTableViewColumn *tvcol = m_data->column(col);
+
+ QVariant cellValue;
+ if (col < (int)item->count()) {
+ if (m_currentItem == item) {
+ if (m_editor && row == m_curRow && col == m_curCol
+ && !m_editor->hasFocusableWidget())
+ {
+ //we're over editing cell and the editor has no widget
+ // - we're displaying internal values, not buffered
+// bool ok;
+ cellValue = m_editor->value();
+ }
+ else {
+ //we're displaying values from edit buffer, if available
+ // this assignment will also get default value if there's no actual value set
+ cellValue = *bufferedValueAt(col);
+ }
+ }
+ else {
+ cellValue = item->at(col);
+ }
+ }
+
+ bool defaultValueDisplayed = isDefaultValueDisplayed(item, col);
+
+ if ((item == m_insertItem /*|| m_newRowEditing*/) && cellValue.isNull()) {
+ if (!tvcol->field()->isAutoIncrement() && !tvcol->field()->defaultValue().isNull()) {
+ //display default value in the "insert row", if available
+ //(but not if there is autoincrement flag set)
+ cellValue = tvcol->field()->defaultValue();
+ defaultValueDisplayed = true;
+ }
+ }
+
+ const bool columnReadOnly = tvcol->isReadOnly();
+ const bool dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
+ = d->appearance.rowHighlightingEnabled && !d->appearance.persistentSelections
+ && m_curRow /*d->highlightedRow*/ >= 0 && row != m_curRow; //d->highlightedRow;
+
+ // setup default pen
+ QPen defaultPen;
+ const bool usesSelectedTextColor = edit && edit->usesSelectedTextColor();
+ if (defaultValueDisplayed) {
+ if (col == m_curCol && row == m_curRow && usesSelectedTextColor)
+ defaultPen = d->defaultValueDisplayParameters.selectedTextColor;
+ else
+ defaultPen = d->defaultValueDisplayParameters.textColor;
+ }
+ else if (d->appearance.fullRowSelection
+ && (row == d->highlightedRow || (row == m_curRow && d->highlightedRow==-1))
+ && usesSelectedTextColor )
+ {
+ defaultPen = d->appearance.rowHighlightingTextColor; //special case: highlighted row
+ }
+ else if (d->appearance.fullRowSelection && row == m_curRow && usesSelectedTextColor)
+ {
+ defaultPen = d->appearance.textColor; //special case for full row selection
+ }
+ else if (m_currentItem == item && col == m_curCol && !columnReadOnly
+ && !dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
+ && usesSelectedTextColor)
+ {
+ defaultPen = colorGroup().highlightedText(); //selected text
+ }
+ else if (d->appearance.rowHighlightingEnabled && row == m_curRow
+ && !dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
+ && usesSelectedTextColor)
+ {
+ defaultPen = d->appearance.rowHighlightingTextColor;
+ }
+ else if (d->appearance.rowMouseOverHighlightingEnabled && row == d->highlightedRow
+ && !dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
+ && usesSelectedTextColor)
+ {
+ defaultPen = d->appearance.rowMouseOverHighlightingTextColor;
+ }
+ else
+ defaultPen = d->appearance.textColor;
+
+ if (edit) {
+ if (defaultValueDisplayed)
+ p->setFont( d->defaultValueDisplayParameters.font );
+ p->setPen( defaultPen );
+
+ //get visible lookup value if available
+ getVisibleLookupValue(cellValue, edit, item, tvcol);
+
+ edit->setupContents( p, m_currentItem == item && col == m_curCol,
+ cellValue, txt, align, x, y_offset, w, h );
+ }
+ if (!d->appearance.gridEnabled)
+ y_offset++; //correction because we're not drawing cell borders
+
+ if (d->appearance.fullRowSelection && d->appearance.fullRowSelection) {
+// p->fillRect(x, y_offset, x+w-1, y_offset+h-1, red);
+ }
+ if (m_currentItem == item && (col == m_curCol || d->appearance.fullRowSelection)) {
+ if (edit && ((d->appearance.rowHighlightingEnabled && !d->appearance.fullRowSelection) || (row == m_curRow && d->highlightedRow==-1 && d->appearance.fullRowSelection))) //!dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted)
+ edit->paintSelectionBackground( p, isEnabled(), txt, align, x, y_offset, w, h,
+ isEnabled() ? colorGroup().highlight() : QColor(200,200,200),//d->grayColor,
+ p->fontMetrics(), columnReadOnly, d->appearance.fullRowSelection );
+ }
+
+ if (!edit) {
+ p->fillRect(0, 0, x2, y2, d->diagonalGrayPattern);
+ }
+
+// If we are in the focus cell, draw indication
+ if(m_currentItem == item && col == m_curCol //js: && !d->recordIndicator)
+ && !d->appearance.fullRowSelection)
+ {
+// kexidbg << ">>> CURRENT CELL ("<<m_curCol<<"," << m_curRow<<") focus="<<has_focus<<endl;
+// if (has_focus) {
+ if (isEnabled()) {
+ p->setPen(d->appearance.textColor);
+ }
+ else {
+ QPen gray_pen(p->pen());
+ gray_pen.setColor(d->appearance.borderColor);
+ p->setPen(gray_pen);
+ }
+ if (edit)
+ edit->paintFocusBorders( p, cellValue, 0, 0, x2, y2 );
+ else
+ p->drawRect(0, 0, x2, y2);
+ }
+
+/// bool autonumber = false;
+ if ((!m_newRowEditing && item == m_insertItem)
+ || (m_newRowEditing && item == m_currentItem && cellValue.isNull())) {
+ //we're in "insert row"
+ if (tvcol->field()->isAutoIncrement()) {
+ //"autonumber" column
+// txt = i18n("(autonumber)");
+// autonumber = true;
+// if (autonumber) {
+ KexiDisplayUtils::paintAutonumberSign(d->autonumberSignDisplayParameters, p,
+ x, y_offset, w - x - x - ((align & Qt::AlignLeft)?2:0), h, align);
+// }
+ }
+ }
+
+ // draw text
+ if (!txt.isEmpty()) {
+ if (defaultValueDisplayed)
+ p->setFont( d->defaultValueDisplayParameters.font );
+ p->setPen( defaultPen );
+ p->drawText(x, y_offset, w - (x + x)- ((align & Qt::AlignLeft)?2:0)/*right space*/, h,
+ align, txt);
+ }
+ p->restore();
+}
+
+QPoint KexiTableView::contentsToViewport2( const QPoint &p )
+{
+ return QPoint( p.x() - contentsX(), p.y() - contentsY() );
+}
+
+void KexiTableView::contentsToViewport2( int x, int y, int& vx, int& vy )
+{
+ const QPoint v = contentsToViewport2( QPoint( x, y ) );
+ vx = v.x();
+ vy = v.y();
+}
+
+QPoint KexiTableView::viewportToContents2( const QPoint& vp )
+{
+ return QPoint( vp.x() + contentsX(),
+ vp.y() + contentsY() );
+}
+
+void KexiTableView::paintEmptyArea( QPainter *p, int cx, int cy, int cw, int ch )
+{
+// qDebug("%s: paintEmptyArea(x:%d y:%d w:%d h:%d)", (const char*)parentWidget()->caption(),cx,cy,cw,ch);
+
+ // Regions work with shorts, so avoid an overflow and adjust the
+ // table size to the visible size
+ QSize ts( tableSize() );
+// ts.setWidth( QMIN( ts.width(), visibleWidth() ) );
+// ts.setHeight( QMIN( ts.height() - (m_navPanel ? m_navPanel->height() : 0), visibleHeight()) );
+/* kdDebug(44021) << QString(" (cx:%1 cy:%2 cw:%3 ch:%4)")
+ .arg(cx).arg(cy).arg(cw).arg(ch) << endl;
+ kdDebug(44021) << QString(" (w:%3 h:%4)")
+ .arg(ts.width()).arg(ts.height()) << endl;*/
+
+ // Region of the rect we should draw, calculated in viewport
+ // coordinates, as a region can't handle bigger coordinates
+ contentsToViewport2( cx, cy, cx, cy );
+ QRegion reg( QRect( cx, cy, cw, ch ) );
+
+//kexidbg << "---cy-- " << contentsY() << endl;
+
+ // Subtract the table from it
+// reg = reg.subtract( QRect( QPoint( 0, 0 ), ts-QSize(0,m_navPanel->isVisible() ? m_navPanel->height() : 0) ) );
+ reg = reg.subtract( QRect( QPoint( 0, 0 ), ts
+ -QSize(0,QMAX((m_navPanel ? m_navPanel->height() : 0), horizontalScrollBar()->sizeHint().height())
+ - (horizontalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height()/2 : 0)
+ + (horizontalScrollBar()->isVisible() ? 0 :
+ d->internal_bottomMargin
+// horizontalScrollBar()->sizeHint().height()/2
+ )
+//- /*d->bottomMargin */ horizontalScrollBar()->sizeHint().height()*3/2
+ + contentsY()
+// - (verticalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height()/2 : 0)
+ )
+ ) );
+// reg = reg.subtract( QRect( QPoint( 0, 0 ), ts ) );
+
+ // And draw the rectangles (transformed inc contents coordinates as needed)
+ QMemArray<QRect> r = reg.rects();
+ for ( int i = 0; i < (int)r.count(); i++ ) {
+ QRect rect( viewportToContents2(r[i].topLeft()), r[i].size() );
+/* kdDebug(44021) << QString("- pEA: p->fillRect(x:%1 y:%2 w:%3 h:%4)")
+ .arg(rect.x()).arg(rect.y())
+ .arg(rect.width()).arg(rect.height()) << endl;*/
+// p->fillRect( QRect(viewportToContents2(r[i].topLeft()),r[i].size()), d->emptyAreaColor );
+ p->fillRect( rect, d->appearance.emptyAreaColor );
+// p->fillRect( QRect(viewportToContents2(r[i].topLeft()),r[i].size()), viewport()->backgroundBrush() );
+ }
+}
+
+void KexiTableView::contentsMouseDoubleClickEvent(QMouseEvent *e)
+{
+// kdDebug(44021) << "KexiTableView::contentsMouseDoubleClickEvent()" << endl;
+ m_contentsMousePressEvent_dblClick = true;
+ contentsMousePressEvent(e);
+ m_contentsMousePressEvent_dblClick = false;
+
+ if(m_currentItem)
+ {
+ if(d->editOnDoubleClick && columnEditable(m_curCol) && columnType(m_curCol) != KexiDB::Field::Boolean) {
+ KexiTableEdit *edit = tableEditorWidget( m_curCol, /*ignoreMissingEditor=*/true );
+ if (edit && edit->handleDoubleClick()) {
+ //nothing to do: editors like BLOB editor has custom handling of double clicking
+ }
+ else {
+ startEditCurrentCell();
+ // createEditor(m_curRow, m_curCol, QString::null);
+ }
+ }
+
+ emit itemDblClicked(m_currentItem, m_curRow, m_curCol);
+ }
+}
+
+void KexiTableView::contentsMousePressEvent( QMouseEvent* e )
+{
+// kdDebug(44021) << "KexiTableView::contentsMousePressEvent() ??" << endl;
+ setFocus();
+ if(m_data->count()==0 && !isInsertingEnabled()) {
+ QScrollView::contentsMousePressEvent( e );
+ return;
+ }
+
+ if (columnAt(e->pos().x())==-1) { //outside a colums
+ QScrollView::contentsMousePressEvent( e );
+ return;
+ }
+// d->contentsMousePressEvent_ev = *e;
+// d->contentsMousePressEvent_enabled = true;
+// QTimer::singleShot(2000, this, SLOT( contentsMousePressEvent_Internal() ));
+// d->contentsMousePressEvent_timer.start(100,true);
+
+// if (!d->contentsMousePressEvent_enabled)
+// return;
+// d->contentsMousePressEvent_enabled=false;
+
+ if (!d->moveCursorOnMouseRelease) {
+ if (!handleContentsMousePressOrRelease(e, false))
+ return;
+ }
+
+// kdDebug(44021)<<"void KexiTableView::contentsMousePressEvent( QMouseEvent* e ) by now the current items should be set, if not -> error + crash"<<endl;
+ if(e->button() == Qt::RightButton)
+ {
+ showContextMenu(e->globalPos());
+ }
+ else if(e->button() == Qt::LeftButton)
+ {
+ if(columnType(m_curCol) == KexiDB::Field::Boolean && columnEditable(m_curCol))
+ {
+ //only accept clicking on the [x] rect (copied from KexiBoolTableEdit::setupContents())
+ int s = QMAX(d->rowHeight - 5, 12);
+ s = QMIN( d->rowHeight-3, s );
+ s = QMIN( columnWidth(m_curCol)-3, s ); //avoid too large box
+ const QRect r( columnPos(m_curCol) + QMAX( columnWidth(m_curCol)/2 - s/2, 0 ), rowPos(m_curRow) +d->rowHeight/2 - s/2 /*- 1*/, s, s);
+ //kexidbg << r << endl;
+ if (r.contains(e->pos())) {
+// kexidbg << "e->x:" << e->x() << " e->y:" << e->y() << " " << rowPos(m_curRow) <<
+// " " << columnPos(m_curCol) << endl;
+ boolToggled();
+ }
+ }
+#if 0 //js: TODO
+ else if(columnType(m_curCol) == QVariant::StringList && columnEditable(m_curCol))
+ {
+ createEditor(m_curRow, m_curCol);
+ }
+#endif
+ }
+//ScrollView::contentsMousePressEvent( e );
+}
+
+void KexiTableView::contentsMouseReleaseEvent( QMouseEvent* e )
+{
+// kdDebug(44021) << "KexiTableView::contentsMousePressEvent() ??" << endl;
+ if(m_data->count()==0 && !isInsertingEnabled())
+ return;
+
+ if (d->moveCursorOnMouseRelease)
+ handleContentsMousePressOrRelease(e, true);
+
+ int col = columnAt(e->pos().x());
+ int row = rowAt(e->pos().y());
+
+ if (!m_currentItem || col==-1 || row==-1 || col!=m_curCol || row!=m_curRow)//outside a current cell
+ return;
+
+ QScrollView::contentsMouseReleaseEvent( e );
+
+ emit itemMouseReleased(m_currentItem, m_curRow, m_curCol);
+}
+
+bool KexiTableView::handleContentsMousePressOrRelease(QMouseEvent* e, bool release)
+{
+ // remember old focus cell
+ int oldRow = m_curRow;
+ int oldCol = m_curCol;
+ kdDebug(44021) << "oldRow=" << oldRow <<" oldCol=" << oldCol <<endl;
+ bool onInsertItem = false;
+
+ int newrow, newcol;
+ //compute clicked row nr
+ if (isInsertingEnabled()) {
+ if (rowAt(e->pos().y())==-1) {
+ newrow = rowAt(e->pos().y() - d->rowHeight);
+ if (newrow==-1 && m_data->count()>0) {
+ if (release)
+ QScrollView::contentsMouseReleaseEvent( e );
+ else
+ QScrollView::contentsMousePressEvent( e );
+ return false;
+ }
+ newrow++;
+ kdDebug(44021) << "Clicked just on 'insert' row." << endl;
+ onInsertItem=true;
+ }
+ else {
+ // get new focus cell
+ newrow = rowAt(e->pos().y());
+ }
+ }
+ else {
+ if (rowAt(e->pos().y())==-1 || columnAt(e->pos().x())==-1) {
+ if (release)
+ QScrollView::contentsMouseReleaseEvent( e );
+ else
+ QScrollView::contentsMousePressEvent( e );
+ return false; //clicked outside a grid
+ }
+ // get new focus cell
+ newrow = rowAt(e->pos().y());
+ }
+ newcol = columnAt(e->pos().x());
+
+ if(e->button() != Qt::NoButton) {
+ setCursorPosition(newrow,newcol);
+ }
+ return true;
+}
+
+void KexiTableView::showContextMenu(const QPoint& _pos)
+{
+ if (!d->contextMenuEnabled || m_popupMenu->count()<1)
+ return;
+ QPoint pos(_pos);
+ if (pos==QPoint(-1,-1)) {
+ pos = viewport()->mapToGlobal( QPoint( columnPos(m_curCol), rowPos(m_curRow) + d->rowHeight ) );
+ }
+ //show own context menu if configured
+// if (updateContextMenu()) {
+ selectRow(m_curRow);
+ m_popupMenu->exec(pos);
+/* }
+ else {
+ //request other context menu
+ emit contextMenuRequested(m_currentItem, m_curCol, pos);
+ }*/
+}
+
+void KexiTableView::contentsMouseMoveEvent( QMouseEvent *e )
+{
+ int row;
+ const int col = columnAt(e->x());
+ if (col < 0) {
+ row = -1;
+ } else {
+ row = rowAt( e->y(), true /*ignoreEnd*/ );
+ if (row > (rows() - 1 + (isInsertingEnabled()?1:0)))
+ row = -1; //no row to paint
+ }
+// kexidbg << " row="<<row<< " col="<<col<<endl;
+ //update row highlight if needed
+ if (d->appearance.rowMouseOverHighlightingEnabled) {
+ if (row != d->highlightedRow) {
+ const int oldRow = d->highlightedRow;
+ d->highlightedRow = row;
+ updateRow(oldRow);
+ updateRow(d->highlightedRow);
+ //currently selected (not necessary highlighted) row needs to be repainted
+ updateRow(m_curRow);
+ m_verticalHeader->setHighlightedRow(d->highlightedRow);
+ }
+ }
+
+#if 0//(js) doesn't work!
+
+ // do the same as in mouse press
+ int x,y;
+ contentsToViewport(e->x(), e->y(), x, y);
+
+ if(y > visibleHeight())
+ {
+ d->needAutoScroll = true;
+ d->scrollTimer->start(70, false);
+ d->scrollDirection = ScrollDown;
+ }
+ else if(y < 0)
+ {
+ d->needAutoScroll = true;
+ d->scrollTimer->start(70, false);
+ d->scrollDirection = ScrollUp;
+ }
+ else if(x > visibleWidth())
+ {
+ d->needAutoScroll = true;
+ d->scrollTimer->start(70, false);
+ d->scrollDirection = ScrollRight;
+ }
+ else if(x < 0)
+ {
+ d->needAutoScroll = true;
+ d->scrollTimer->start(70, false);
+ d->scrollDirection = ScrollLeft;
+ }
+ else
+ {
+ d->needAutoScroll = false;
+ d->scrollTimer->stop();
+ contentsMousePressEvent(e);
+ }
+#endif
+ QScrollView::contentsMouseMoveEvent(e);
+}
+
+#if 0//(js) doesn't work!
+void KexiTableView::contentsMouseReleaseEvent(QMouseEvent *)
+{
+ if(d->needAutoScroll)
+ {
+ d->scrollTimer->stop();
+ }
+}
+#endif
+
+static bool overrideEditorShortcutNeeded(QKeyEvent *e)
+{
+ //perhaps more to come...
+ return e->key() == Qt::Key_Delete && e->state()==Qt::ControlButton;
+}
+
+bool KexiTableView::shortCutPressed( QKeyEvent *e, const QCString &action_name )
+{
+ const int k = e->key();
+ KAction *action = m_sharedActions[action_name];
+ if (action) {
+ if (!action->isEnabled())//this action is disabled - don't process it!
+ return false;
+ if (action->shortcut() == KShortcut( KKey(e) )) {
+ //special cases when we need to override editor's shortcut
+ if (overrideEditorShortcutNeeded(e)) {
+ return true;
+ }
+ return false;//this shortcut is owned by shared action - don't process it!
+ }
+ }
+
+ //check default shortcut (when user app has no action shortcuts defined
+ // but we want these shortcuts to still work)
+ if (action_name=="data_save_row")
+ return (k == Qt::Key_Return || k == Qt::Key_Enter) && e->state()==Qt::ShiftButton;
+ if (action_name=="edit_delete_row")
+ return k == Qt::Key_Delete && e->state()==Qt::ControlButton;
+ if (action_name=="edit_delete")
+ return k == Qt::Key_Delete && e->state()==Qt::NoButton;
+ if (action_name=="edit_edititem")
+ return k == Qt::Key_F2 && e->state()==Qt::NoButton;
+ if (action_name=="edit_insert_empty_row")
+ return k == Qt::Key_Insert && e->state()==(Qt::ShiftButton | Qt::ControlButton);
+
+ return false;
+}
+
+void KexiTableView::keyPressEvent(QKeyEvent* e)
+{
+ if (!hasData())
+ return;
+// kexidbg << "KexiTableView::keyPressEvent: key=" <<e->key() << " txt=" <<e->text()<<endl;
+
+ const int k = e->key();
+ const bool ro = isReadOnly();
+ QWidget *w = focusWidget();
+// if (!w || w!=viewport() && w!=this && (!m_editor || w!=m_editor->view() && w!=m_editor)) {
+// if (!w || w!=viewport() && w!=this && (!m_editor || w!=m_editor->view())) {
+ if (!w || w!=viewport() && w!=this && (!m_editor || !KexiUtils::hasParent(dynamic_cast<QObject*>(m_editor), w))) {
+ //don't process stranger's events
+ e->ignore();
+ return;
+ }
+ if (d->skipKeyPress) {
+ d->skipKeyPress=false;
+ e->ignore();
+ return;
+ }
+
+ if(m_currentItem == 0 && (m_data->count() > 0 || isInsertingEnabled()))
+ {
+ setCursorPosition(0,0);
+ }
+ else if(m_data->count() == 0 && !isInsertingEnabled())
+ {
+ e->accept();
+ return;
+ }
+
+ if(m_editor) {// if a cell is edited, do some special stuff
+ if (k == Qt::Key_Escape) {
+ cancelEditor();
+ e->accept();
+ return;
+ } else if (k == Qt::Key_Return || k == Qt::Key_Enter) {
+ if (columnType(m_curCol) == KexiDB::Field::Boolean) {
+ boolToggled();
+ }
+ else {
+ acceptEditor();
+ }
+ e->accept();
+ return;
+ }
+ }
+ else if (m_rowEditing) {// if a row is in edit mode, do some special stuff
+ if (shortCutPressed( e, "data_save_row")) {
+ kexidbg << "shortCutPressed!!!" <<endl;
+ acceptRowEdit();
+ return;
+ }
+ }
+
+ if(k == Qt::Key_Return || k == Qt::Key_Enter)
+ {
+ emit itemReturnPressed(m_currentItem, m_curRow, m_curCol);
+ }
+
+ int curRow = m_curRow;
+ int curCol = m_curCol;
+
+ const bool nobtn = e->state()==NoButton;
+ bool printable = false;
+
+ //check shared shortcuts
+ if (!ro) {
+ if (shortCutPressed(e, "edit_delete_row")) {
+ deleteCurrentRow();
+ e->accept();
+ return;
+ } else if (shortCutPressed(e, "edit_delete")) {
+ deleteAndStartEditCurrentCell();
+ e->accept();
+ return;
+ }
+ else if (shortCutPressed(e, "edit_insert_empty_row")) {
+ insertEmptyRow();
+ e->accept();
+ return;
+ }
+ }
+
+/* case Qt::Key_Delete:
+ if (e->state()==Qt::ControlButton) {//remove current row
+ deleteCurrentRow();
+ }
+ else if (nobtn) {//remove contents of the current cell
+ deleteAndStartEditCurrentCell();
+ }
+ break;*/
+
+// bool _return;
+ if (k == Qt::Key_Shift || k == Qt::Key_Alt || k == Qt::Key_Control || k == Qt::Key_Meta) {
+ e->ignore();
+ }
+ else if (KexiDataAwareObjectInterface::handleKeyPress(e, curRow, curCol, d->appearance.fullRowSelection)) {
+ if (e->isAccepted())
+ return;
+ }
+ else if (k == Qt::Key_Backspace && nobtn) {
+ if (!ro && columnType(curCol) != KexiDB::Field::Boolean && columnEditable(curCol))
+ createEditor(curRow, curCol, QString::null, true);
+ }
+ else if (k == Qt::Key_Space) {
+ if (nobtn && !ro && columnEditable(curCol)) {
+ if (columnType(curCol) == KexiDB::Field::Boolean) {
+ boolToggled();
+ }
+ else
+ printable = true; //just space key
+ }
+ }
+ else if (k == Qt::Key_Escape) {
+ if (nobtn && m_rowEditing) {
+ cancelRowEdit();
+ return;
+ }
+ }
+ else {
+ //others:
+ if (nobtn && (k==Qt::Key_Tab || k==Qt::Key_Right)) {
+//! \todo add option for stopping at 1st column for Qt::Key_left
+ //tab
+ if (acceptEditor()) {
+ if (curCol == (columns() - 1)) {
+ if (curRow < (rows()-1+(isInsertingEnabled()?1:0))) {//skip to next row
+ curRow++;
+ curCol = 0;
+ }
+ }
+ else
+ curCol++;
+ }
+ }
+ else if ((e->state()==Qt::ShiftButton && k==Qt::Key_Tab)
+ || (nobtn && k==Qt::Key_Backtab)
+ || (e->state()==Qt::ShiftButton && k==Qt::Key_Backtab)
+ || (nobtn && k==Qt::Key_Left)
+ ) {
+//! \todo add option for stopping at last column
+ //backward tab
+ if (acceptEditor()) {
+ if (curCol == 0) {
+ if (curRow>0) {//skip to previous row
+ curRow--;
+ curCol = columns() - 1;
+ }
+ }
+ else
+ curCol--;
+ }
+ }
+ else if (nobtn && k==d->contextMenuKey) { //Qt::Key_Menu:
+ showContextMenu();
+ }
+ else {
+ KexiTableEdit *edit = tableEditorWidget( m_curCol );
+ if (edit && edit->handleKeyPress(e, m_editor==edit)) {
+ //try to handle the event @ editor's level
+ e->accept();
+ return;
+ }
+ else if ( nobtn && (k==Qt::Key_Enter || k==Qt::Key_Return || shortCutPressed(e, "edit_edititem")) ) {
+ //this condition is moved after handleKeyPress() to allow to everride enter key as well
+ startEditOrToggleValue();
+ }
+ else {
+ kexidbg << "KexiTableView::KeyPressEvent(): default" << endl;
+ if (e->text().isEmpty() || !e->text().isEmpty() && !e->text()[0].isPrint() ) {
+ kdDebug(44021) << "NOT PRINTABLE: 0x0" << QString("%1").arg(k,0,16) <<endl;
+ // e->ignore();
+ QScrollView::keyPressEvent(e);
+ return;
+ }
+ printable = true;
+ }
+ }
+ }
+ //finally: we've printable char:
+ if (printable && !ro) {
+ KexiTableViewColumn *tvcol = m_data->column(curCol);
+ if (tvcol->acceptsFirstChar(e->text()[0])) {
+ kdDebug(44021) << "KexiTableView::KeyPressEvent(): ev pressed: acceptsFirstChar()==true" << endl;
+ // if (e->text()[0].isPrint())
+ createEditor(curRow, curCol, e->text(), true);
+ }
+ else {
+//TODO show message "key not allowed eg. on a statusbar"
+ kdDebug(44021) << "KexiTableView::KeyPressEvent(): ev pressed: acceptsFirstChar()==false" << endl;
+ }
+ }
+
+ m_vScrollBarValueChanged_enabled=false;
+
+ // if focus cell changes, repaint
+ setCursorPosition(curRow, curCol);
+
+ m_vScrollBarValueChanged_enabled=true;
+
+ e->accept();
+}
+
+void KexiTableView::emitSelected()
+{
+ if(m_currentItem)
+ emit itemSelected(m_currentItem);
+}
+
+int KexiTableView::rowsPerPage() const
+{
+ return visibleHeight() / d->rowHeight;
+}
+
+KexiDataItemInterface *KexiTableView::editor( int col, bool ignoreMissingEditor )
+{
+ if (!m_data || col<0 || col>=columns())
+ return 0;
+ KexiTableViewColumn *tvcol = m_data->column(col);
+// int t = tvcol->field->type();
+
+ //find the editor for this column
+ KexiTableEdit *editor = d->editors[ tvcol ];
+ if (editor)
+ return editor;
+
+ //not found: create
+// editor = KexiCellEditorFactory::createEditor(*m_data->column(col)->field, this);
+ editor = KexiCellEditorFactory::createEditor(*tvcol, this);
+ if (!editor) {//create error!
+ if (!ignoreMissingEditor) {
+ //js TODO: show error???
+ cancelRowEdit();
+ }
+ return 0;
+ }
+ editor->hide();
+ if (m_data->cursor() && m_data->cursor()->query())
+ editor->createInternalEditor(*m_data->cursor()->query());
+
+ connect(editor,SIGNAL(editRequested()),this,SLOT(slotEditRequested()));
+ connect(editor,SIGNAL(cancelRequested()),this,SLOT(cancelEditor()));
+ connect(editor,SIGNAL(acceptRequested()),this,SLOT(acceptEditor()));
+
+ editor->resize(columnWidth(col)-1, rowHeight()-1);
+ editor->installEventFilter(this);
+ if (editor->widget())
+ editor->widget()->installEventFilter(this);
+ //store
+ d->editors.insert( tvcol, editor );
+ return editor;
+}
+
+void KexiTableView::editorShowFocus( int /*row*/, int col )
+{
+ KexiDataItemInterface *edit = editor( col );
+ /*nt p = rowPos(row);
+ (!edit || (p < contentsY()) || (p > (contentsY()+clipper()->height()))) {
+ kexidbg<< "KexiTableView::editorShowFocus() : OUT" << endl;
+ return;
+ }*/
+ if (edit) {
+ kexidbg<< "KexiTableView::editorShowFocus() : IN" << endl;
+ QRect rect = cellGeometry( m_curRow, m_curCol );
+// rect.moveBy( -contentsX(), -contentsY() );
+ edit->showFocus( rect, isReadOnly() || m_data->column(col)->isReadOnly() );
+ }
+}
+
+void KexiTableView::slotEditRequested()
+{
+ createEditor(m_curRow, m_curCol);
+}
+
+void KexiTableView::reloadData() {
+ KexiDataAwareObjectInterface::reloadData();
+ updateContents();
+}
+
+void KexiTableView::createEditor(int row, int col, const QString& addText, bool removeOld)
+{
+ kdDebug(44021) << "KexiTableView::createEditor('"<<addText<<"',"<<removeOld<<")"<<endl;
+ if (isReadOnly()) {
+ kdDebug(44021) << "KexiTableView::createEditor(): DATA IS READ ONLY!"<<endl;
+ return;
+ }
+
+ if (m_data->column(col)->isReadOnly()) {//d->pColumnModes.at(d->numCols-1) & ColumnReadOnly)
+ kdDebug(44021) << "KexiTableView::createEditor(): COL IS READ ONLY!"<<endl;
+ return;
+ }
+
+ const bool startRowEdit = !m_rowEditing; //remember if we're starting row edit
+
+ if (!m_rowEditing) {
+ //we're starting row editing session
+ m_data->clearRowEditBuffer();
+
+ m_rowEditing = true;
+ //indicate on the vheader that we are editing:
+ m_verticalHeader->setEditRow(m_curRow);
+ if (isInsertingEnabled() && m_currentItem==m_insertItem) {
+ //we should know that we are in state "new row editing"
+ m_newRowEditing = true;
+ //'insert' row editing: show another row after that:
+ m_data->append( m_insertItem );
+ //new empty 'inserting' item
+ m_insertItem = m_data->createItem();
+ m_verticalHeader->addLabel();
+ m_verticalHeaderAlreadyAdded = true;
+ updateWidgetContentsSize();
+ //refr. current and next row
+ updateContents(columnPos(0), rowPos(row), viewport()->width(), d->rowHeight*2);
+// updateContents(columnPos(0), rowPos(row+1), viewport()->width(), d->rowHeight);
+//js: warning this breaks behaviour (cursor is skipping, etc.): qApp->processEvents(500);
+ ensureVisible(columnPos(m_curCol), rowPos(row+1)+d->rowHeight-1, columnWidth(m_curCol), d->rowHeight);
+
+ m_verticalHeader->setOffset(contentsY());
+ }
+ }
+
+ KexiTableEdit *editorWidget = tableEditorWidget( col );
+ m_editor = editorWidget;
+ if (!editorWidget)
+ return;
+
+ m_editor->setValue(*bufferedValueAt(col, !removeOld/*useDefaultValueIfPossible*/), addText, removeOld);
+ if (m_editor->hasFocusableWidget()) {
+ moveChild(editorWidget, columnPos(m_curCol), rowPos(m_curRow));
+
+ editorWidget->resize(columnWidth(m_curCol)-1, rowHeight()-1);
+ editorWidget->show();
+
+ m_editor->setFocus();
+ }
+
+ if (startRowEdit) {
+ m_navPanel->showEditingIndicator(true); //this will allow to enable 'next' btn
+// m_navPanel->updateButtons(rows()); //refresh 'next' btn
+ emit rowEditStarted(m_curRow);
+ }
+}
+
+void KexiTableView::focusInEvent(QFocusEvent* e)
+{
+ Q_UNUSED(e);
+ updateCell(m_curRow, m_curCol);
+}
+
+void KexiTableView::focusOutEvent(QFocusEvent* e)
+{
+ KexiDataAwareObjectInterface::focusOutEvent(e);
+}
+
+bool KexiTableView::focusNextPrevChild(bool /*next*/)
+{
+ return false; //special Tab/BackTab meaning
+/* if (m_editor)
+ return true;
+ return QScrollView::focusNextPrevChild(next);*/
+}
+
+void KexiTableView::resizeEvent(QResizeEvent *e)
+{
+ QScrollView::resizeEvent(e);
+ //updateGeometries();
+
+ if (m_navPanel)
+ m_navPanel->updateGeometry(leftMargin());
+// updateNavPanelGeometry();
+
+ if ((contentsHeight() - e->size().height()) <= d->rowHeight) {
+ slotUpdate();
+ triggerUpdate();
+ }
+// d->pTopHeader->repaint();
+
+
+/* m_navPanel->setGeometry(
+ frameWidth(),
+ viewport()->height() +d->pTopHeader->height()
+ -(horizontalScrollBar()->isVisible() ? 0 : horizontalScrollBar()->sizeHint().height())
+ +frameWidth(),
+ m_navPanel->sizeHint().width(), // - verticalScrollBar()->sizeHint().width() - horizontalScrollBar()->sizeHint().width(),
+ horizontalScrollBar()->sizeHint().height()
+ );*/
+// updateContents();
+// m_navPanel->setGeometry(1,horizontalScrollBar()->pos().y(),
+ // m_navPanel->width(), horizontalScrollBar()->height());
+// updateContents(0,0,2000,2000);//js
+// erase(); repaint();
+}
+
+void KexiTableView::viewportResizeEvent( QResizeEvent *e )
+{
+ QScrollView::viewportResizeEvent( e );
+ updateGeometries();
+// erase(); repaint();
+}
+
+void KexiTableView::showEvent(QShowEvent *e)
+{
+ QScrollView::showEvent(e);
+ if (!d->maximizeColumnsWidthOnShow.isEmpty()) {
+ maximizeColumnsWidth(d->maximizeColumnsWidthOnShow);
+ d->maximizeColumnsWidthOnShow.clear();
+ }
+
+ if (m_initDataContentsOnShow) {
+ //full init
+ m_initDataContentsOnShow = false;
+ initDataContents();
+ }
+ else {
+ //just update size
+ QSize s(tableSize());
+// QRect r(cellGeometry(rows() - 1 + (isInsertingEnabled()?1:0), columns() - 1 ));
+// resizeContents(r.right() + 1, r.bottom() + 1);
+ resizeContents(s.width(),s.height());
+ }
+ updateGeometries();
+
+ //now we can ensure cell's visibility ( if there was such a call before show() )
+ if (d->ensureCellVisibleOnShow!=QPoint(-1,-1)) {
+ ensureCellVisible( d->ensureCellVisibleOnShow.x(), d->ensureCellVisibleOnShow.y() );
+ d->ensureCellVisibleOnShow = QPoint(-1,-1); //reset the flag
+ }
+ if (m_navPanel)
+ m_navPanel->updateGeometry(leftMargin());
+// updateNavPanelGeometry();
+}
+
+void KexiTableView::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ if (!hasData())
+ return;
+ if (m_dropsAtRowEnabled) {
+ QPoint p = e->pos();
+ int row = rowAt(p.y());
+ KexiTableItem *item = 0;
+// if (row==(rows()-1) && (p.y() % d->rowHeight) > (d->rowHeight*2/3) ) {
+ if ((p.y() % d->rowHeight) > (d->rowHeight*2/3) ) {
+ row++;
+ }
+ item = m_data->at(row);
+ emit dragOverRow(item, row, e);
+ if (e->isAccepted()) {
+ if (m_dragIndicatorLine>=0 && m_dragIndicatorLine != row) {
+ //erase old indicator
+ updateRow(m_dragIndicatorLine);
+ }
+ if (m_dragIndicatorLine != row) {
+ m_dragIndicatorLine = row;
+ updateRow(m_dragIndicatorLine);
+ }
+ }
+ else {
+ if (m_dragIndicatorLine>=0) {
+ //erase old indicator
+ updateRow(m_dragIndicatorLine);
+ }
+ m_dragIndicatorLine = -1;
+ }
+ }
+ else
+ e->acceptAction(false);
+/* QStringList::ConstIterator it, end( d->dropFilters.constEnd() );
+ for( it = d->dropFilters.constBegin(); it != end; it++)
+ {
+ if(e->provides((*it).latin1()))
+ {
+ e->acceptAction(true);
+ return;
+ }
+ }*/
+// e->acceptAction(false);
+}
+
+void KexiTableView::contentsDropEvent(QDropEvent *e)
+{
+ if (!hasData())
+ return;
+ if (m_dropsAtRowEnabled) {
+ //we're no longer dragging over the table
+ if (m_dragIndicatorLine>=0) {
+ int row2update = m_dragIndicatorLine;
+ m_dragIndicatorLine = -1;
+ updateRow(row2update);
+ }
+ QPoint p = e->pos();
+ int row = rowAt(p.y());
+ if ((p.y() % d->rowHeight) > (d->rowHeight*2/3) ) {
+ row++;
+ }
+ KexiTableItem *item = m_data->at(row);
+ KexiTableItem *newItem = 0;
+ emit droppedAtRow(item, row, e, newItem);
+ if (newItem) {
+ const int realRow = (row==m_curRow ? -1 : row);
+ insertItem(newItem, realRow);
+ setCursorPosition(row, 0);
+// m_currentItem = newItem;
+ }
+ }
+}
+
+void KexiTableView::viewportDragLeaveEvent( QDragLeaveEvent *e )
+{
+ Q_UNUSED(e);
+ if (!hasData())
+ return;
+ if (m_dropsAtRowEnabled) {
+ //we're no longer dragging over the table
+ if (m_dragIndicatorLine>=0) {
+ int row2update = m_dragIndicatorLine;
+ m_dragIndicatorLine = -1;
+ updateRow(row2update);
+ }
+ }
+}
+
+void KexiTableView::updateCell(int row, int col)
+{
+// kdDebug(44021) << "updateCell("<<row<<", "<<col<<")"<<endl;
+ updateContents(cellGeometry(row, col));
+/* QRect r = cellGeometry(row, col);
+ r.setHeight(r.height()+6);
+ r.setTop(r.top()-3);
+ updateContents();*/
+}
+
+void KexiTableView::updateCurrentCell()
+{
+ updateCell(m_curRow, m_curCol);
+}
+
+void KexiTableView::updateRow(int row)
+{
+// kdDebug(44021) << "updateRow("<<row<<")"<<endl;
+ if (row < 0 || row >= (rows() + 2/* sometimes we want to refresh the row after last*/ ))
+ return;
+ //int leftcol = d->pTopHeader->sectionAt( d->pTopHeader->offset() );
+
+ //kexidbg << contentsX() << " " << contentsY() << endl;
+ //kexidbg << QRect( columnPos( leftcol ), rowPos(row), clipper()->width(), rowHeight() ) << endl;
+ // updateContents( QRect( columnPos( leftcol ), rowPos(row), clipper()->width(), rowHeight() ) ); //columnPos(rightcol)+columnWidth(rightcol), rowHeight() ) );
+ updateContents( QRect( contentsX(), rowPos(row), clipper()->width(), rowHeight() ) ); //columnPos(rightcol)+columnWidth(rightcol), rowHeight() ) );
+}
+
+void KexiTableView::slotColumnWidthChanged( int, int, int )
+{
+ QSize s(tableSize());
+ int w = contentsWidth();
+ viewport()->setUpdatesEnabled(false);
+ resizeContents( s.width(), s.height() );
+ viewport()->setUpdatesEnabled(true);
+ if (contentsWidth() < w) {
+ updateContents(contentsX(), 0, viewport()->width(), contentsHeight());
+// repaintContents( s.width(), 0, w - s.width() + 1, contentsHeight(), true );
+ }
+ else {
+ // updateContents( columnPos(col), 0, contentsWidth(), contentsHeight() );
+ updateContents(contentsX(), 0, viewport()->width(), contentsHeight());
+ // viewport()->repaint();
+ }
+
+// updateContents(0, 0, d->pBufferPm->width(), d->pBufferPm->height());
+ QWidget *editorWidget = dynamic_cast<QWidget*>(m_editor);
+ if (editorWidget)
+ {
+ editorWidget->resize(columnWidth(m_curCol)-1, rowHeight()-1);
+ moveChild(editorWidget, columnPos(m_curCol), rowPos(m_curRow));
+ }
+ updateGeometries();
+ updateScrollBars();
+ if (m_navPanel)
+ m_navPanel->updateGeometry(leftMargin());
+// updateNavPanelGeometry();
+}
+
+void KexiTableView::slotSectionHandleDoubleClicked( int section )
+{
+ adjustColumnWidthToContents(section);
+ slotColumnWidthChanged(0,0,0); //to update contents and redraw
+}
+
+
+void KexiTableView::updateGeometries()
+{
+ QSize ts = tableSize();
+ if (m_horizontalHeader->offset() && ts.width() < (m_horizontalHeader->offset() + m_horizontalHeader->width()))
+ horizontalScrollBar()->setValue(ts.width() - m_horizontalHeader->width());
+
+// m_verticalHeader->setGeometry(1, topMargin() + 1, leftMargin(), visibleHeight());
+ m_horizontalHeader->setGeometry(leftMargin() + 1, 1, visibleWidth(), topMargin());
+ m_verticalHeader->setGeometry(1, topMargin() + 1, leftMargin(), visibleHeight());
+}
+
+int KexiTableView::columnWidth(int col) const
+{
+ if (!hasData())
+ return 0;
+ int vcID = m_data->visibleColumnID( col );
+ return (vcID==-1) ? 0 : m_horizontalHeader->sectionSize( vcID );
+}
+
+int KexiTableView::rowHeight() const
+{
+ return d->rowHeight;
+}
+
+int KexiTableView::columnPos(int col) const
+{
+ if (!hasData())
+ return 0;
+ //if this column is hidden, find first column before that is visible
+ int c = QMIN(col, (int)m_data->columnsCount()-1), vcID = 0;
+ while (c>=0 && (vcID=m_data->visibleColumnID( c ))==-1)
+ c--;
+ if (c<0)
+ return 0;
+ if (c==col)
+ return m_horizontalHeader->sectionPos(vcID);
+ return m_horizontalHeader->sectionPos(vcID)+m_horizontalHeader->sectionSize(vcID);
+}
+
+int KexiTableView::rowPos(int row) const
+{
+ return d->rowHeight*row;
+}
+
+int KexiTableView::columnAt(int pos) const
+{
+ if (!hasData())
+ return -1;
+ int r = m_horizontalHeader->sectionAt(pos);
+ if (r<0)
+ return r;
+ return m_data->globalColumnID( r );
+
+// if (r==-1)
+// kexidbg << "columnAt("<<pos<<")==-1 !!!" << endl;
+// return r;
+}
+
+int KexiTableView::rowAt(int pos, bool ignoreEnd) const
+{
+ if (!hasData())
+ return -1;
+ pos /=d->rowHeight;
+ if (pos < 0)
+ return 0;
+ if ((pos >= (int)m_data->count()) && !ignoreEnd)
+ return -1;
+ return pos;
+}
+
+QRect KexiTableView::cellGeometry(int row, int col) const
+{
+ return QRect(columnPos(col), rowPos(row),
+ columnWidth(col), rowHeight());
+}
+
+QSize KexiTableView::tableSize() const
+{
+ if ((rows()+ (isInsertingEnabled()?1:0) ) > 0 && columns() > 0) {
+/* kexidbg << "tableSize()= " << columnPos( columns() - 1 ) + columnWidth( columns() - 1 )
+ << ", " << rowPos( rows()-1+(isInsertingEnabled()?1:0)) + d->rowHeight
+// + QMAX(m_navPanel ? m_navPanel->height() : 0, horizontalScrollBar()->sizeHint().height())
+ + (m_navPanel->isVisible() ? QMAX( m_navPanel->height(), horizontalScrollBar()->sizeHint().height() ) :0 )
+ + margin() << endl;
+*/
+// kexidbg<< m_navPanel->isVisible() <<" "<<m_navPanel->height()<<" "
+// <<horizontalScrollBar()->sizeHint().height()<<" "<<rowPos( rows()-1+(isInsertingEnabled()?1:0))<<endl;
+
+ //int xx = horizontalScrollBar()->sizeHint().height()/2;
+
+ QSize s(
+ columnPos( columns() - 1 ) + columnWidth( columns() - 1 ),
+// + verticalScrollBar()->sizeHint().width(),
+ rowPos( rows()-1+(isInsertingEnabled()?1:0) ) + d->rowHeight
+ + (horizontalScrollBar()->isVisible() ? 0 : horizontalScrollBar()->sizeHint().height())
+ + d->internal_bottomMargin
+// horizontalScrollBar()->sizeHint().height()/2
+// - /*d->bottomMargin */ horizontalScrollBar()->sizeHint().height()*3/2
+
+// + ( (m_navPanel && m_navPanel->isVisible() && verticalScrollBar()->isVisible()
+ // && !horizontalScrollBar()->isVisible())
+ // ? horizontalScrollBar()->sizeHint().height() : 0)
+
+// + QMAX( (m_navPanel && m_navPanel->isVisible()) ? m_navPanel->height() : 0,
+// horizontalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height() : 0)
+
+// + (m_navPanel->isVisible()
+// ? QMAX( m_navPanel->height(), horizontalScrollBar()->sizeHint().height() ) :0 )
+
+// - (horizontalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height() :0 )
+ + margin()
+//-2*d->rowHeight
+ );
+
+// kexidbg << rows()-1 <<" "<< (isInsertingEnabled()?1:0) <<" "<< (m_rowEditing?1:0) << " " << s << endl;
+ return s;
+// +horizontalScrollBar()->sizeHint().height() + margin() );
+ }
+ return QSize(0,0);
+}
+
+void KexiTableView::ensureCellVisible(int row, int col/*=-1*/)
+{
+ if (!isVisible()) {
+ //the table is invisible: we can't ensure visibility now
+ d->ensureCellVisibleOnShow = QPoint(row,col);
+ return;
+ }
+
+ //quite clever: ensure the cell is visible:
+ QRect r( columnPos(col==-1 ? m_curCol : col), rowPos(row) +(d->appearance.fullRowSelection?1:0),
+ columnWidth(col==-1 ? m_curCol : col), rowHeight());
+
+/* if (m_navPanel && horizontalScrollBar()->isHidden() && row == rows()-1) {
+ //when cursor is moved down and navigator covers the cursor's area,
+ //area is scrolled up
+ if ((viewport()->height() - m_navPanel->height()) < r.bottom()) {
+ scrollBy(0,r.bottom() - (viewport()->height() - m_navPanel->height()));
+ }
+ }*/
+
+ if (m_navPanel && m_navPanel->isVisible() && horizontalScrollBar()->isHidden()) {
+ //a hack: for visible navigator: increase height of the visible rect 'r'
+ r.setBottom(r.bottom()+m_navPanel->height());
+ }
+
+ QPoint pcenter = r.center();
+ ensureVisible(pcenter.x(), pcenter.y(), r.width()/2, r.height()/2);
+// updateContents();
+// updateNavPanelGeometry();
+// slotUpdate();
+}
+
+void KexiTableView::updateAfterCancelRowEdit()
+{
+ KexiDataAwareObjectInterface::updateAfterCancelRowEdit();
+ m_navPanel->showEditingIndicator(false);
+}
+
+void KexiTableView::updateAfterAcceptRowEdit()
+{
+ KexiDataAwareObjectInterface::updateAfterAcceptRowEdit();
+ m_navPanel->showEditingIndicator(false);
+}
+
+bool KexiTableView::getVisibleLookupValue(QVariant& cellValue, KexiTableEdit *edit,
+ KexiTableItem *item, KexiTableViewColumn *tvcol) const
+{
+ if (edit->columnInfo() && edit->columnInfo()->indexForVisibleLookupValue()!=-1
+ && edit->columnInfo()->indexForVisibleLookupValue() < (int)item->count())
+ {
+ const QVariant *visibleFieldValue = 0;
+ if (m_currentItem == item && m_data->rowEditBuffer()) {
+ visibleFieldValue = m_data->rowEditBuffer()->at(
+ *tvcol->visibleLookupColumnInfo, false/*!useDefaultValueIfPossible*/ );
+ }
+
+ if (visibleFieldValue)
+ //(use bufferedValueAt() - try to get buffered visible value for lookup field)
+ cellValue = *visibleFieldValue; //txt = visibleFieldValue->toString();
+ else
+ cellValue /*txt*/ = item->at( edit->columnInfo()->indexForVisibleLookupValue() ); //.toString();
+ return true;
+ }
+ return false;
+}
+
+//reimpl.
+void KexiTableView::removeEditor()
+{
+ if (!m_editor)
+ return;
+ KexiDataAwareObjectInterface::removeEditor();
+ viewport()->setFocus();
+}
+
+void KexiTableView::slotRowRepaintRequested(KexiTableItem& item)
+{
+ updateRow( m_data->findRef(&item) );
+}
+
+//(js) unused
+void KexiTableView::slotAutoScroll()
+{
+ kdDebug(44021) << "KexiTableView::slotAutoScroll()" <<endl;
+ if (!d->needAutoScroll)
+ return;
+
+ switch(d->scrollDirection)
+ {
+ case ScrollDown:
+ setCursorPosition(m_curRow + 1, m_curCol);
+ break;
+
+ case ScrollUp:
+ setCursorPosition(m_curRow - 1, m_curCol);
+ break;
+ case ScrollLeft:
+ setCursorPosition(m_curRow, m_curCol - 1);
+ break;
+
+ case ScrollRight:
+ setCursorPosition(m_curRow, m_curCol + 1);
+ break;
+ }
+}
+
+#ifndef KEXI_NO_PRINT
+void
+KexiTableView::print(KPrinter &/*printer*/)
+{
+// printer.setFullPage(true);
+#if 0
+ int leftMargin = printer.margins().width() + 2 + d->rowHeight;
+ int topMargin = printer.margins().height() + 2;
+// int bottomMargin = topMargin + ( printer.realPageSize()->height() * printer.resolution() + 36 ) / 72;
+ int bottomMargin = 0;
+ kdDebug(44021) << "KexiTableView::print: bottom = " << bottomMargin << endl;
+
+ QPainter p(&printer);
+
+ KexiTableItem *i;
+ int width = leftMargin;
+ for(int col=0; col < columns(); col++)
+ {
+ p.fillRect(width, topMargin - d->rowHeight, columnWidth(col), d->rowHeight, QBrush(Qt::gray));
+ p.drawRect(width, topMargin - d->rowHeight, columnWidth(col), d->rowHeight);
+ p.drawText(width, topMargin - d->rowHeight, columnWidth(col), d->rowHeight, Qt::AlignLeft | Qt::AlignVCenter,
+ m_horizontalHeader->label(col));
+ width = width + columnWidth(col);
+ }
+
+ int yOffset = topMargin;
+ int row = 0;
+ int right = 0;
+ for(i = m_data->first(); i; i = m_data->next())
+ {
+ if(!i->isInsertItem())
+ { kdDebug(44021) << "KexiTableView::print: row = " << row << " y = " << yOffset << endl;
+ int xOffset = leftMargin;
+ for(int col=0; col < columns(); col++)
+ {
+ kdDebug(44021) << "KexiTableView::print: col = " << col << " x = " << xOffset << endl;
+ p.saveWorldMatrix();
+ p.translate(xOffset, yOffset);
+ paintCell(&p, i, col, QRect(0, 0, columnWidth(col) + 1, d->rowHeight), true);
+ p.restoreWorldMatrix();
+// p.drawRect(xOffset, yOffset, columnWidth(col), d->rowHeight);
+ xOffset = xOffset + columnWidth(col);
+ right = xOffset;
+ }
+
+ row++;
+ yOffset = topMargin + row * d->rowHeight;
+ }
+
+ if(yOffset > 900)
+ {
+ p.drawLine(leftMargin, topMargin, leftMargin, yOffset);
+ p.drawLine(leftMargin, topMargin, right - 1, topMargin);
+ printer.newPage();
+ yOffset = topMargin;
+ row = 0;
+ }
+ }
+ p.drawLine(leftMargin, topMargin, leftMargin, yOffset);
+ p.drawLine(leftMargin, topMargin, right - 1, topMargin);
+
+// p.drawLine(60,60,120,150);
+ p.end();
+#endif
+}
+#endif
+
+QString KexiTableView::columnCaption(int colNum) const
+{
+ return m_horizontalHeader->label(colNum);
+}
+
+KexiDB::Field* KexiTableView::field(int colNum) const
+{
+ if (!m_data || !m_data->column(colNum))
+ return 0;
+ return m_data->column(colNum)->field();
+}
+
+void KexiTableView::adjustColumnWidthToContents(int colNum)
+{
+ if (!hasData())
+ return;
+ if (colNum==-1) {
+ const int cols = columns();
+ for (int i=0; i<cols; i++)
+ adjustColumnWidthToContents(i);
+ return;
+ }
+
+ int indexOfVisibleColumn = (m_data->column(colNum) && m_data->column(colNum)->columnInfo)
+ ? m_data->column(colNum)->columnInfo->indexForVisibleLookupValue() : -1;
+ if (-1==indexOfVisibleColumn)
+ indexOfVisibleColumn = colNum;
+
+ if (indexOfVisibleColumn < 0)
+ return;
+
+ QPtrListIterator<KexiTableItem> it = m_data->iterator();
+ if (it.current() && it.current()->count()<=(uint)indexOfVisibleColumn)
+ return;
+
+ KexiCellEditorFactoryItem *item = KexiCellEditorFactory::item( columnType(indexOfVisibleColumn) );
+ if (!item)
+ return;
+ QFontMetrics fm(fontMetrics());
+ int maxw = horizontalHeaderVisible()
+ ? fm.width( m_horizontalHeader->label( colNum/* not indexOfVisibleColumn*/ ) ) : 0;
+ if (maxw == 0 && m_data->isEmpty())
+ return; //nothing to adjust
+
+//! \todo js: this is NOT EFFECTIVE for big data sets!!!!
+
+ KexiTableEdit *ed = tableEditorWidget( colNum/* not indexOfVisibleColumn*/ );
+ if (ed) {
+ for (it = m_data->iterator(); it.current(); ++it) {
+ const int wfw = ed->widthForValue( it.current()->at( indexOfVisibleColumn ), fm );
+ maxw = QMAX( maxw, wfw );
+ }
+ const bool focused = currentColumn() == colNum;
+ maxw += (fm.width(" ") + ed->leftMargin() + ed->rightMargin(focused));
+ }
+ if (maxw < KEXITV_MINIMUM_COLUMN_WIDTH )
+ maxw = KEXITV_MINIMUM_COLUMN_WIDTH; //not too small
+ kexidbg << "KexiTableView: setColumnWidth(colNum=" << colNum
+ << ", indexOfVisibleColumn=" << indexOfVisibleColumn << ", width=" << maxw <<" )" << endl;
+ setColumnWidth( colNum/* not indexOfVisibleColumn*/, maxw );
+}
+
+void KexiTableView::setColumnWidth(int colNum, int width)
+{
+ if (columns()<=colNum || colNum < 0)
+ return;
+ const int oldWidth = m_horizontalHeader->sectionSize( colNum );
+ m_horizontalHeader->resizeSection( colNum, width );
+ slotTopHeaderSizeChange( colNum, oldWidth, m_horizontalHeader->sectionSize( colNum ) );
+}
+
+void KexiTableView::maximizeColumnsWidth( const QValueList<int> &columnList )
+{
+ if (!isVisible()) {
+ d->maximizeColumnsWidthOnShow += columnList;
+ return;
+ }
+ if (width() <= m_horizontalHeader->headerWidth())
+ return;
+ //sort the list and make it unique
+ QValueList<int> cl, sortedList = columnList;
+ qHeapSort(sortedList);
+ int i=-999;
+
+ QValueList<int>::ConstIterator it, end( sortedList.constEnd() );
+ for ( it = sortedList.constBegin(); it != end; ++it) {
+ if (i != (*it)) {
+ cl += (*it);
+ i = (*it);
+ }
+ }
+ //resize
+ int sizeToAdd = (width() - m_horizontalHeader->headerWidth()) / cl.count() - verticalHeader()->width();
+ if (sizeToAdd<=0)
+ return;
+ end = cl.constEnd();
+ for ( it = cl.constBegin(); it != end; ++it) {
+ int w = m_horizontalHeader->sectionSize(*it);
+ if (w>0) {
+ m_horizontalHeader->resizeSection(*it, w+sizeToAdd);
+ }
+ }
+ updateContents();
+ editorShowFocus( m_curRow, m_curCol );
+}
+
+void KexiTableView::adjustHorizontalHeaderSize()
+{
+ m_horizontalHeader->adjustHeaderSize();
+}
+
+void KexiTableView::setColumnStretchEnabled( bool set, int colNum )
+{
+ m_horizontalHeader->setStretchEnabled( set, colNum );
+}
+
+void KexiTableView::setEditableOnDoubleClick(bool set)
+{
+ d->editOnDoubleClick = set;
+}
+bool KexiTableView::editableOnDoubleClick() const
+{
+ return d->editOnDoubleClick;
+}
+
+bool KexiTableView::verticalHeaderVisible() const
+{
+ return m_verticalHeader->isVisible();
+}
+
+void KexiTableView::setVerticalHeaderVisible(bool set)
+{
+ int left_width;
+ if (set) {
+ m_verticalHeader->show();
+ left_width = QMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight);
+ }
+ else {
+ m_verticalHeader->hide();
+ left_width = 0;
+ }
+ setMargins( left_width, horizontalHeaderVisible() ? m_horizontalHeader->sizeHint().height() : 0, 0, 0);
+}
+
+bool KexiTableView::horizontalHeaderVisible() const
+{
+ return d->horizontalHeaderVisible;
+}
+
+void KexiTableView::setHorizontalHeaderVisible(bool set)
+{
+ int top_height;
+ d->horizontalHeaderVisible = set; //needed because isVisible() is not always accurate
+ if (set) {
+ m_horizontalHeader->show();
+ top_height = m_horizontalHeader->sizeHint().height();
+ }
+ else {
+ m_horizontalHeader->hide();
+ top_height = 0;
+ }
+ setMargins( verticalHeaderVisible() ? m_verticalHeader->width() : 0, top_height, 0, 0);
+}
+
+void KexiTableView::triggerUpdate()
+{
+// kdDebug(44021) << "KexiTableView::triggerUpdate()" << endl;
+// if (!d->pUpdateTimer->isActive())
+ d->pUpdateTimer->start(20, true);
+// d->pUpdateTimer->start(200, true);
+}
+
+void KexiTableView::setHBarGeometry( QScrollBar & hbar, int x, int y, int w, int h )
+{
+/*todo*/
+ kdDebug(44021)<<"KexiTableView::setHBarGeometry"<<endl;
+ if (d->appearance.navigatorEnabled) {
+ m_navPanel->setHBarGeometry( hbar, x, y, w, h );
+ }
+ else {
+ hbar.setGeometry( x , y, w, h );
+ }
+}
+
+void KexiTableView::setSpreadSheetMode()
+{
+ KexiDataAwareObjectInterface::setSpreadSheetMode();
+ //copy m_navPanelEnabled flag
+ Appearance a = d->appearance;
+ a.navigatorEnabled = m_navPanelEnabled;
+ setAppearance( a );
+}
+
+int KexiTableView::validRowNumber(const QString& text)
+{
+ bool ok=true;
+ int r = text.toInt(&ok);
+ if (!ok || r<1)
+ r = 1;
+ else if (r > (rows()+(isInsertingEnabled()?1:0)))
+ r = rows()+(isInsertingEnabled()?1:0);
+ return r-1;
+}
+
+void KexiTableView::moveToRecordRequested( uint r )
+{
+ if (r > uint(rows()+(isInsertingEnabled()?1:0)))
+ r = rows()+(isInsertingEnabled()?1:0);
+ setFocus();
+ selectRow( r );
+}
+
+void KexiTableView::moveToLastRecordRequested()
+{
+ setFocus();
+ selectRow(rows()>0 ? (rows()-1) : 0);
+}
+
+void KexiTableView::moveToPreviousRecordRequested()
+{
+ setFocus();
+ selectPrevRow();
+}
+
+void KexiTableView::moveToNextRecordRequested()
+{
+ setFocus();
+ selectNextRow();
+}
+
+void KexiTableView::moveToFirstRecordRequested()
+{
+ setFocus();
+ selectFirstRow();
+}
+
+void KexiTableView::copySelection()
+{
+ if (m_currentItem && m_curCol!=-1) {
+ KexiTableEdit *edit = tableEditorWidget( m_curCol );
+ QVariant defaultValue;
+ const bool defaultValueDisplayed
+ = isDefaultValueDisplayed(m_currentItem, m_curCol, &defaultValue);
+ if (edit) {
+ QVariant visibleValue;
+ getVisibleLookupValue(visibleValue, edit, m_currentItem, m_data->column(m_curCol));
+ edit->handleCopyAction(
+ defaultValueDisplayed ? defaultValue : m_currentItem->at( m_curCol ),
+ visibleValue );
+ }
+ }
+}
+
+void KexiTableView::cutSelection()
+{
+ //try to handle @ editor's level
+ KexiTableEdit *edit = tableEditorWidget( m_curCol );
+ if (edit)
+ edit->handleAction("edit_cut");
+}
+
+void KexiTableView::paste()
+{
+ //try to handle @ editor's level
+ KexiTableEdit *edit = tableEditorWidget( m_curCol );
+ if (edit)
+ edit->handleAction("edit_paste");
+}
+
+bool KexiTableView::eventFilter( QObject *o, QEvent *e )
+{
+ //don't allow to stole key my events by others:
+// kexidbg << "spontaneous " << e->spontaneous() << " type=" << e->type() << endl;
+
+ if (e->type()==QEvent::KeyPress) {
+ if (e->spontaneous() /*|| e->type()==QEvent::AccelOverride*/) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ const int k = ke->key();
+ int s = ke->state();
+ //cell editor's events:
+ //try to handle the event @ editor's level
+ KexiTableEdit *edit = tableEditorWidget( m_curCol );
+ if (edit && edit->handleKeyPress(ke, m_editor==edit)) {
+ ke->accept();
+ return true;
+ }
+ else if (m_editor && (o==dynamic_cast<QObject*>(m_editor) || o==m_editor->widget())) {
+ if ( (k==Qt::Key_Tab && (s==Qt::NoButton || s==Qt::ShiftButton))
+ || (overrideEditorShortcutNeeded(ke))
+ || (k==Qt::Key_Enter || k==Qt::Key_Return || k==Qt::Key_Up || k==Qt::Key_Down)
+ || (k==Qt::Key_Left && m_editor->cursorAtStart())
+ || (k==Qt::Key_Right && m_editor->cursorAtEnd())
+ )
+ {
+ //try to steal the key press from editor or it's internal widget...
+ keyPressEvent(ke);
+ if (ke->isAccepted())
+ return true;
+ }
+ }
+ /*
+ else if (e->type()==QEvent::KeyPress && (o==this || (m_editor && o==m_editor->widget()))){//|| o==viewport())
+ keyPressEvent(ke);
+ if (ke->isAccepted())
+ return true;
+ }*/
+/*todo else if ((k==Qt::Key_Tab || k==(Qt::SHIFT|Qt::Key_Tab)) && o==d->navRowNumber) {
+ //tab key focuses tv
+ ke->accept();
+ setFocus();
+ return true;
+ }*/
+ }
+ }
+ else if (o==horizontalScrollBar()) {
+ if ((e->type()==QEvent::Show && !horizontalScrollBar()->isVisible())
+ || (e->type()==QEvent::Hide && horizontalScrollBar()->isVisible())) {
+ updateWidgetContentsSize();
+ }
+ }
+ else if (e->type()==QEvent::Leave) {
+ if (o==viewport() && d->appearance.rowMouseOverHighlightingEnabled
+ && d->appearance.persistentSelections)
+ {
+ if (d->highlightedRow!=-1) {
+ int oldRow = d->highlightedRow;
+ d->highlightedRow = -1;
+ updateRow(oldRow);
+ const bool dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
+ = d->appearance.rowHighlightingEnabled && !d->appearance.persistentSelections;
+ if (oldRow!=m_curRow && m_curRow>=0) {
+ if (!dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted)
+ //no highlight for now: show selection again
+ updateRow(m_curRow);
+ m_verticalHeader->setHighlightedRow(-1);
+ }
+ }
+ }
+ d->recentCellWithToolTip = QPoint(-1,-1);
+ }
+/* else if (e->type()==QEvent::FocusOut && o->inherits("QWidget")) {
+ //hp==true if currently focused widget is a child of this table view
+ const bool hp = KexiUtils::hasParent( static_cast<QWidget*>(o), focusWidget());
+ if (!hp && KexiUtils::hasParent( this, static_cast<QWidget*>(o))) {
+ //accept row editing if focus is moved to foreign widget
+ //(not a child, like eg. editor) from one of our table view's children
+ //or from table view itself
+ if (!acceptRowEdit()) {
+ static_cast<QWidget*>(o)->setFocus();
+ return true;
+ }
+ }
+ }*/
+ return QScrollView::eventFilter(o,e);
+}
+
+void KexiTableView::slotTopHeaderSizeChange(
+ int /*section*/, int /*oldSize*/, int /*newSize*/ )
+{
+ editorShowFocus( m_curRow, m_curCol );
+}
+
+void KexiTableView::setBottomMarginInternal(int pixels)
+{
+ d->internal_bottomMargin = pixels;
+}
+
+void KexiTableView::paletteChange( const QPalette &oldPalette )
+{
+ Q_UNUSED(oldPalette);
+ //update:
+ if (m_verticalHeader)
+ m_verticalHeader->setSelectionBackgroundColor( palette().active().highlight() );
+ if (m_horizontalHeader)
+ m_horizontalHeader->setSelectionBackgroundColor( palette().active().highlight() );
+}
+
+const KexiTableView::Appearance& KexiTableView::appearance() const
+{
+ return d->appearance;
+}
+
+void KexiTableView::setAppearance(const Appearance& a)
+{
+// if (d->appearance.fullRowSelection != a.fullRowSelection) {
+ if (a.fullRowSelection) {
+ d->rowHeight -= 1;
+ }
+ else {
+ d->rowHeight += 1;
+ }
+ if (m_verticalHeader)
+ m_verticalHeader->setCellHeight(d->rowHeight);
+ if (m_horizontalHeader) {
+ setMargins(
+ QMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight),
+ m_horizontalHeader->sizeHint().height(), 0, 0);
+ }
+// }
+ if (a.rowHighlightingEnabled)
+ m_updateEntireRowWhenMovingToOtherRow = true;
+
+ if(!a.navigatorEnabled)
+ m_navPanel->hide();
+ else
+ m_navPanel->show();
+// }
+
+ d->highlightedRow = -1;
+//! @todo is setMouseTracking useful for other purposes?
+ viewport()->setMouseTracking(a.rowMouseOverHighlightingEnabled);
+
+ d->appearance = a;
+
+ setFont(font()); //this also updates contents
+}
+
+int KexiTableView::highlightedRow() const
+{
+ return d->highlightedRow;
+}
+
+void KexiTableView::setHighlightedRow(int row)
+{
+ if (row!=-1) {
+ row = QMIN(rows() - 1 + (isInsertingEnabled()?1:0), row);
+ row = QMAX(0, row);
+ ensureCellVisible(row, -1);
+ }
+ const int previouslyHighlightedRow = d->highlightedRow;
+ if (previouslyHighlightedRow == row) {
+ if (previouslyHighlightedRow!=-1)
+ updateRow(previouslyHighlightedRow);
+ return;
+ }
+ d->highlightedRow = row;
+ if (d->highlightedRow!=-1)
+ updateRow(d->highlightedRow);
+
+ if (previouslyHighlightedRow!=-1)
+ updateRow(previouslyHighlightedRow);
+
+ if (m_curRow>=0 && (previouslyHighlightedRow==-1 || previouslyHighlightedRow==m_curRow)
+ && d->highlightedRow!=m_curRow && !d->appearance.persistentSelections)
+ {
+ //currently selected row needs to be repainted
+ updateRow(m_curRow);
+ }
+}
+
+KexiTableItem *KexiTableView::highlightedItem() const
+{
+ return d->highlightedRow == -1 ? 0 : m_data->at(d->highlightedRow);
+}
+
+void KexiTableView::slotSettingsChanged(int category)
+{
+ if (category==KApplication::SETTINGS_SHORTCUTS) {
+ d->contextMenuKey = KGlobalSettings::contextMenuKey();
+ }
+}
+
+int KexiTableView::lastVisibleRow() const
+{
+ return rowAt( contentsY() );
+}
+
+#include "kexitableview.moc"
+
diff --git a/kexi/widget/tableview/kexitableview.h b/kexi/widget/tableview/kexitableview.h
new file mode 100644
index 00000000..9f9c632e
--- /dev/null
+++ b/kexi/widget/tableview/kexitableview.h
@@ -0,0 +1,639 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Till Busch <till@bux.at>
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@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.
+
+ Original Author: Till Busch <till@bux.at>
+ Original Project: buX (www.bux.at)
+*/
+
+#ifndef KEXITABLEVIEW_H
+#define KEXITABLEVIEW_H
+
+#include <qscrollview.h>
+#include <qvariant.h>
+#include <qptrlist.h>
+#include <qheader.h>
+#include <qtooltip.h>
+
+#include <kdebug.h>
+
+#include "kexitableviewdata.h"
+#include "kexitableedit.h"
+#include <kexiutils/tristate.h>
+#include <widget/utils/kexirecordnavigator.h>
+#include <widget/utils/kexisharedactionclient.h>
+#include "kexidataawareobjectiface.h"
+
+class KPopupMenu;
+class KPrinter;
+class KAction;
+
+class KexiTableHeader;
+class KexiTableItem;
+class KexiTableView;
+class KexiTableEdit;
+class KexiTableViewPrivate;
+class KActionCollection;
+
+namespace KexiDB {
+ class RowEditBuffer;
+}
+
+//! minimum column width in pixels
+#define KEXITV_MINIMUM_COLUMN_WIDTH 10
+
+//! @short KexiTableView class provides a widget for displaying data in a tabular view.
+/*! @see KexiFormScrollView
+*/
+class KEXIDATATABLE_EXPORT KexiTableView :
+ public QScrollView,
+ public KexiRecordNavigatorHandler,
+ public KexiSharedActionClient,
+ public KexiDataAwareObjectInterface
+{
+Q_OBJECT
+KEXI_DATAAWAREOBJECTINTERFACE
+public:
+
+ /*! Defines table view's detailed appearance settings. */
+ class KEXIDATATABLE_EXPORT Appearance {
+ public:
+ Appearance(QWidget *widget = 0);
+
+ /*! base color for cells, default is "Base" color for application's
+ current active palette */
+ QColor baseColor;
+
+ /*! text color for cells, default is "Text" color for application's
+ current active palette */
+ QColor textColor;
+
+ /*! border color for cells, default is QColor(200,200,200) */
+ QColor borderColor;
+
+ /*! empty area color, default is "Base" color for application's
+ current active palette */
+ QColor emptyAreaColor;
+
+ /*! alternate background color, default is KGlobalSettings::alternateBackgroundColor() */
+ QColor alternateBackgroundColor;
+
+ /*! true if background altering should be enabled, true by default */
+ bool backgroundAltering : 1;
+
+ /*! true if full-row-selection mode is set,
+ what means that all cells of the current row are always selected, instead of single cell.
+ This mode is usable for read-only table views, when we're interested only in navigating
+ by rows. False by default, even for read-only table views.
+ */
+ bool fullRowSelection : 1;
+
+ /*! true if fullgrid is enabled. True by default.
+ It is set to false for comboboxpopup table, to mimic original
+ combobox look and feel. */
+ bool gridEnabled : 1;
+
+ /*! \if the navigation panel is enabled (visible) for the view.
+ True by default. */
+ bool navigatorEnabled : 1;
+
+ /*! true if "row highlight" behaviour is enabled. False by default. */
+ bool rowHighlightingEnabled : 1;
+
+ /*! true if "row highlight over " behaviour is enabled. False by default. */
+ bool rowMouseOverHighlightingEnabled : 1;
+
+ /*! true if selection of a row should be kept when a user moved mouse
+ pointer over other rows. Makes only sense when rowMouseOverHighlightingEnabled is true.
+ True by default. It is set to false for comboboxpopup table, to mimic original
+ combobox look and feel. */
+ bool persistentSelections : 1;
+
+ /*! color for row highlight, default is intermediate (33%/60%) between
+ active highlight and base color. */
+ QColor rowHighlightingColor;
+
+ /*! color for text under row highlight, default is the same as textColor.
+ Used when rowHighlightingEnabled is true; */
+ QColor rowHighlightingTextColor;
+
+ /*! color for row highlight for mouseover, default is intermediate (20%/80%) between
+ active highlight and base color. Used when rowMouseOverHighlightingEnabled is true. */
+ QColor rowMouseOverHighlightingColor;
+
+ /*! color for text under row highlight for mouseover, default is the same as textColor.
+ Used when rowMouseOverHighlightingEnabled is true; */
+ QColor rowMouseOverHighlightingTextColor;
+
+ /*! Like rowMouseOverHighlightingColor but for areas painted with alternate color.
+ This is computed using active highlight color and alternateBackgroundColor. */
+ QColor rowMouseOverAlternateHighlightingColor;
+ };
+
+ KexiTableView(KexiTableViewData* data=0, QWidget* parent=0, const char* name=0);
+ virtual ~KexiTableView();
+
+ /*! \return current appearance settings */
+ const Appearance& appearance() const;
+
+ /*! Sets appearance settings. Table view is updated automatically. */
+ void setAppearance(const Appearance& a);
+
+ /*! \return string displayed for column's header \a colNum */
+ QString columnCaption(int colNum) const;
+
+ /*! Convenience function.
+ \return field object that define column \a colNum or NULL if there is no such column */
+ KexiDB::Field* field(int colNum) const;
+
+ /*! Reimplementation for KexiDataAwareObjectInterface */
+ virtual void setSpreadSheetMode();
+
+ /*! \return true if vertical scrollbar's tooltips are enabled (true by default). */
+//moved bool scrollbarToolTipsEnabled() const;
+
+ /*! Enables or disables vertical scrollbar's. */
+//moved void setScrollbarToolTipsEnabled(bool set);
+
+ /*! \return maximum number of rows that can be displayed per one "page"
+ for current table view's size. */
+ virtual int rowsPerPage() const;
+
+ QRect cellGeometry(int row, int col) const;
+ int columnWidth(int col) const;
+ int rowHeight() const;
+ int columnPos(int col) const;
+ int rowPos(int row) const;
+ int columnAt(int pos) const;
+ int rowAt(int pos, bool ignoreEnd=false) const;
+
+ /*! \return last row visible on the screen (counting from 0).
+ The returned value is guaranteed to be smaller or equal to currentRow() or -1
+ if there are no rows. */
+ virtual int lastVisibleRow() const;
+
+ /*! Redraws specified cell. */
+ virtual void updateCell(int row, int col);
+
+ /*! Redraws the current cell. Implemented after KexiDataAwareObjectInterface. */
+ virtual void updateCurrentCell();
+
+ /*! Redraws all cells of specified row. */
+ virtual void updateRow(int row);
+
+ bool editableOnDoubleClick() const;
+ void setEditableOnDoubleClick(bool set);
+
+ //! \return true if the vertical header is visible
+ bool verticalHeaderVisible() const;
+
+ //! Sets vertical header's visibility
+ void setVerticalHeaderVisible(bool set);
+
+ //! \return true if the horizontal header is visible
+ bool horizontalHeaderVisible() const;
+
+ //! Sets horizontal header's visibility
+ void setHorizontalHeaderVisible(bool set);
+
+#ifndef KEXI_NO_PRINT
+ // printing
+// void setupPrinter(KPrinter &printer);
+ void print(KPrinter &printer);
+#endif
+
+ // reimplemented for internal reasons
+ virtual QSizePolicy sizePolicy() const;
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ /*! Reimplemented to update cached fonts and row sizes for the painter. */
+ void setFont(const QFont &f);
+
+ virtual QSize tableSize() const;
+
+ void emitSelected();
+
+ //! single shot after 1ms for contents updatinh
+ void triggerUpdate();
+
+ typedef enum ScrollDirection
+ {
+ ScrollUp,
+ ScrollDown,
+ ScrollLeft,
+ ScrollRight
+ };
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ //! Initializes standard editor cell editor factories. This is called internally, once.
+ static void initCellEditorFactories();
+
+ /*! \return highlighted row number or -1 if no row is highlighted.
+ Makes sense if row highlighting is enabled.
+ @see Appearance::rowHighlightingEnabled setHighlightedRow() */
+ int highlightedRow() const;
+
+ KexiTableItem *highlightedItem() const;
+
+ /*! \return vertical scrollbar. Implemented for KexiDataAwareObjectInterface. */
+ virtual QScrollBar* verticalScrollBar() const { return QScrollView::verticalScrollBar(); }
+
+public slots:
+ virtual void setData( KexiTableViewData *data, bool owner = true )
+ { KexiDataAwareObjectInterface::setData( data, owner ); }
+
+ virtual void clearColumnsInternal(bool repaint);
+
+ /*! Adjusts \a colNum column's width to its (current) contents.
+ If \a colNum == -1, all columns' width is adjusted. */
+ void adjustColumnWidthToContents(int colNum = -1);
+
+ //! Sets width of column width to \a width.
+ void setColumnWidth(int col, int width);
+
+ /*! If \a set is true, \a colNum column is resized to take full possible width.
+ If \a set is false, no automatic resize will be performed.
+ If \a colNum is -1, all columns are equally resized, when needed, to take full possible width.
+ This method behaves like QHeader::setStretchEnabled ( bool b, int section ). */
+ void setColumnStretchEnabled( bool set, int colNum );
+
+ /*! Maximizes widths of columns selected by \a columnList, so the horizontal
+ header has maximum overall width. Each selected column's width will be increased
+ by the same value. Does nothing if \a columnList is empty or there is no free space
+ to resize columns. If this table view is not visible, resizing will be performed on showing. */
+ void maximizeColumnsWidth( const QValueList<int> &columnList );
+
+ /*! Adjusts the size of the sections to fit the size of the horizontal header
+ as completely as possible. Only sections for which column stretch is enabled will be resized.
+ \sa setColumnStretchEnabled() QHeader::adjustHeaderSize() */
+ void adjustHorizontalHeaderSize();
+
+ /*! Sets highlighted row number or -1 if no row has to be highlighted.
+ Makes sense if row highlighting is enabled.
+ @see Appearance::rowHighlightingEnabled */
+ void setHighlightedRow(int row);
+
+ /*! Sets no row that will be highlighted. Equivalent to setHighlightedRow(-1). */
+ inline void clearHighlightedRow() { setHighlightedRow(-1); }
+
+ /*! Ensures that cell at \a row and \a col is visible.
+ If \a col is -1, current column number is used. \a row and \a col (if not -1) must
+ be between 0 and rows() (or cols() accordingly). */
+ virtual void ensureCellVisible(int row, int col/*=-1*/);
+
+// void gotoNext();
+//js int findString(const QString &string);
+
+ /*! Deletes currently selected record; does nothing if no record
+ is currently selected. If record is in edit mode, editing
+ is cancelled before deleting. */
+ virtual void deleteCurrentRow() { KexiDataAwareObjectInterface::deleteCurrentRow(); }
+
+ /*! Inserts one empty row above row \a row. If \a row is -1 (the default),
+ new row is inserted above the current row (or above 1st row if there is no current).
+ A new item becomes current if row is -1 or if row is equal currentRow().
+ This method does nothing if:
+ -inserting flag is disabled (see isInsertingEnabled())
+ -read-only flag is set (see isReadOnly())
+ \ return inserted row's data
+ */
+ virtual KexiTableItem *insertEmptyRow(int row = -1)
+ { return KexiDataAwareObjectInterface::insertEmptyRow(row); }
+
+ /*! Used when Return key is pressed on cell or "+" nav. button is clicked.
+ Also used when we want to continue editing a cell after "invalid value" message
+ was displayed (in this case, \a setText is usually not empty, what means
+ that text will be set in the cell replacing previous value).
+ */
+ virtual void startEditCurrentCell(const QString& setText = QString::null)
+ { KexiDataAwareObjectInterface::startEditCurrentCell(setText); }
+
+ /*! Deletes currently selected cell's contents, if allowed.
+ In most cases delete is not accepted immediately but "row editing" mode is just started. */
+ virtual void deleteAndStartEditCurrentCell()
+ { KexiDataAwareObjectInterface::deleteAndStartEditCurrentCell(); }
+
+ /*! Cancels row editing All changes made to the editing
+ row during this current session will be undone.
+ \return true on success or false on failure (e.g. when editor does not exist) */
+ virtual bool cancelRowEdit() { return KexiDataAwareObjectInterface::cancelRowEdit(); }
+
+ /*! Accepts row editing. All changes made to the editing
+ row during this current session will be accepted (saved).
+ \return true if accepting was successful, false otherwise
+ (e.g. when current row contain data that does not meet given constraints). */
+ virtual bool acceptRowEdit() { return KexiDataAwareObjectInterface::acceptRowEdit(); }
+
+ /*! Specifies, if this table view automatically accepts
+ row editing (using acceptRowEdit()) on accepting any cell's edit
+ (i.e. after acceptEditor()). \sa acceptsRowEditAfterCellAccepting() */
+ virtual void setAcceptsRowEditAfterCellAccepting(bool set)
+ { KexiDataAwareObjectInterface::setAcceptsRowEditAfterCellAccepting(set); }
+
+ /*! Specifies, if this table accepts dropping data on the rows.
+ If enabled:
+ - dragging over row is indicated by drawing a line at bottom side of this row
+ - dragOverRow() signal will be emitted on dragging,
+ -droppedAtRow() will be emitted on dropping
+ By default this flag is set to false. */
+ virtual void setDropsAtRowEnabled(bool set) { KexiDataAwareObjectInterface::setDropsAtRowEnabled(set); }
+
+ virtual bool cancelEditor() { return KexiDataAwareObjectInterface::cancelEditor(); }
+ virtual bool acceptEditor() { return KexiDataAwareObjectInterface::acceptEditor(); }
+
+signals:
+ virtual void dataSet( KexiTableViewData *data );
+
+ virtual void itemSelected(KexiTableItem *);
+ virtual void cellSelected(int col, int row);
+
+ void itemReturnPressed(KexiTableItem *, int row, int col);
+ void itemDblClicked(KexiTableItem *, int row, int col);
+ void itemMouseReleased(KexiTableItem *, int row, int col);
+
+ void dragOverRow(KexiTableItem *item, int row, QDragMoveEvent* e);
+ void droppedAtRow(KexiTableItem *item, int row, QDropEvent *e, KexiTableItem*& newItem);
+
+ /*! Data has been refreshed on-screen - emitted from initDataContents(). */
+ virtual void dataRefreshed();
+
+ virtual void itemChanged(KexiTableItem *, int row, int col);
+ virtual void itemChanged(KexiTableItem *, int row, int col, QVariant oldValue);
+ virtual void itemDeleteRequest(KexiTableItem *, int row, int col);
+ virtual void currentItemDeleteRequest();
+ //! Emitted for spreadsheet mode when an item was deleted and a new item has been appended
+ virtual void newItemAppendedForAfterDeletingInSpreadSheetMode();
+// void addRecordRequest();
+// void contextMenuRequested(KexiTableItem *, int row, int col, const QPoint &);
+ void sortedColumnChanged(int col);
+
+ //! emmited when row editing is started (for updating or inserting)
+ void rowEditStarted(int row);
+
+ //! emmited when row editing is terminated (for updating or inserting)
+ //! no matter if accepted or not
+ void rowEditTerminated(int row);
+
+ //! Emitted in initActions() to force reload actions
+ //! You should remove existing actions and add them again.
+ void reloadActions();
+
+protected slots:
+ void slotSettingsChanged(int category);
+
+ virtual void slotDataDestroying() { KexiDataAwareObjectInterface::slotDataDestroying(); }
+
+ virtual void slotRowsDeleted( const QValueList<int> & );
+
+ //! updates display after many rows deletion
+ void slotColumnWidthChanged( int col, int os, int ns );
+
+ void slotSectionHandleDoubleClicked( int section );
+
+ void slotUpdate();
+ //! implemented because we needed this as slot
+ virtual void sortColumnInternal(int col, int order = 0)
+ { KexiDataAwareObjectInterface::sortColumnInternal(col, order); }
+
+ void slotAutoScroll();
+
+ //! internal, used when top header's size changed
+ void slotTopHeaderSizeChange( int section, int oldSize, int newSize );
+
+ //! receives a signal from cell editors
+ void slotEditRequested();
+
+ /*! Reloads data for this widget.
+ Handles KexiTableViewData::reloadRequested() signal. */
+ virtual void reloadData();
+
+ //! Handles KexiTableViewData::rowRepaintRequested() signal
+ virtual void slotRowRepaintRequested(KexiTableItem& item);
+
+ //! Handles KexiTableViewData::aboutToDeleteRow() signal. Prepares info for slotRowDeleted().
+ virtual void slotAboutToDeleteRow(KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint)
+ { KexiDataAwareObjectInterface::slotAboutToDeleteRow(item, result, repaint); }
+
+ //! Handles KexiTableViewData::rowDeleted() signal to repaint when needed.
+ virtual void slotRowDeleted() { KexiDataAwareObjectInterface::slotRowDeleted(); }
+
+ //! Handles KexiTableViewData::rowInserted() signal to repaint when needed.
+ virtual void slotRowInserted(KexiTableItem *item, bool repaint)
+ { KexiDataAwareObjectInterface::slotRowInserted(item, repaint); }
+
+ //! Like above, not db-aware version
+ virtual void slotRowInserted(KexiTableItem *item, uint row, bool repaint)
+ { KexiDataAwareObjectInterface::slotRowInserted(item, row, repaint); }
+
+ /*! Handles verticalScrollBar()'s valueChanged(int) signal.
+ Called when vscrollbar's value has been changed. */
+ virtual void vScrollBarValueChanged(int v) { KexiDataAwareObjectInterface::vScrollBarValueChanged(v); }
+
+ /*! Handles sliderReleased() signal of the verticalScrollBar(). Used to hide the "row number" tooltip. */
+ virtual void vScrollBarSliderReleased() { KexiDataAwareObjectInterface::vScrollBarSliderReleased(); }
+
+ /*! Handles timeout() signal of the m_scrollBarTipTimer. If the tooltip is visible,
+ m_scrollBarTipTimerCnt is set to 0 and m_scrollBarTipTimerCnt is restarted;
+ else the m_scrollBarTipTimerCnt is just set to 0.*/
+ virtual void scrollBarTipTimeout() { KexiDataAwareObjectInterface::scrollBarTipTimeout(); }
+
+protected:
+ /*! Reimplementation for KexiDataAwareObjectInterface
+ Initializes data contents (resizes it, sets cursor at 1st row).
+ Called on setData(). Also called once on show event after
+ reloadRequested() signal was received from KexiTableViewData object. */
+ virtual void initDataContents();
+
+ /*! Implementation for KexiDataAwareObjectInterface.
+ Updates widget's contents size using QScrollView::resizeContents()
+ depending on tableSize(). */
+ virtual void updateWidgetContentsSize();
+
+ /*! Reimplementation for KexiDataAwareObjectInterface */
+ virtual void clearVariables();
+
+ /*! Implementation for KexiDataAwareObjectInterface */
+ virtual int currentLocalSortingOrder() const;
+
+ /*! Implementation for KexiDataAwareObjectInterface */
+ virtual int currentLocalSortColumn() const;
+
+ /*! Implementation for KexiDataAwareObjectInterface */
+ virtual void setLocalSortingOrder(int col, int order);
+
+ /*! Implementation for KexiDataAwareObjectInterface */
+ virtual void updateGUIAfterSorting();
+
+ /*! Implementation for KexiDataAwareObjectInterface */
+ virtual void updateWidgetScrollBars() { updateScrollBars(); }
+
+// /*! Implementation for KexiDataAwareObjectInterface */
+// virtual void emitSortedColumnChanged(int col) { emit sortedColumnChanged(col); }
+
+// /*! Implementation for KexiDataAwareObjectInterface */
+// virtual void emitRowEditTerminated(int row) { emit rowEditTerminated(row); }
+
+ /*! Implementation for KexiDataAwareObjectInterface.
+ Adds another section within the horizontal header. */
+ virtual void addHeaderColumn(const QString& caption, const QString& description,
+ const QIconSet& icon, int size);
+
+ /*! @internal \return true if the row defined by \a item has default
+ value at column \a col. If this is the case and \a value is not NULL,
+ *value is set to the default value. */
+ bool isDefaultValueDisplayed(KexiTableItem *item, int col, QVariant* value = 0);
+
+ //! painting and layout
+ void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+ void createBuffer(int width, int height);
+ void paintCell(QPainter* p, KexiTableItem *item, int col, int row, const QRect &cr, bool print=false);
+ void paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch);
+ void updateGeometries();
+
+ QPoint contentsToViewport2( const QPoint &p );
+ void contentsToViewport2( int x, int y, int& vx, int& vy );
+ QPoint viewportToContents2( const QPoint& vp );
+
+ // event handling
+ virtual void contentsMousePressEvent(QMouseEvent* e);
+ virtual void contentsMouseReleaseEvent(QMouseEvent* e);
+ //! @internal called by contentsMouseOrEvent() contentsMouseReleaseEvent() to move cursor
+ bool handleContentsMousePressOrRelease(QMouseEvent* e, bool release);
+ virtual void contentsMouseMoveEvent(QMouseEvent* e);
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent* e);
+ virtual void keyPressEvent(QKeyEvent* e);
+ virtual void focusInEvent(QFocusEvent* e);
+ virtual void focusOutEvent(QFocusEvent* e);
+ virtual void resizeEvent(QResizeEvent* e);
+ virtual void viewportResizeEvent(QResizeEvent *e);
+ virtual void showEvent(QShowEvent *e);
+ virtual void contentsDragMoveEvent(QDragMoveEvent *e);
+ virtual void contentsDropEvent(QDropEvent *e);
+ virtual void viewportDragLeaveEvent(QDragLeaveEvent *e);
+ virtual void paletteChange( const QPalette &oldPalette );
+
+ /*! Implementation for KexiDataAwareObjectInterface */
+ virtual KexiDataItemInterface *editor( int col, bool ignoreMissingEditor = false );
+
+ inline KexiTableEdit *tableEditorWidget( int col, bool ignoreMissingEditor = false )
+ { return dynamic_cast<KexiTableEdit*>( editor( col, ignoreMissingEditor ) ); }
+
+ /*! Implementation for KexiDataAwareObjectInterface */
+ virtual void editorShowFocus( int row, int col );
+
+ //! Creates editors and shows it, what usually means the beginning of a cell editing
+ virtual void createEditor(int row, int col, const QString& addText = QString::null,
+ bool removeOld = false);
+
+ bool focusNextPrevChild(bool next);
+
+ /*! Used in key event: \return true if event \a e should execute action \a action_name.
+ Action shortcuts defined by shortCutPressed() are reused, if present, and if \a e matches
+ given action's shortcut - false is returned (beause action is already performed at main
+ window's level).
+ */
+ bool shortCutPressed( QKeyEvent *e, const QCString &action_name );
+
+#if 0 //we have now KexiActionProxy
+ /*! Updates visibility/accesibility of popup menu items,
+ returns false if no items are visible after update. */
+ bool updateContextMenu();
+#endif
+
+ /*! Shows context menu at \a pos for selected cell
+ if menu is configured,
+ else: contextMenuRequested() signal is emmited.
+ Method used in contentsMousePressEvent() (for right button)
+ and keyPressEvent() for Qt::Key_Menu key.
+ If \a pos is QPoint(-1,-1) (the default), menu is positioned below the current cell.
+ */
+ void showContextMenu( const QPoint& pos = QPoint(-1,-1) );
+
+ /*! internal */
+ inline void paintRow(KexiTableItem *item,
+ QPainter *pb, int r, int rowp, int cx, int cy,
+ int colfirst, int collast, int maxwc);
+
+ virtual void setHBarGeometry( QScrollBar & hbar, int x, int y, int w, int h );
+
+ //! Setups navigator widget
+ void setupNavigator();
+
+ //! internal, to determine valid row number when navigator text changed
+ int validRowNumber(const QString& text);
+
+ /*! Reimplementation for KexiDataAwareObjectInterface
+ (viewport()->setFocus() is just added) */
+ virtual void removeEditor();
+
+ //! Internal: updated sched fonts for painting.
+ void updateFonts(bool repaint = false);
+
+ /*! @internal Changes bottom margin settings, in pixels.
+ At this time, it's used by KexiComboBoxPopup to decrease margin for popup's table. */
+ void setBottomMarginInternal(int pixels);
+
+ virtual void updateWidgetContents() { update(); }
+
+ //! for navigator
+ virtual void moveToRecordRequested(uint r);
+ virtual void moveToLastRecordRequested();
+ virtual void moveToPreviousRecordRequested();
+ virtual void moveToNextRecordRequested();
+ virtual void moveToFirstRecordRequested();
+ virtual void addNewRecordRequested() { KexiDataAwareObjectInterface::addNewRecordRequested(); }
+
+ //! Copy current selection to a clipboard (e.g. cell)
+ virtual void copySelection();
+
+ //! Cut current selection to a clipboard (e.g. cell)
+ virtual void cutSelection();
+
+ //! Paste current clipboard contents (e.g. to a cell)
+ virtual void paste();
+
+ /*! Used in KexiDataAwareObjectInterface::slotRowDeleted()
+ to repaint tow \a row and all visible below. */
+ virtual void updateAllVisibleRowsBelow(int row);
+
+ void updateAfterCancelRowEdit();
+ void updateAfterAcceptRowEdit();
+
+ /*! Sets \a cellValue if there is a lookup value for the cell \a item.
+ Used in KexiTableView::paintCell() and KexiTableViewCellToolTip::maybeTip()
+ \return true is \a cellValue has been found. */
+ bool getVisibleLookupValue(QVariant& cellValue, KexiTableEdit *edit,
+ KexiTableItem *item, KexiTableViewColumn *tvcol) const;
+
+// //! Called to repaint contents after a row is deleted.
+// void repaintAfterDelete();
+
+ KexiTableViewPrivate *d;
+
+ class WhatsThis;
+ friend class KexiTableItem;
+ friend class WhatsThis;
+ friend class KexiTableViewCellToolTip;
+};
+
+#endif
diff --git a/kexi/widget/tableview/kexitableview_p.cpp b/kexi/widget/tableview/kexitableview_p.cpp
new file mode 100644
index 00000000..7cf774db
--- /dev/null
+++ b/kexi/widget/tableview/kexitableview_p.cpp
@@ -0,0 +1,67 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Till Busch <till@bux.at>
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@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.
+
+ Original Author: Till Busch <till@bux.at>
+ Original Project: buX (www.bux.at)
+*/
+
+#include "kexitableview_p.h"
+#include "kexitableedit.h"
+
+#include <qlabel.h>
+
+#include <kglobalsettings.h>
+
+
+KexiTableViewPrivate::KexiTableViewPrivate(KexiTableView* t)
+ : appearance(t)
+{
+ clearVariables();
+ tv = t;
+ editOnDoubleClick = true;
+ pBufferPm = 0;
+ disableDrawContents = false;
+ navigatorEnabled = true;
+ contextMenuEnabled = true;
+ skipKeyPress = false;
+//moved vScrollBarValueChanged_enabled = true;
+//moved scrollbarToolTipsEnabled = true;
+//moved scrollBarTipTimerCnt = 0;
+//moved scrollBarTip = 0;
+ ensureCellVisibleOnShow = QPoint(-1,-1);
+ internal_bottomMargin = tv->horizontalScrollBar()->sizeHint().height()/2;
+ highlightedRow = -1;
+ moveCursorOnMouseRelease = false;
+ horizontalHeaderVisible = true;
+ recentCellWithToolTip = QPoint(-1,-1);
+}
+
+KexiTableViewPrivate::~KexiTableViewPrivate()
+{
+ delete pBufferPm;
+//moved delete scrollBarTip;
+}
+
+void KexiTableViewPrivate::clearVariables()
+{
+ // Initialize variables
+}
diff --git a/kexi/widget/tableview/kexitableview_p.h b/kexi/widget/tableview/kexitableview_p.h
new file mode 100644
index 00000000..58fe8574
--- /dev/null
+++ b/kexi/widget/tableview/kexitableview_p.h
@@ -0,0 +1,155 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Till Busch <till@bux.at>
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (C) 2003 Joseph Wenninger <jowenn@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.
+
+ Original Author: Till Busch <till@bux.at>
+ Original Project: buX (www.bux.at)
+*/
+
+#ifndef KEXITABLEVIEW_P_H
+#define KEXITABLEVIEW_P_H
+
+#include "kexitableview.h"
+
+#include <kexidb/roweditbuffer.h>
+#include <widget/utils/kexidisplayutils.h>
+
+#include <qevent.h>
+#include <qtimer.h>
+#include <qvalidator.h>
+#include <qasciidict.h>
+
+#include <kpushbutton.h>
+#include <ktoolbarbutton.h>
+#include <klineedit.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+
+class KexiTableItem;
+class KexiTableRM;
+class KexiTableEdit;
+class QLabel;
+class KexiTableViewHeader;
+
+//! @short a dynamic tooltip for table view cells
+/*! @internal */
+class KexiTableViewCellToolTip : public QToolTip
+{
+ public:
+ KexiTableViewCellToolTip( KexiTableView * tableView );
+ virtual ~KexiTableViewCellToolTip();
+ protected:
+ virtual void maybeTip( const QPoint & p );
+
+ KexiTableView *m_tableView;
+};
+
+/*! KexiTableView's internal structures
+ @internal */
+class KexiTableViewPrivate
+{
+ public:
+
+ KexiTableViewPrivate(KexiTableView* t);
+ ~KexiTableViewPrivate();
+
+ void clearVariables();
+
+ KexiTableView *tv;
+
+ //! editors: one for each column (indexed by KexiTableViewColumn)
+ QPtrDict<KexiTableEdit> editors;
+
+ int rowHeight;
+
+ QPixmap *pBufferPm;
+ QTimer *pUpdateTimer;
+ int menu_id_addRecord;
+ int menu_id_removeRecord;
+
+#if 0//(js) doesn't work!
+ QTimer *scrollTimer;
+#endif
+
+ KexiTableView::ScrollDirection scrollDirection;
+
+ bool editOnDoubleClick : 1;
+
+ bool needAutoScroll : 1;
+
+ bool disableDrawContents : 1;
+
+ /*! true if the navigation panel is enabled (visible) for the view.
+ True by default. */
+ bool navigatorEnabled : 1;
+
+ /*! true if the context menu is enabled (visible) for the view.
+ True by default. */
+ bool contextMenuEnabled : 1;
+
+ /*! used to force single skip keyPress event. */
+ bool skipKeyPress : 1;
+
+ /*! Needed because m_horizontalHeader->isVisible() is not always accurate. True by default. */
+ bool horizontalHeaderVisible : 1;
+
+ /*! true if cursor should be moved on mouse release evenr rather than mouse press
+ in handleContentsMousePressOrRelease().
+ False by default. Used by KeixComboBoxPopup. */
+ bool moveCursorOnMouseRelease : 1;
+
+ KexiTableView::Appearance appearance;
+
+ //! brushes, fonts
+ QBrush diagonalGrayPattern;
+
+ //! Parameters for displaying autonumbers
+ KexiDisplayUtils::DisplayParameters autonumberSignDisplayParameters;
+
+ //! Parameters for displaying default values
+ KexiDisplayUtils::DisplayParameters defaultValueDisplayParameters;
+
+ //! Used by delayed mode of maximizeColumnsWidth()
+ QValueList<int> maximizeColumnsWidthOnShow;
+
+ /*! Used for delayed call of ensureCellVisible() after show().
+ It's equal to (-1,-1) if ensureCellVisible() shouldn't e called. */
+ QPoint ensureCellVisibleOnShow;
+
+ /*! @internal Changes bottom margin settings, in pixels.
+ At this time, it's used by KexiComboBoxPopup to decrease margin for popup's table. */
+ int internal_bottomMargin;
+
+ /*! Helper for "highlighted row" effect. */
+ int highlightedRow;
+
+ /*! Id of context menu key (cached). */
+ int contextMenuKey;
+
+ /*! Specifies currently displayed cell tooltip.
+ Value of QPoint(-1,-1) means "no tooltip". */
+ QPoint recentCellWithToolTip;
+
+ /*! Table cell tooltip */
+ KexiTableViewCellToolTip *cellToolTip;
+};
+
+#endif
diff --git a/kexi/widget/tableview/kexitableviewdata.cpp b/kexi/widget/tableview/kexitableviewdata.cpp
new file mode 100644
index 00000000..62259db3
--- /dev/null
+++ b/kexi/widget/tableview/kexitableviewdata.cpp
@@ -0,0 +1,886 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 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.
+
+ Original Author: Till Busch <till@bux.at>
+ Original Project: buX (www.bux.at)
+*/
+
+#include "kexitableviewdata.h"
+
+#include <kexiutils/validator.h>
+
+#include <kexidb/field.h>
+#include <kexidb/queryschema.h>
+#include <kexidb/roweditbuffer.h>
+#include <kexidb/cursor.h>
+#include <kexidb/utils.h>
+#include <kexi.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qapplication.h>
+
+unsigned short KexiTableViewData::charTable[]=
+{
+ #include "chartable.txt"
+};
+
+KexiTableViewColumn::KexiTableViewColumn(KexiDB::Field& f, bool owner)
+: columnInfo(0)
+, visibleLookupColumnInfo(0)
+, m_field(&f)
+{
+ isDBAware = false;
+ m_fieldOwned = owner;
+ m_captionAliasOrName = m_field->captionOrName();
+ init();
+}
+
+KexiTableViewColumn::KexiTableViewColumn(const QString& name, KexiDB::Field::Type ctype,
+ uint cconst,
+ uint options,
+ uint length, uint precision,
+ QVariant defaultValue,
+ const QString& caption, const QString& description, uint width
+)
+: columnInfo(0)
+, visibleLookupColumnInfo(0)
+{
+ m_field = new KexiDB::Field(
+ name, ctype,
+ cconst,
+ options,
+ length, precision,
+ defaultValue,
+ caption, description, width);
+
+ isDBAware = false;
+ m_fieldOwned = true;
+ m_captionAliasOrName = m_field->captionOrName();
+ init();
+}
+
+KexiTableViewColumn::KexiTableViewColumn(const QString& name, KexiDB::Field::Type ctype,
+ const QString& caption, const QString& description)
+: columnInfo(0)
+, visibleLookupColumnInfo(0)
+{
+ m_field = new KexiDB::Field(
+ name, ctype,
+ KexiDB::Field::NoConstraints,
+ KexiDB::Field::NoOptions,
+ 0, 0,
+ QVariant(),
+ caption, description);
+
+ isDBAware = false;
+ m_fieldOwned = true;
+ m_captionAliasOrName = m_field->captionOrName();
+ init();
+}
+
+// db-aware
+KexiTableViewColumn::KexiTableViewColumn(
+ const KexiDB::QuerySchema &query, KexiDB::QueryColumnInfo& aColumnInfo,
+ KexiDB::QueryColumnInfo* aVisibleLookupColumnInfo)
+: columnInfo(&aColumnInfo)
+, visibleLookupColumnInfo(aVisibleLookupColumnInfo)
+, m_field(aColumnInfo.field)
+{
+ isDBAware = true;
+ m_fieldOwned = false;
+
+ //setup column's caption:
+ if (!columnInfo->field->caption().isEmpty()) {
+ m_captionAliasOrName = columnInfo->field->caption();
+ }
+ else {
+ //reuse alias if available:
+ m_captionAliasOrName = columnInfo->alias;
+ //last hance: use field name
+ if (m_captionAliasOrName.isEmpty())
+ m_captionAliasOrName = columnInfo->field->name();
+ //todo: compute other auto-name?
+ }
+ init();
+ //setup column's readonly flag: true, if
+ // - it's not from parent table's field, or
+ // - if the query itself is coming from read-only connection, or
+ // - if the query itself is stored (i.e. has connection) and lookup column is defined
+ const bool columnFromMasterTable = query.masterTable()==columnInfo->field->table();
+ m_readOnly = !columnFromMasterTable
+ || (query.connection() && query.connection()->isReadOnly());
+// || (query.connection() && (query.connection()->isReadOnly() || visibleLookupColumnInfo));
+//! @todo 2.0: remove this when queries become editable ^^^^^^^^^^^^^^
+// kdDebug() << "KexiTableViewColumn: query.masterTable()=="
+// << (query.masterTable() ? query.masterTable()->name() : "notable") << ", columnInfo->field->table()=="
+// << (columnInfo->field->table() ? columnInfo->field->table()->name() : "notable") << endl;
+
+// m_visible = query.isFieldVisible(&f);
+}
+
+KexiTableViewColumn::KexiTableViewColumn(bool)
+: columnInfo(0)
+, visibleLookupColumnInfo(0)
+, m_field(0)
+{
+ isDBAware = false;
+ init();
+}
+
+KexiTableViewColumn::~KexiTableViewColumn()
+{
+ if (m_fieldOwned)
+ delete m_field;
+ setValidator( 0 );
+ delete m_relatedData;
+}
+
+void KexiTableViewColumn::init()
+{
+ m_relatedData = 0;
+ m_readOnly = false;
+ m_visible = true;
+ m_data = 0;
+ m_validator = 0;
+ m_relatedDataEditable = false;
+ m_headerTextVisible = true;
+}
+
+void KexiTableViewColumn::setValidator( KexiUtils::Validator* v )
+{
+ if (m_validator) {//remove old one
+ if (!m_validator->parent()) //destroy if has no parent
+ delete m_validator;
+ }
+ m_validator = v;
+}
+
+void KexiTableViewColumn::setRelatedData(KexiTableViewData *data)
+{
+ if (isDBAware)
+ return;
+ if (m_relatedData)
+ delete m_relatedData;
+ m_relatedData = 0;
+ if (!data)
+ return;
+ //find a primary key
+ KexiTableViewColumn::ListIterator it( data->columns );
+ for (int id = 0;it.current();++it, id++) {
+ if (it.current()->field()->isPrimaryKey()) {
+ //found, remember
+ m_relatedDataPKeyID = id;
+ m_relatedData = data;
+ return;
+ }
+ }
+}
+
+void KexiTableViewColumn::setRelatedDataEditable(bool set)
+{
+ m_relatedDataEditable = set;
+}
+
+bool KexiTableViewColumn::isReadOnly() const
+{
+ return m_readOnly || (m_data && m_data->isReadOnly());
+}
+
+bool KexiTableViewColumn::acceptsFirstChar(const QChar& ch) const
+{
+ // the field we're looking at can be related to "visible lookup column"
+ // if lookup column is present
+ KexiDB::Field *visibleField = visibleLookupColumnInfo
+ ? visibleLookupColumnInfo->field : m_field;
+ if (visibleField->isNumericType()) {
+ if (ch=='.' || ch==',')
+ return visibleField->isFPNumericType();
+ if (ch=='-')
+ return !visibleField->isUnsigned();
+ if (ch=='+' || (ch>='0' && ch<='9'))
+ return true;
+ return false;
+ }
+
+ switch (visibleField->type()) {
+ case KexiDB::Field::Boolean:
+ return false;
+ case KexiDB::Field::Date:
+ case KexiDB::Field::DateTime:
+ case KexiDB::Field::Time:
+ return ch>='0' && ch<='9';
+ default:;
+ }
+ return true;
+}
+
+
+//------------------------------------------------------
+
+KexiTableViewData::KexiTableViewData()
+ : QObject()
+ , KexiTableViewDataBase()
+{
+ init();
+}
+
+// db-aware ctor
+KexiTableViewData::KexiTableViewData(KexiDB::Cursor *c)
+ : QObject()
+ , KexiTableViewDataBase()
+{
+ init();
+ m_cursor = c;
+ m_containsROWIDInfo = m_cursor->containsROWIDInfo();
+ if (m_cursor && m_cursor->query()) {
+ const KexiDB::QuerySchema::FieldsExpandedOptions fieldsExpandedOptions
+ = m_containsROWIDInfo ? KexiDB::QuerySchema::WithInternalFieldsAndRowID
+ : KexiDB::QuerySchema::WithInternalFields;
+ m_itemSize = m_cursor->query()->fieldsExpanded( fieldsExpandedOptions ).count();
+ }
+ else
+ m_itemSize = columns.count()+(m_containsROWIDInfo?1:0);
+
+ // Allocate KexiTableViewColumn objects for each visible query column
+ const KexiDB::QueryColumnInfo::Vector fields = m_cursor->query()->fieldsExpanded();
+ const uint fieldsCount = fields.count();
+ for (uint i=0;i < fieldsCount;i++) {
+ KexiDB::QueryColumnInfo *ci = fields[i];
+ if (ci->visible) {
+ KexiDB::QueryColumnInfo *visibleLookupColumnInfo = 0;
+ if (ci->indexForVisibleLookupValue() != -1) {
+ //Lookup field is defined
+ visibleLookupColumnInfo = m_cursor->query()->expandedOrInternalField( ci->indexForVisibleLookupValue() );
+ /* not needed
+ if (visibleLookupColumnInfo) {
+ // 2. Create a KexiTableViewData object for each found lookup field
+ }*/
+ }
+ KexiTableViewColumn* col = new KexiTableViewColumn(*m_cursor->query(), *ci, visibleLookupColumnInfo);
+ addColumn( col );
+ }
+ }
+}
+
+KexiTableViewData::KexiTableViewData(
+ const QValueList<QVariant> &keys, const QValueList<QVariant> &values,
+ KexiDB::Field::Type keyType, KexiDB::Field::Type valueType)
+ : QObject()
+ , KexiTableViewDataBase()
+{
+ init(keys, values, keyType, valueType);
+}
+
+KexiTableViewData::KexiTableViewData(
+ KexiDB::Field::Type keyType, KexiDB::Field::Type valueType)
+{
+ const QValueList<QVariant> empty;
+ init(empty, empty, keyType, valueType);
+}
+
+KexiTableViewData::~KexiTableViewData()
+{
+ emit destroying();
+ clearInternal();
+}
+
+void KexiTableViewData::init(
+ const QValueList<QVariant> &keys, const QValueList<QVariant> &values,
+ KexiDB::Field::Type keyType, KexiDB::Field::Type valueType)
+{
+ init();
+ KexiDB::Field *keyField = new KexiDB::Field("key", keyType);
+ keyField->setPrimaryKey(true);
+ KexiTableViewColumn *keyColumn = new KexiTableViewColumn(*keyField, true);
+ keyColumn->setVisible(false);
+ addColumn(keyColumn);
+
+ KexiDB::Field *valueField = new KexiDB::Field("value", valueType);
+ KexiTableViewColumn *valueColumn = new KexiTableViewColumn(*valueField, true);
+ addColumn(valueColumn);
+
+ uint cnt = QMIN(keys.count(), values.count());
+ QValueList<QVariant>::ConstIterator it_keys = keys.constBegin();
+ QValueList<QVariant>::ConstIterator it_values = values.constBegin();
+ for (;cnt>0;++it_keys, ++it_values, cnt--) {
+ KexiTableItem *item = new KexiTableItem(2);
+ (*item)[0] = (*it_keys);
+ (*item)[1] = (*it_values);
+ append( item );
+ }
+}
+
+void KexiTableViewData::init()
+{
+ m_sortedColumn = 0;
+ m_realSortedColumn = 0;
+// m_order = 1;
+ m_order = 0;
+ m_type = 1;
+ m_pRowEditBuffer = 0;
+ m_cursor = 0;
+ m_readOnly = false;
+ m_insertingEnabled = true;
+
+ setAutoDelete(true);
+ columns.setAutoDelete(true);
+ m_visibleColumnsCount=0;
+ m_visibleColumnsIDs.resize(100);
+ m_globalColumnsIDs.resize(100);
+
+ m_autoIncrementedColumn = -2;
+ m_containsROWIDInfo = false;
+ m_itemSize = 0;
+}
+
+void KexiTableViewData::deleteLater()
+{
+ m_cursor = 0;
+ QObject::deleteLater();
+}
+
+void KexiTableViewData::addColumn( KexiTableViewColumn* col )
+{
+// if (!col->isDBAware) {
+// if (!m_simpleColumnsByName)
+// m_simpleColumnsByName = new QDict<KexiTableViewColumn>(101);
+// m_simpleColumnsByName->insert(col->caption,col);//for faster lookup
+// }
+ columns.append( col );
+ col->m_data = this;
+ if (m_globalColumnsIDs.size() < columns.count()) {//sanity
+ m_globalColumnsIDs.resize( m_globalColumnsIDs.size()*2 );
+ }
+ if (col->visible()) {
+ m_visibleColumnsCount++;
+ if (m_visibleColumnsIDs.size() < m_visibleColumnsCount) {//sanity
+ m_visibleColumnsIDs.resize( m_visibleColumnsIDs.size()*2 );
+ }
+ m_visibleColumnsIDs[ columns.count()-1 ] = m_visibleColumnsCount-1;
+ m_globalColumnsIDs[ m_visibleColumnsCount-1 ] = columns.count()-1;
+ }
+ else {
+ m_visibleColumnsIDs[ columns.count()-1 ] = -1;
+ }
+ m_autoIncrementedColumn = -2; //clear cache;
+ if (!m_cursor || !m_cursor->query())
+ m_itemSize = columns.count()+(m_containsROWIDInfo?1:0);
+}
+
+QString KexiTableViewData::dbTableName() const
+{
+ if (m_cursor && m_cursor->query() && m_cursor->query()->masterTable())
+ return m_cursor->query()->masterTable()->name();
+ return QString::null;
+}
+
+void KexiTableViewData::setSorting(int column, bool ascending)
+{
+ if (column>=0 && column<(int)columns.count()) {
+ m_order = (ascending ? 1 : -1);
+ }
+ else {
+ m_order = 0;
+ m_sortedColumn = -1;
+ m_realSortedColumn = -1;
+ return;
+ }
+ // find proper column information for sorting (lookup column points to alternate column with visible data)
+ const KexiTableViewColumn *tvcol = columns.at(column);
+ KexiDB::QueryColumnInfo* visibleLookupColumnInfo = tvcol->visibleLookupColumnInfo;
+ const KexiDB::Field *field = visibleLookupColumnInfo ? visibleLookupColumnInfo->field : tvcol->field();
+ m_sortedColumn = column;
+ m_realSortedColumn = tvcol->columnInfo->indexForVisibleLookupValue()!=-1
+ ? tvcol->columnInfo->indexForVisibleLookupValue() : m_sortedColumn;
+
+ // setup compare function
+ const int t = field->type();
+ if (field->isTextType())
+ cmpFunc = &KexiTableViewData::cmpStr;
+ else if (KexiDB::Field::isFPNumericType(t))
+ cmpFunc = &KexiTableViewData::cmpDouble;
+ else if (t==KexiDB::Field::BigInteger) {
+ if (field->isUnsigned())
+ cmpFunc = &KexiTableViewData::cmpULongLong;
+ else
+ cmpFunc = &KexiTableViewData::cmpLongLong;
+ }
+ else if (t == KexiDB::Field::Integer && field->isUnsigned())
+ cmpFunc = &KexiTableViewData::cmpUInt;
+ else if (t == KexiDB::Field::Boolean || KexiDB::Field::isNumericType(t))
+ cmpFunc = &KexiTableViewData::cmpInt; //other integers
+ else if (t == KexiDB::Field::Date)
+ cmpFunc = &KexiTableViewData::cmpDate;
+ else if (t == KexiDB::Field::Time)
+ cmpFunc = &KexiTableViewData::cmpTime;
+ else if (t == KexiDB::Field::DateTime)
+ cmpFunc = &KexiTableViewData::cmpDateTime;
+ else if (t == KexiDB::Field::BLOB)
+//! TODO allow users to define BLOB sorting function?
+ cmpFunc = &KexiTableViewData::cmpBLOB;
+ else
+ cmpFunc = &KexiTableViewData::cmpStr; //anything else
+}
+
+int KexiTableViewData::compareItems(Item item1, Item item2)
+{
+ return ((this->*cmpFunc) (item1, item2));
+}
+
+//! compare NULLs : NULL is smaller than everything
+#define CMP_NULLS(item1, item2) \
+ m_leftTmp = ((KexiTableItem *)item1)->at(m_realSortedColumn); \
+ if (m_leftTmp.isNull()) \
+ return -m_order; \
+ m_rightTmp = ((KexiTableItem *)item2)->at(m_realSortedColumn); \
+ if (m_rightTmp.isNull()) \
+ return m_order
+
+#define CAST_AND_COMPARE(casting, item1, item2) \
+ CMP_NULLS(item1, item2); \
+ if (m_leftTmp.casting() < m_rightTmp.casting()) \
+ return -m_order; \
+ if (m_leftTmp.casting() > m_rightTmp.casting()) \
+ return m_order; \
+ return 0
+
+int KexiTableViewData::cmpInt(Item item1, Item item2)
+{
+ CAST_AND_COMPARE(toInt, item1, item2);
+}
+
+int KexiTableViewData::cmpUInt(Item item1, Item item2)
+{
+ CAST_AND_COMPARE(toUInt, item1, item2);
+}
+
+int KexiTableViewData::cmpLongLong(Item item1, Item item2)
+{
+ CAST_AND_COMPARE(toLongLong, item1, item2);
+}
+
+int KexiTableViewData::cmpULongLong(Item item1, Item item2)
+{
+ CAST_AND_COMPARE(toULongLong, item1, item2);
+}
+
+int KexiTableViewData::cmpDouble(Item item1, Item item2)
+{
+ CAST_AND_COMPARE(toDouble, item1, item2);
+}
+
+int KexiTableViewData::cmpDate(Item item1, Item item2)
+{
+ CAST_AND_COMPARE(toDate, item1, item2);
+}
+
+int KexiTableViewData::cmpDateTime(Item item1, Item item2)
+{
+ CAST_AND_COMPARE(toDateTime, item1, item2);
+}
+
+int KexiTableViewData::cmpTime(Item item1, Item item2)
+{
+ CAST_AND_COMPARE(toDate, item1, item2);
+}
+
+int KexiTableViewData::cmpStr(Item item1, Item item2)
+{
+ CMP_NULLS(item1, item2);
+ const QString &as = m_leftTmp.toString();
+ const QString &bs = m_rightTmp.toString();
+
+ const QChar *a = as.unicode();
+ const QChar *b = bs.unicode();
+
+ if ( a == b )
+ return 0;
+ if ( a == 0 )
+ return -1;
+ if ( b == 0 )
+ return 1;
+
+ unsigned short au;
+ unsigned short bu;
+
+ int l=QMIN(as.length(),bs.length());
+
+ au = a->unicode();
+ bu = b->unicode();
+ au = (au <= 0x17e ? charTable[au] : 0xffff);
+ bu = (bu <= 0x17e ? charTable[bu] : 0xffff);
+
+ while (l-- && au == bu)
+ {
+ a++,b++;
+ au = a->unicode();
+ bu = b->unicode();
+ au = (au <= 0x17e ? charTable[au] : 0xffff);
+ bu = (bu <= 0x17e ? charTable[bu] : 0xffff);
+ }
+
+ if ( l==-1 )
+ return m_order*(as.length()-bs.length());
+
+ return m_order*(au-bu);
+}
+
+int KexiTableViewData::cmpBLOB(Item item1, Item item2)
+{
+ CMP_NULLS(item1, item2);
+ return m_leftTmp.toByteArray().size() - m_rightTmp.toByteArray().size();
+}
+
+void KexiTableViewData::setReadOnly(bool set)
+{
+ if (m_readOnly == set)
+ return;
+ m_readOnly = set;
+ if (m_readOnly)
+ setInsertingEnabled(false);
+}
+
+void KexiTableViewData::setInsertingEnabled(bool set)
+{
+ if (m_insertingEnabled == set)
+ return;
+ m_insertingEnabled = set;
+ if (m_insertingEnabled)
+ setReadOnly(false);
+}
+
+void KexiTableViewData::clearRowEditBuffer()
+{
+ //init row edit buffer
+ if (!m_pRowEditBuffer)
+ m_pRowEditBuffer = new KexiDB::RowEditBuffer(isDBAware());
+ else
+ m_pRowEditBuffer->clear();
+}
+
+bool KexiTableViewData::updateRowEditBufferRef(KexiTableItem *item,
+ int colnum, KexiTableViewColumn* col, QVariant& newval, bool allowSignals,
+ QVariant *visibleValueForLookupField)
+{
+ m_result.clear();
+ if (allowSignals)
+ emit aboutToChangeCell(item, colnum, newval, &m_result);
+ if (!m_result.success)
+ return false;
+
+ kdDebug() << "KexiTableViewData::updateRowEditBufferRef() column #"
+ << colnum << " = " << newval.toString() << endl;
+ if (!col) {
+ kdWarning() << "KexiTableViewData::updateRowEditBufferRef(): column #"
+ << colnum << " not found! col==0" << endl;
+ return false;
+ }
+ if (!m_pRowEditBuffer)
+ m_pRowEditBuffer = new KexiDB::RowEditBuffer(isDBAware());
+ if (m_pRowEditBuffer->isDBAware()) {
+ if (!(col->columnInfo)) {
+ kdWarning() << "KexiTableViewData::updateRowEditBufferRef(): column #"
+ << colnum << " not found!" << endl;
+ return false;
+ }
+ m_pRowEditBuffer->insert( *col->columnInfo, newval);
+
+ if (col->visibleLookupColumnInfo && visibleValueForLookupField) {
+ //this is value for lookup table: update visible value as well
+ m_pRowEditBuffer->insert( *col->visibleLookupColumnInfo, *visibleValueForLookupField);
+ }
+ return true;
+ }
+ if (!(col->field())) {
+ kdDebug() << "KexiTableViewData::updateRowEditBufferRef(): column #" << colnum<<" not found!" << endl;
+ return false;
+ }
+ //not db-aware:
+ const QString colname = col->field()->name();
+ if (colname.isEmpty()) {
+ kdDebug() << "KexiTableViewData::updateRowEditBufferRef(): column #" << colnum<<" not found!" << endl;
+ return false;
+ }
+ m_pRowEditBuffer->insert(colname, newval);
+ return true;
+}
+
+//get a new value (if present in the buffer), or the old one, otherwise
+//(taken here for optimization)
+#define GET_VALUE if (!val) { \
+ val = m_cursor \
+ ? m_pRowEditBuffer->at( *it_f.current()->columnInfo, true /* useDefaultValueIfPossible */ ) \
+ : m_pRowEditBuffer->at( *f ); \
+ if (!val) \
+ val = &(*it_r); /* get old value */ \
+ }
+
+//! @todo if there're multiple views for this data, we need multiple buffers!
+bool KexiTableViewData::saveRow(KexiTableItem& item, bool insert, bool repaint)
+{
+ if (!m_pRowEditBuffer)
+ return true; //nothing to do
+
+ //check constraints:
+ //-check if every NOT NULL and NOT EMPTY field is filled
+ KexiTableViewColumn::ListIterator it_f(columns);
+ KexiDB::RowData::ConstIterator it_r = item.constBegin();
+ int col = 0;
+ const QVariant *val;
+ for (;it_f.current() && it_r!=item.constEnd();++it_f,++it_r,col++) {
+ KexiDB::Field *f = it_f.current()->field();
+ val = 0;
+ if (f->isNotNull()) {
+ GET_VALUE;
+ //check it
+ if (val->isNull() && !f->isAutoIncrement()) {
+ //NOT NULL violated
+ m_result.msg = i18n("\"%1\" column requires a value to be entered.")
+ .arg(f->captionOrName()) + "\n\n" + Kexi::msgYouCanImproveData();
+ m_result.desc = i18n("The column's constraint is declared as NOT NULL.");
+ m_result.column = col;
+ return false;
+ }
+ }
+ if (f->isNotEmpty()) {
+ GET_VALUE;
+ if (!f->isAutoIncrement() && (val->isNull() || KexiDB::isEmptyValue( f, *val ))) {
+ //NOT EMPTY violated
+ m_result.msg = i18n("\"%1\" column requires a value to be entered.")
+ .arg(f->captionOrName()) + "\n\n" + Kexi::msgYouCanImproveData();
+ m_result.desc = i18n("The column's constraint is declared as NOT EMPTY.");
+ m_result.column = col;
+ return false;
+ }
+ }
+ }
+
+ if (m_cursor) {//db-aware
+ if (insert) {
+ if (!m_cursor->insertRow( static_cast<KexiDB::RowData&>(item), *m_pRowEditBuffer,
+ m_containsROWIDInfo/*also retrieve ROWID*/ ))
+ {
+ m_result.msg = i18n("Row inserting failed.") + "\n\n"
+ + Kexi::msgYouCanImproveData();
+ KexiDB::getHTMLErrorMesage(m_cursor, &m_result);
+
+/* if (desc)
+ *desc =
+js: TODO: use KexiMainWindowImpl::showErrorMessage(const QString &title, KexiDB::Object *obj)
+ after it will be moved somewhere to kexidb (this will require moving other
+ showErrorMessage() methods from KexiMainWindowImpl to libkexiutils....)
+ then: just call: *desc = KexiDB::errorMessage(m_cursor);
+*/
+ return false;
+ }
+ }
+ else { // row updating
+// if (m_containsROWIDInfo)
+// ROWID = item[columns.count()].toULongLong();
+ if (!m_cursor->updateRow( static_cast<KexiDB::RowData&>(item), *m_pRowEditBuffer,
+ m_containsROWIDInfo/*use ROWID*/))
+ {
+ m_result.msg = i18n("Row changing failed.") + "\n\n" + Kexi::msgYouCanImproveData();
+//! @todo set m_result.column if possible
+ KexiDB::getHTMLErrorMesage(m_cursor, m_result.desc);
+ return false;
+ }
+ }
+ }
+ else {//not db-aware version
+ KexiDB::RowEditBuffer::SimpleMap b = m_pRowEditBuffer->simpleBuffer();
+ for (KexiDB::RowEditBuffer::SimpleMap::ConstIterator it = b.constBegin();it!=b.constEnd();++it) {
+ uint i=0;
+ for (KexiTableViewColumn::ListIterator it2(columns);it2.current();++it2, i++) {
+ if (it2.current()->field()->name()==it.key()) {
+ kdDebug() << it2.current()->field()->name()<< ": "<<item[i].toString()<<" -> "<<it.data().toString()<<endl;
+ item[i] = it.data();
+ }
+ }
+ }
+ }
+
+ m_pRowEditBuffer->clear();
+
+ if (repaint)
+ emit rowRepaintRequested(item);
+ return true;
+}
+
+bool KexiTableViewData::saveRowChanges(KexiTableItem& item, bool repaint)
+{
+ kdDebug() << "KexiTableViewData::saveRowChanges()..." << endl;
+ m_result.clear();
+ emit aboutToUpdateRow(&item, m_pRowEditBuffer, &m_result);
+ if (!m_result.success)
+ return false;
+
+ if (saveRow(item, false /*update*/, repaint)) {
+ emit rowUpdated(&item);
+ return true;
+ }
+ return false;
+}
+
+bool KexiTableViewData::saveNewRow(KexiTableItem& item, bool repaint)
+{
+ kdDebug() << "KexiTableViewData::saveNewRow()..." << endl;
+ m_result.clear();
+ emit aboutToInsertRow(&item, &m_result, repaint);
+ if (!m_result.success)
+ return false;
+
+ if (saveRow(item, true /*insert*/, repaint)) {
+ emit rowInserted(&item, repaint);
+ return true;
+ }
+ return false;
+}
+
+bool KexiTableViewData::deleteRow(KexiTableItem& item, bool repaint)
+{
+ m_result.clear();
+ emit aboutToDeleteRow(item, &m_result, repaint);
+ if (!m_result.success)
+ return false;
+
+ if (m_cursor) {//db-aware
+ m_result.success = false;
+ if (!m_cursor->deleteRow( static_cast<KexiDB::RowData&>(item), m_containsROWIDInfo/*use ROWID*/ )) {
+ m_result.msg = i18n("Row deleting failed.");
+/*js: TODO: use KexiDB::errorMessage() for description (desc) as in KexiTableViewData::saveRow() */
+ KexiDB::getHTMLErrorMesage(m_cursor, &m_result);
+ m_result.success = false;
+ return false;
+ }
+ }
+
+ if (!removeRef(&item)) {
+ //aah - this shouldn't be!
+ kdWarning() << "KexiTableViewData::deleteRow(): !removeRef() - IMPL. ERROR?" << endl;
+ m_result.success = false;
+ return false;
+ }
+ emit rowDeleted();
+ return true;
+}
+
+void KexiTableViewData::deleteRows( const QValueList<int> &rowsToDelete, bool repaint )
+{
+ Q_UNUSED( repaint );
+
+ if (rowsToDelete.isEmpty())
+ return;
+ int last_r=0;
+ first();
+ for (QValueList<int>::ConstIterator r_it = rowsToDelete.constBegin(); r_it!=rowsToDelete.constEnd(); ++r_it) {
+ for (; last_r<(*r_it); last_r++) {
+ next();
+ }
+ remove();
+ last_r++;
+ }
+//DON'T CLEAR BECAUSE KexiTableViewPropertyBuffer will clear BUFFERS!
+//--> emit reloadRequested(); //! \todo more effective?
+ emit rowsDeleted( rowsToDelete );
+}
+
+void KexiTableViewData::insertRow(KexiTableItem& item, uint index, bool repaint)
+{
+ if (!insert( index = QMIN(index, count()), &item ))
+ return;
+ emit rowInserted(&item, index, repaint);
+}
+
+void KexiTableViewData::clearInternal()
+{
+ clearRowEditBuffer();
+// qApp->processEvents( 1 );
+//TODO: this is time consuming: find better data model
+// KexiTableViewDataBase::clear();
+ const uint c = count();
+ for (uint i=0; i<c; i++) {
+ removeLast();
+#ifndef KEXI_NO_PROCESS_EVENTS
+ if (i % 1000 == 0)
+ qApp->processEvents( 1 );
+#endif
+ }
+}
+
+bool KexiTableViewData::deleteAllRows(bool repaint)
+{
+ clearInternal();
+
+ bool res = true;
+ if (m_cursor) {
+ //db-aware
+ res = m_cursor->deleteAllRows();
+ }
+
+ if (repaint)
+ emit reloadRequested();
+ return res;
+}
+
+int KexiTableViewData::autoIncrementedColumn()
+{
+ if (m_autoIncrementedColumn==-2) {
+ //find such a column
+ m_autoIncrementedColumn = 0;
+ KexiTableViewColumn::ListIterator it(columns);
+ for (; it.current(); ++it, m_autoIncrementedColumn++) {
+ if (it.current()->field()->isAutoIncrement())
+ break;
+ }
+ if (!it.current())
+ m_autoIncrementedColumn = -1;
+ }
+ return m_autoIncrementedColumn;
+}
+
+void KexiTableViewData::preloadAllRows()
+{
+ if (!m_cursor)
+ return;
+
+ //const uint fcount = m_cursor->fieldCount() + (m_containsROWIDInfo ? 1 : 0);
+ m_cursor->moveFirst();
+ for (int i=0;!m_cursor->eof();i++) {
+ KexiTableItem *item = new KexiTableItem(0);
+ m_cursor->storeCurrentRow(*item);
+// item->debug();
+ append( item );
+ m_cursor->moveNext();
+#ifndef KEXI_NO_PROCESS_EVENTS
+ if ((i % 1000) == 0)
+ qApp->processEvents( 1 );
+#endif
+ }
+}
+
+bool KexiTableViewData::isReadOnly() const
+{
+ return m_readOnly || (m_cursor && m_cursor->connection()->isReadOnly());
+}
+
+#include "kexitableviewdata.moc"
diff --git a/kexi/widget/tableview/kexitableviewdata.h b/kexi/widget/tableview/kexitableviewdata.h
new file mode 100644
index 00000000..970d1d23
--- /dev/null
+++ b/kexi/widget/tableview/kexitableviewdata.h
@@ -0,0 +1,540 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2003 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.
+
+ Original Author: Till Busch <till@bux.at>
+ Original Project: buX (www.bux.at)
+*/
+
+#ifndef KEXITABLEVIEWDATA_H
+#define KEXITABLEVIEWDATA_H
+
+#include <qptrlist.h>
+#include <qvariant.h>
+#include <qvaluevector.h>
+#include <qstring.h>
+#include <qobject.h>
+
+#include "kexitableitem.h"
+
+#include <kexidb/error.h>
+
+namespace KexiDB {
+class Field;
+class QuerySchema;
+class RowEditBuffer;
+class Cursor;
+}
+
+namespace KexiUtils {
+class Validator;
+}
+class KexiTableViewData;
+
+
+/*! Single column definition. */
+class KEXIDATATABLE_EXPORT KexiTableViewColumn {
+ public:
+ typedef QPtrList<KexiTableViewColumn> List;
+ typedef QPtrListIterator<KexiTableViewColumn> ListIterator;
+
+ /*! Not db-aware ctor. if \a owner is true, the field \a will be owned by this column,
+ so you shouldn't care about destroying this field. */
+ KexiTableViewColumn(KexiDB::Field& f, bool owner = false);
+
+ /*! Not db-aware, convenience ctor, like above. The field is created using specified parameters that are
+ equal to these accepted by KexiDB::Field ctor. The column will be the owner
+ of this automatically generated field.
+ */
+ KexiTableViewColumn(const QString& name, KexiDB::Field::Type ctype,
+ uint cconst = KexiDB::Field::NoConstraints,
+ uint options = KexiDB::Field::NoOptions,
+ uint length=0, uint precision=0,
+ QVariant defaultValue=QVariant(),
+ const QString& caption = QString::null,
+ const QString& description = QString::null,
+ uint width = 0);
+
+ /*! Not db-aware, convenience ctor, simplified version of the above. */
+ KexiTableViewColumn(const QString& name, KexiDB::Field::Type ctype, const QString& caption,
+ const QString& description = QString::null);
+
+ //! Db-aware version.
+ KexiTableViewColumn(const KexiDB::QuerySchema &query, KexiDB::QueryColumnInfo& aColumnInfo,
+ KexiDB::QueryColumnInfo* aVisibleLookupColumnInfo = 0);
+
+ virtual ~KexiTableViewColumn();
+
+ virtual bool acceptsFirstChar(const QChar& ch) const;
+
+ /*! \return true if the column is read-only
+ For db-aware column this can depend on whether the column
+ is in parent table of this query. \sa setReadOnly() */
+ bool isReadOnly() const;
+
+//TODO: synchronize this with table view:
+ //! forces readOnly flag to be set to \a ro
+ inline void setReadOnly(bool ro) { m_readOnly=ro; }
+
+ //! Column visibility. By default column is visible.
+ inline bool visible() const { return columnInfo ? columnInfo->visible : m_visible; }
+
+ //! Changes column visibility.
+ inline void setVisible(bool v) {
+ if (columnInfo)
+ columnInfo->visible = v;
+ m_visible=v;
+ }
+
+ /*! Sets icon for displaying in the caption area (header). */
+ void setIcon(const QIconSet& icon) { m_icon = icon; }
+
+ /*! \return bame of icon displayed in the caption area (header). */
+ QIconSet icon() const { return m_icon; }
+
+ /*! If \a visible is true, caption has to be displayed in the column's header,
+ (or field's name if caption is empty. True by default. */
+ void setHeaderTextVisible(bool visible) { m_headerTextVisible = visible; }
+
+ /*! \return true if caption has to be displayed in the column's header,
+ (or field's name if caption is empty. */
+ bool isHeaderTextVisible() const { return m_headerTextVisible; }
+
+ /*! \return whatever is available:
+ - field's caption
+ - or field's alias (from query)
+ - or finally - field's name */
+ inline QString captionAliasOrName() const { return m_captionAliasOrName; }
+
+ /*! Assigns validator \a v for this column.
+ If the validator has no parent object, it will be owned by the column,
+ so you shouldn't care about destroying it. */
+ void setValidator( KexiUtils::Validator* v );
+
+ //! \return validator assigned for this column of 0 if there is no validator assigned.
+ inline KexiUtils::Validator* validator() const { return m_validator; }
+
+ /*! For not-db-aware data only:
+ Sets related data \a data for this column, what defines simple one-field,
+ one-to-many relationship between this column and the primary key in \a data.
+ The relationship will be used to generate a popup editor instead of just regular editor.
+ This assignment has no result if \a data has no primary key defined.
+ \a data is owned, so is will be destroyed when needed. It is also destroyed
+ when another data (or NULL) is set for the same column. */
+ void setRelatedData(KexiTableViewData *data);
+
+ /*! For not-db-aware data only:
+ Related data \a data for this column, what defines simple one-field.
+ NULL by default. \sa setRelatedData() */
+ inline KexiTableViewData *relatedData() const { return m_relatedData; }
+
+ /*! \return field for this column.
+ For db-aware information is taken from \a columnInfo member. */
+ inline KexiDB::Field* field() const { return m_field; }
+
+ /*! Only usable if related data is set (ie. this is for combo boxes).
+ Sets 'editable' flag for this column, what means a new value can be entered
+ by hand. This is similar to QComboBox::setEditable(). */
+ void setRelatedDataEditable(bool set);
+
+ /*! Only usable if related data is set (ie. this is for combo boxes).
+ \return 'editable' flag for this column.
+ False by default. @see setRelatedDataEditable(bool). */
+ inline bool relatedDataEditable() const { return m_relatedDataEditable; }
+
+ /*! A rich field information for db-aware data.
+ For not-db-aware data it is always 0 (use field() instead). */
+ KexiDB::QueryColumnInfo* columnInfo;
+
+ /*! A rich field information for db-aware data. Specifies information for a column
+ that should be visible instead of columnInfo. For example case see
+ @ref KexiDB::QueryColumnInfo::Vector KexiDB::QuerySchema::fieldsExpanded(KexiDB::QuerySchema::FieldsExpandedOptions options = Default)
+
+ For not-db-aware data it is always 0. */
+ KexiDB::QueryColumnInfo* visibleLookupColumnInfo;
+
+ bool isDBAware : 1; //!< true if data is stored in DB, not only in memeory
+
+/* QString caption;
+ int type; //!< one of KexiDB::Field::Type
+ uint width;
+*/
+// bool isNull() const;
+
+/* virtual QString caption() const;
+ virtual void setCaption(const QString& c);
+ */
+ protected:
+ //! special ctor that do not allocate d member;
+ KexiTableViewColumn(bool);
+
+ void init();
+
+ QString m_captionAliasOrName;
+
+ QIconSet m_icon;
+
+ KexiUtils::Validator* m_validator;
+
+ //! Data that this column is assigned to.
+ KexiTableViewData* m_data;
+
+ KexiTableViewData* m_relatedData;
+ uint m_relatedDataPKeyID;
+
+ KexiDB::Field* m_field;
+
+ bool m_readOnly : 1;
+ bool m_fieldOwned : 1;
+ bool m_visible : 1;
+ bool m_relatedDataEditable : 1;
+ bool m_headerTextVisible : 1;
+
+ friend class KexiTableViewData;
+};
+
+
+/*! List of column definitions. */
+//typedef QValueVector<KexiTableViewColumn> KexiTableViewColumnList;
+
+typedef QPtrList<KexiTableItem> KexiTableViewDataBase;
+
+/*! Reimplements QPtrList to allow configurable sorting and more.
+ Original author: Till Busch.
+ Reimplemented by Jaroslaw Staniek.
+*/
+class KEXIDATATABLE_EXPORT KexiTableViewData : public QObject, protected KexiTableViewDataBase
+{
+ Q_OBJECT
+
+public:
+ typedef QPtrListIterator<KexiTableItem> Iterator;
+
+ //! not db-aware version
+ KexiTableViewData();
+
+ //! db-aware version
+ KexiTableViewData(KexiDB::Cursor *c);
+
+//TODO: make this more generic: allow to add more columns!
+ /*! Defines two-column table usually used with comboboxes.
+ First column is invisible and contains key values.
+ Second column and contains user-visible value.
+ @param keys a list of keys
+ @param values a list of text values (must be of the same length as keys list)
+ @param keyType a type for keys
+ @param valueType a type for values
+ */
+ KexiTableViewData(
+ const QValueList<QVariant> &keys, const QValueList<QVariant> &values,
+ KexiDB::Field::Type keyType = KexiDB::Field::Text,
+ KexiDB::Field::Type valueType = KexiDB::Field::Text);
+
+ /*! Like above constructor, but keys and values are not provided.
+ You can do this later by calling append(KexiTableItem*) method.
+ (KexiTableItem object must have exactly two columns)
+ */
+ KexiTableViewData(KexiDB::Field::Type keyType, KexiDB::Field::Type valueType);
+
+ virtual ~KexiTableViewData();
+//js void setSorting(int key, bool order=true, short type=1);
+
+ /*! Preloads all rows provided by cursor (only for db-aware version). */
+//! @todo change to bool and return false on error!
+ void preloadAllRows();
+
+ /*! Sets sorting for \a column. If \a column is -1, sorting is disabled. */
+ void setSorting(int column, bool ascending=true);
+
+ /*! \return the column number by which the data is sorted,
+ or -1 if sorting is disabled. In this case sortingOrder() will return 0.
+ Initial sorted column number for data after instantiating object is -1. */
+ inline int sortedColumn() const { return m_sortedColumn; }
+
+ /*! \return 1 if ascending sort order is set, -1 id descending sort order is set,
+ or 0 if no sorting is set. This is independent of whether data is sorted now.
+ Initial sorting for data after instantiating object is 0. */
+ inline int sortingOrder() const { return m_order; }
+
+ /*! Adds column \a col.
+ Warning: \a col will be owned by this object, and deleted on its destruction. */
+ void addColumn( KexiTableViewColumn* col );
+
+ inline int globalColumnID(int visibleID) { return m_globalColumnsIDs.at( visibleID ); }
+ inline int visibleColumnID(int globalID) { return m_visibleColumnsIDs.at( globalID ); }
+
+ /*virtual?*/
+ /*! \return true if this db-aware data set. */
+ inline bool isDBAware() { return m_cursor; }
+
+ /*! For db-aware data set only: table name is returned;
+ equivalent to cursor()->query()->parentTable()->name(). */
+ QString dbTableName() const;
+
+ inline KexiDB::Cursor* cursor() const { return m_cursor; }
+
+ inline uint columnsCount() const { return columns.count(); }
+
+ inline KexiTableViewColumn* column(uint c) { return columns.at(c); }
+
+ /*! Columns information */
+ KexiTableViewColumn::List columns;
+
+ /*! \return true if data is not editable. Can be set using setReadOnly()
+ but it's still true if database cursor returned by cursor()
+ is not 0 and has read-only connection. */
+ virtual bool isReadOnly() const;
+
+ /*! Sets readOnly flag for this data.
+ If \a set is true, insertingEnabled flag will be cleared automatically.
+ \sa isInsertingEnabled() */
+ virtual void setReadOnly(bool set);
+
+ /*! \return true if data inserting is enabled (the default). */
+ virtual bool isInsertingEnabled() const { return m_insertingEnabled; }
+
+ /*! Sets insertingEnabled flag. If true, empty row is available
+ If \a set is true, read-only flag will be cleared automatically.
+ \sa setReadOnly() */
+ virtual void setInsertingEnabled(bool set);
+
+ /*! Clears and initializes internal row edit buffer for incoming editing.
+ Creates buffer using KexiDB::RowEditBuffer(false) (false means not db-aware type)
+ if our data is not db-aware,
+ or db-aware buffer if data is db-aware (isDBAware()==true).
+ \sa KexiDB::RowEditBuffer
+ */
+ void clearRowEditBuffer();
+
+ /*! Updates internal row edit buffer: currently edited column \a col (number \a colnum)
+ has now assigned new value of \a newval.
+ Uses column's caption to address the column in buffer
+ if the buffer is of simple type, or db-aware buffer if (isDBAware()==true).
+ (then fields are addressed with KexiDB::Field, instead of caption strings).
+ If \a allowSignals is true (the default), aboutToChangeCell() signal is emitted.
+ \a visibleValueForLookupField allows to pass visible value (usually a text)
+ for a lookup field (only reasonable if col->visibleLookupColumnInfo != 0).
+ Note that \a newval may be changed in aboutToChangeCell() signal handler.
+ \sa KexiDB::RowEditBuffer */
+ bool updateRowEditBufferRef(KexiTableItem *item,
+ int colnum, KexiTableViewColumn* col, QVariant& newval, bool allowSignals = true,
+ QVariant *visibleValueForLookupField = 0);
+
+ /*! Added for convenience. Like above but \a newval is passed by value. */
+ inline bool updateRowEditBuffer(KexiTableItem *item, int colnum, KexiTableViewColumn* col,
+ QVariant newval, bool allowSignals = true)
+ {
+ QVariant newv(newval);
+ return updateRowEditBufferRef(item, colnum, col, newv, allowSignals);
+ }
+
+ /*! Added for convenience. Like above but it's assumed that \a item item's columns are ordered
+ like in table view, not like in form view. Don't use this with form views. */
+ inline bool updateRowEditBuffer(KexiTableItem *item, int colnum,
+ QVariant newval, bool allowSignals = true)
+ {
+ KexiTableViewColumn* col = columns.at(colnum);
+ return col ? updateRowEditBufferRef(item, colnum, col, newval, allowSignals) : false;
+ }
+
+ inline KexiDB::RowEditBuffer* rowEditBuffer() const { return m_pRowEditBuffer; }
+
+ /*! \return last operation's result information (always not null). */
+ inline KexiDB::ResultInfo* result() { return &m_result; }
+
+ bool saveRowChanges(KexiTableItem& item, bool repaint = false);
+
+ bool saveNewRow(KexiTableItem& item, bool repaint = false);
+
+ bool deleteRow(KexiTableItem& item, bool repaint = false);
+
+ /*! Deletes rows (by number) passed with \a rowsToDelete.
+ Currently, this method is only for non data-aware tables. */
+ void deleteRows( const QValueList<int> &rowsToDelete, bool repaint = false );
+
+ /*! Deletes all rows. Works either for db-aware and non db-aware tables.
+ Column's definition is not changed.
+ For db-aware version, all rows are removed from a database.
+ Row-edit buffer is cleared.
+
+ If \a repaint is true, reloadRequested() signal
+ is emitted after deleting (if at least one row was deleted),
+ so presenters can repaint their contents.
+
+ \return true on success. */
+ virtual bool deleteAllRows(bool repaint = false);
+
+ /*! @internal method, used mostly by specialized classes like KexiTableView.
+ Clears internal row structures. Row-edit buffer is cleared.
+ Does not touch data @ database backend.
+ Use deleteAllRows() to safely delete all rows. */
+ virtual void clearInternal();
+
+ /*! Inserts new \a item at index \a index.
+ \a item will be owned by this data object.
+ Note: Reasonable only for not not-db-aware version. */
+ void insertRow(KexiTableItem& item, uint index, bool repaint = false);
+
+/*TODO: add this as well?
+ void insertRow(KexiTableItem& item, KexiTableItem& aboveItem); */
+
+ //! \return index of autoincremented column. The result is cached.
+//! \todo what about multiple autoinc columns?
+//! \todo what about changing column order?
+ int autoIncrementedColumn();
+
+ //! Emits reloadRequested() signal to reload presenters.
+ void reload() { emit reloadRequested(); }
+
+ inline KexiTableItem* at( uint index ) { return KexiTableViewDataBase::at(index); }
+ inline virtual uint count() const { return KexiTableViewDataBase::count(); }
+ inline bool isEmpty () const { return KexiTableViewDataBase::isEmpty(); }
+ inline KexiTableItem* first() { return KexiTableViewDataBase::first(); }
+ inline KexiTableItem* last() { return KexiTableViewDataBase::last(); }
+ inline int findRef( const KexiTableItem* item ) { return KexiTableViewDataBase::findRef(item); }
+ inline void sort() { KexiTableViewDataBase::sort(); }
+ inline bool removeFirst() { return KexiTableViewDataBase::removeFirst(); }
+ inline bool removeLast() { return KexiTableViewDataBase::removeLast(); }
+ inline void append( const KexiTableItem* item ) { KexiTableViewDataBase::append(item); }
+ inline void prepend( const KexiTableItem* item ) { KexiTableViewDataBase::prepend(item); }
+ inline Iterator iterator() { return Iterator(*this); }
+ inline Iterator* createIterator() { return new Iterator(*this); }
+
+ /*! \return true if ROWID information is stored within every row.
+ Only reasonable for db-aware version. ROWID information is available
+ if DriverBehaviour::ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE == false
+ for a KexiDB database driver and a table has no primary key defined.
+ Phisically, ROWID information is stored after last KexiTableItem's element,
+ so every KexiTableItem's length is expanded by one. */
+ inline bool containsROWIDInfo() const { return m_containsROWIDInfo; }
+
+ inline KexiTableItem* createItem() const
+ { return new KexiTableItem(m_itemSize); }
+
+public slots:
+ //! @internal The same as QObject::deleteLater() but also sets smart pointer m_cursor to 0 to avoid crashes...
+ void deleteLater();
+
+signals:
+ void destroying();
+
+ /*! Emitted before change of the single, currently edited cell.
+ Connect this signal to your slot and set \a result->success to false
+ to disallow this change. You can also change \a newValue to other value,
+ or change other columns in \a item row. */
+ void aboutToChangeCell(KexiTableItem *item, int colnum, QVariant& newValue,
+ KexiDB::ResultInfo* result);
+
+ /*! Emited before inserting of a new, current row.
+ Connect this signal to your slot and set \a result->success to false
+ to disallow this inserting. You can also change columns in \a item row. */
+ void aboutToInsertRow(KexiTableItem *item, KexiDB::ResultInfo* result, bool repaint);
+
+ /*! Emited before changing of an edited, current row.
+ Connect this signal to your slot and set \a result->success to false
+ to disallow this change. You can also change columns in \a item row. */
+ void aboutToUpdateRow(KexiTableItem *item, KexiDB::RowEditBuffer* buffer,
+ KexiDB::ResultInfo* result);
+
+ void rowUpdated(KexiTableItem*); //!< Current row has been updated
+
+ void rowInserted(KexiTableItem*, bool repaint); //!< A row has been inserted
+
+ //! A row has been inserted at \a index position (not db-aware data only)
+ void rowInserted(KexiTableItem*, uint index, bool repaint);
+
+ /*! Emited before deleting of a current row.
+ Connect this signal to your slot and set \a result->success to false
+ to disallow this deleting. */
+ void aboutToDeleteRow(KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint);
+
+ //! Current row has been deleted
+ void rowDeleted();
+
+ //! Rows have been deleted
+ void rowsDeleted( const QValueList<int> &rowsToDelete );
+
+ //! Displayed data needs to be reloaded in all presenters.
+ void reloadRequested();
+
+ void rowRepaintRequested(KexiTableItem&);
+
+protected:
+ void init();
+ void init(
+ const QValueList<QVariant> &keys, const QValueList<QVariant> &values,
+ KexiDB::Field::Type keyType, KexiDB::Field::Type valueType);
+
+ virtual int compareItems(Item item1, Item item2);
+ int cmpStr(Item item1, Item item2);
+ int cmpInt(Item item1, Item item2);
+ int cmpUInt(Item item1, Item item2);
+ int cmpLongLong(Item item1, Item item2);
+ int cmpULongLong(Item item1, Item item2);
+ int cmpDouble(Item item1, Item item2);
+ int cmpDate(Item item1, Item item2);
+ int cmpDateTime(Item item1, Item item2);
+ int cmpTime(Item item1, Item item2);
+
+ //! Compare function for BLOB data (QByteArray). Uses size as the weight.
+ int cmpBLOB(Item item1, Item item2);
+
+ //! internal: for saveRowChanges() and saveNewRow()
+ bool saveRow(KexiTableItem& item, bool insert, bool repaint);
+
+ //! (logical) sorted column number, set by setSorting()
+ //! can differ from m_realSortedColumn if there's lookup column used
+ int m_sortedColumn;
+ //! real sorted column number, set by setSorting(), used by cmp*() methods
+ int m_realSortedColumn;
+ short m_order;
+ short m_type;
+ int m_itemSize;
+ static unsigned short charTable[];
+ KexiDB::RowEditBuffer *m_pRowEditBuffer;
+ QGuardedPtr<KexiDB::Cursor> m_cursor;
+
+ //! used to faster lookup columns of simple type (not dbaware)
+// QDict<KexiTableViewColumn> *m_simpleColumnsByName;
+
+ KexiDB::ResultInfo m_result;
+
+ uint m_visibleColumnsCount;
+ QValueVector<int> m_visibleColumnsIDs, m_globalColumnsIDs;
+
+ bool m_readOnly : 1;
+ bool m_insertingEnabled : 1;
+
+ /*! Used in acceptEditor() to avoid infinite recursion,
+ eg. when we're calling acceptRowEdit() during cell accepting phase. */
+ bool m_inside_acceptEditor : 1;
+
+ //! @see containsROWIDInfo()
+ bool m_containsROWIDInfo : 1;
+
+ int m_autoIncrementedColumn;
+
+ int (KexiTableViewData::*cmpFunc)(void *, void *);
+
+ //! Temporary, used in compare functions like cmpInt(), cmpString()
+ //! to avoid memory allocations.
+ QVariant m_leftTmp, m_rightTmp;
+};
+
+#endif
diff --git a/kexi/widget/tableview/kexitableviewheader.cpp b/kexi/widget/tableview/kexitableviewheader.cpp
new file mode 100644
index 00000000..3656a041
--- /dev/null
+++ b/kexi/widget/tableview/kexitableviewheader.cpp
@@ -0,0 +1,202 @@
+/* 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 "kexitableviewheader.h"
+
+#include <qapplication.h>
+#include <qtooltip.h>
+#include <qstyle.h>
+
+#include <kexiutils/utils.h>
+#include <kexiutils/styleproxy.h>
+
+//! @internal A style that allows to temporary change background color while
+//! drawing header section primitive. Used in KexiTableViewHeader.
+class KexiTableViewHeaderStyle : public KexiUtils::StyleProxy
+{
+ public:
+ KexiTableViewHeaderStyle(QStyle *parentStyle, QWidget *widget)
+ : KexiUtils::StyleProxy(parentStyle)
+ {
+ setBackgroundColor( widget->palette().active().background() );
+ }
+ ~KexiTableViewHeaderStyle() {}
+
+ virtual void drawPrimitive( PrimitiveElement pe,
+ QPainter *p, const QRect &r, const QColorGroup &cg, SFlags flags = Style_Default,
+ const QStyleOption& option = QStyleOption::Default ) const
+ {
+ if (pe==QStyle::PE_HeaderSection) {
+ QColorGroup newCg(cg);
+ newCg.setColor(QColorGroup::Button, m_backgroundColor);
+ newCg.setColor(QColorGroup::Background, m_backgroundColor); //set background color as well (e.g. for thinkeramik)
+ m_style->drawPrimitive( pe, p, r, newCg, flags, option );
+ return;
+ }
+ m_style->drawPrimitive( pe, p, r, cg, flags, option );
+ }
+
+ void setBackgroundColor( const QColor& color ) { m_backgroundColor = color; }
+
+ protected:
+ QColor m_backgroundColor;
+};
+
+KexiTableViewHeader::KexiTableViewHeader(QWidget * parent, const char * name)
+ : QHeader(parent, name)
+ , m_lastToolTipSection(-1)
+ , m_selectionBackgroundColor(qApp->palette().active().highlight())
+ , m_selectedSection(-1)
+ , m_styleChangeEnabled(true)
+{
+ styleChange( style() );
+ installEventFilter(this);
+ connect(this, SIGNAL(sizeChange(int,int,int)),
+ this, SLOT(slotSizeChange(int,int,int)));
+}
+
+KexiTableViewHeader::~KexiTableViewHeader()
+{
+}
+
+void KexiTableViewHeader::styleChange( QStyle& oldStyle )
+{
+ QHeader::styleChange( oldStyle );
+ if (!m_styleChangeEnabled)
+ return;
+ m_styleChangeEnabled = false;
+ setStyle( new KexiTableViewHeaderStyle(&qApp->style(), this) );
+ m_styleChangeEnabled = true;
+}
+
+int KexiTableViewHeader::addLabel ( const QString & s, int size )
+{
+ m_toolTips += "";
+ slotSizeChange(0,0,0);//refresh
+ return QHeader::addLabel(s, size);
+}
+
+int KexiTableViewHeader::addLabel ( const QIconSet & iconset, const QString & s, int size )
+{
+ m_toolTips += "";
+ slotSizeChange(0,0,0);//refresh
+ return QHeader::addLabel(iconset, s, size);
+}
+
+void KexiTableViewHeader::removeLabel( int section )
+{
+ if (section < 0 || section >= count())
+ return;
+ QStringList::Iterator it = m_toolTips.begin();
+ it += section;
+ m_toolTips.remove(it);
+ slotSizeChange(0,0,0);//refresh
+ QHeader::removeLabel(section);
+}
+
+void KexiTableViewHeader::setToolTip( int section, const QString & toolTip )
+{
+ if (section < 0 || section >= (int)m_toolTips.count())
+ return;
+ m_toolTips[ section ] = toolTip;
+}
+
+bool KexiTableViewHeader::eventFilter(QObject * watched, QEvent * e)
+{
+ if (e->type()==QEvent::MouseMove) {
+ const int section = sectionAt( static_cast<QMouseEvent*>(e)->x() );
+ if (section != m_lastToolTipSection && section >= 0 && section < (int)m_toolTips.count()) {
+ QToolTip::remove(this, m_toolTipRect);
+ QString tip = m_toolTips[ section ];
+ if (tip.isEmpty()) { //try label
+ QFontMetrics fm(font());
+ int minWidth = fm.width( label( section ) ) + style().pixelMetric( QStyle::PM_HeaderMargin );
+ QIconSet *iset = iconSet( section );
+ if (iset)
+ minWidth += (2+iset->pixmap( QIconSet::Small, QIconSet::Normal ).width()); //taken from QHeader::sectionSizeHint()
+ if (minWidth > sectionSize( section ))
+ tip = label( section );
+ }
+ if (tip.isEmpty()) {
+ m_lastToolTipSection = -1;
+ }
+ else {
+ QToolTip::add(this, m_toolTipRect = sectionRect(section), tip);
+ m_lastToolTipSection = section;
+ }
+ }
+ }
+// if (e->type()==QEvent::MouseButtonPress) {
+// todo
+// }
+ return QHeader::eventFilter(watched, e);
+}
+
+void KexiTableViewHeader::slotSizeChange(int /*section*/, int /*oldSize*/, int /*newSize*/ )
+{
+ if (m_lastToolTipSection>0)
+ QToolTip::remove(this, m_toolTipRect);
+ m_lastToolTipSection = -1; //tooltip's rect is now invalid
+}
+
+void KexiTableViewHeader::setSelectionBackgroundColor(const QColor &color)
+{
+ m_selectionBackgroundColor = color;
+}
+
+QColor KexiTableViewHeader::selectionBackgroundColor() const
+{
+ return m_selectionBackgroundColor;
+}
+
+void KexiTableViewHeader::setSelectedSection(int section)
+{
+ if (m_selectedSection==section || (section!=-1 && section>=count()))
+ return;
+ const int oldSection = m_selectedSection;
+ m_selectedSection = section;
+ if (oldSection!=-1)
+ update(sRect(oldSection));
+ if (m_selectedSection!=-1)
+ update(sRect(m_selectedSection));
+}
+
+int KexiTableViewHeader::selectedSection() const
+{
+ return m_selectedSection;
+}
+
+void KexiTableViewHeader::paintSection( QPainter * p, int index, const QRect & fr )
+{
+ const bool paintSelection = index==m_selectedSection && index != -1;
+ if (paintSelection) {
+ static_cast<KexiTableViewHeaderStyle&>(style()).setBackgroundColor(
+ KexiUtils::blendedColors(
+ palette().active().background(), m_selectionBackgroundColor, 2, 1) );
+ }
+
+ QHeader::paintSection( p, index, fr );
+
+ if (paintSelection) { //revert the color for subsequent paints
+ static_cast<KexiTableViewHeaderStyle&>(style()).setBackgroundColor(
+ palette().active().background());
+ }
+}
+
+#include "kexitableviewheader.moc"
diff --git a/kexi/widget/tableview/kexitableviewheader.h b/kexi/widget/tableview/kexitableviewheader.h
new file mode 100644
index 00000000..5da3fa7b
--- /dev/null
+++ b/kexi/widget/tableview/kexitableviewheader.h
@@ -0,0 +1,75 @@
+/* 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 KEXITABLEVIEWHEADER_H
+#define KEXITABLEVIEWHEADER_H
+
+#include <qheader.h>
+
+class QStyle;
+
+//! @short A table view header with additional actions.
+/*! Displays field description (Field::description()) text as tool tip, if available.
+ Displays tool tips if a pointed section is not wide enough to fit its label text.
+
+ \todo react on indexChange ( int section, int fromIndex, int toIndex ) signal
+*/
+class KEXIDATATABLE_EXPORT KexiTableViewHeader : public QHeader
+{
+ Q_OBJECT
+
+ public:
+ KexiTableViewHeader(QWidget * parent = 0, const char * name = 0);
+
+ virtual ~KexiTableViewHeader();
+
+ int addLabel( const QString & s, int size = -1 );
+
+ int addLabel( const QIconSet & iconset, const QString & s, int size = -1 );
+
+ void removeLabel( int section );
+
+ /*! Sets \a toolTip for \a section. */
+ void setToolTip( int section, const QString & toolTip );
+
+ virtual bool eventFilter(QObject * watched, QEvent * e);
+
+ void setSelectedSection(int section);
+ int selectedSection() const;
+
+ QColor selectionBackgroundColor() const;
+ void setSelectionBackgroundColor(const QColor &color);
+
+ protected slots:
+ void slotSizeChange(int section, int oldSize, int newSize );
+
+ protected:
+ virtual void paintSection ( QPainter * p, int index, const QRect & fr );
+ virtual void styleChange( QStyle& oldStyle );
+
+ int m_lastToolTipSection;
+ QRect m_toolTipRect;
+
+ QStringList m_toolTips;
+ QColor m_selectionBackgroundColor;
+ int m_selectedSection;
+ bool m_styleChangeEnabled : 1;
+};
+
+#endif
diff --git a/kexi/widget/tableview/kexitextformatter.cpp b/kexi/widget/tableview/kexitextformatter.cpp
new file mode 100644
index 00000000..f4e0b89d
--- /dev/null
+++ b/kexi/widget/tableview/kexitextformatter.cpp
@@ -0,0 +1,237 @@
+/* This file is part of the KDE project
+ Copyright (C) 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 <klocale.h>
+
+#include "kexitextformatter.h"
+#include <widget/utils/kexidatetimeformatter.h>
+#include <kexidb/utils.h>
+
+//! @internal
+class KexiTextFormatter::Private
+{
+ public:
+ Private() : field(0), dateFormatter(0), timeFormatter(0)
+ {
+ }
+
+ ~Private()
+ {
+ delete dateFormatter;
+ delete timeFormatter;
+ }
+
+ KexiDB::Field* field;
+ KexiDateFormatter *dateFormatter;
+ KexiTimeFormatter *timeFormatter;
+};
+
+KexiTextFormatter::KexiTextFormatter()
+ : d( new Private )
+{
+}
+
+KexiTextFormatter::~KexiTextFormatter()
+{
+ delete d;
+}
+
+void KexiTextFormatter::setField( KexiDB::Field* field )
+{
+ d->field = field;
+ if (!d->field)
+ return;
+ if (d->field->type() == KexiDB::Field::Date || d->field->type() == KexiDB::Field::DateTime)
+ d->dateFormatter = new KexiDateFormatter();
+ else {
+ delete d->dateFormatter;
+ d->dateFormatter = 0;
+ }
+ if (d->field->type() == KexiDB::Field::Time || d->field->type() == KexiDB::Field::DateTime)
+ d->timeFormatter = new KexiTimeFormatter();
+ else {
+ delete d->timeFormatter;
+ d->timeFormatter = 0;
+ }
+}
+
+QString KexiTextFormatter::valueToText(const QVariant& value, const QString& add) const
+{
+ //cases, in order of expected frequency
+ if (!d->field || d->field->isTextType())
+ return value.toString() + add;
+ else if (d->field->isIntegerType()) {
+ if (value.toInt() == 0)
+ return add; //eat 0
+ }
+ else if (d->field->isFPNumericType()) {
+//! @todo precision!
+//! @todo support 'g' format
+ if (value.toDouble() == 0.0)
+ return add.isEmpty() ? "0" : add; //eat 0
+#if 0 //moved to KexiDB::formatNumberForVisibleDecimalPlaces()
+ QString text( QString::number(value.toDouble(), 'f',
+ QMAX(d->field->visibleDecimalPlaces(), 10)) ); //!<-- 10 is quite good maximum for fractional digits
+ //!< @todo add command line settings?
+//! @todo (js): get decimal places settings here...
+ QStringList sl = QStringList::split(".", text);
+ //nothing
+ }
+ else if (sl.count()==2) {
+// kdDebug() << "sl.count()=="<<sl.count()<< " " <<sl[0] << " | " << sl[1] << endl;
+ const QString sl1 = sl[1];
+ int pos = sl1.length()-1;
+ if (pos>=1) {
+ for (;pos>=0 && sl1[pos]=='0';pos--)
+ ;
+ pos++;
+ }
+ if (pos>0)
+ text = sl[0] + m_decsym + sl1.left(pos);
+ else
+ text = sl[0]; //no decimal point
+ }
+#endif
+ return KexiDB::formatNumberForVisibleDecimalPlaces(
+ value.toDouble(), d->field->visibleDecimalPlaces() ) + add;
+ }
+ else if (d->field->type() == KexiDB::Field::Boolean) {
+//! @todo temporary solution for booleans!
+ const bool boolValue = value.isNull() ? QVariant(add).toBool() : value.toBool();
+ return boolValue ? "1" : "0";
+ }
+ else if (d->field->type() == KexiDB::Field::Date) {
+ return d->dateFormatter->dateToString( value.toString().isEmpty() ? QDate() : value.toDate() );
+ }
+ else if (d->field->type() == KexiDB::Field::Time) {
+ return d->timeFormatter->timeToString(
+ //hack to avoid converting null variant to valid QTime(0,0,0)
+ value.toString().isEmpty() ? value.toTime() : QTime(99,0,0) );
+ }
+ else if (d->field->type() == KexiDB::Field::DateTime) {
+ if (value.toString().isEmpty() )
+ return add;
+ return d->dateFormatter->dateToString( value.toDateTime().date() ) + " " +
+ d->timeFormatter->timeToString( value.toDateTime().time() );
+ }
+ else if (d->field->type() == KexiDB::Field::BigInteger) {
+ if (value.toLongLong() == 0)
+ return add; //eat 0
+ }
+ //default: text
+ return value.toString() + add;
+}
+
+QVariant KexiTextFormatter::textToValue(const QString& text) const
+{
+ if (!d->field)
+ return QVariant();
+ const KexiDB::Field::Type t = d->field->type();
+ switch (t) {
+ case KexiDB::Field::Text:
+ case KexiDB::Field::LongText:
+ return text;
+ case KexiDB::Field::Byte:
+ case KexiDB::Field::ShortInteger:
+ return text.toShort();
+//! @todo uint, etc?
+ case KexiDB::Field::Integer:
+ return text.toInt();
+ case KexiDB::Field::BigInteger:
+ return text.toLongLong();
+ case KexiDB::Field::Boolean:
+//! @todo temporary solution for booleans!
+ return text == "1" ? QVariant(true,1) : QVariant(false,0);
+ case KexiDB::Field::Date:
+ return d->dateFormatter->stringToVariant( text );
+ case KexiDB::Field::Time:
+ return d->timeFormatter->stringToVariant( text );
+ case KexiDB::Field::DateTime:
+ return stringToDateTime(*d->dateFormatter, *d->timeFormatter, text);
+ case KexiDB::Field::Float:
+ case KexiDB::Field::Double: {
+ // replace custom decimal symbol with '.' as required by to{Float|Double}()
+ QString fixedText( text );
+ fixedText.replace(KGlobal::locale()->decimalSymbol(), ".");
+ if (t == KexiDB::Field::Double)
+ return fixedText.toDouble();
+ return fixedText.toFloat();
+ }
+ default:
+ return text;
+ }
+//! @todo more data types!
+}
+
+bool KexiTextFormatter::valueIsEmpty(const QString& text) const
+{
+ if (text.isEmpty())
+ return true;
+
+ if (d->field) {
+ const KexiDB::Field::Type t = d->field->type();
+ if (t == KexiDB::Field::Date)
+ return d->dateFormatter->isEmpty( text );
+ else if (t == KexiDB::Field::Time)
+ return d->timeFormatter->isEmpty( text );
+ else if (t == KexiDB::Field::Time)
+ return dateTimeIsEmpty( *d->dateFormatter, *d->timeFormatter, text );
+ }
+
+//! @todo
+ return text.isEmpty();
+}
+
+bool KexiTextFormatter::valueIsValid(const QString& text) const
+{
+ if (!d->field)
+ return true;
+//! @todo fix for fields with "required" property = true
+ if (valueIsEmpty(text)/*ok?*/)
+ return true;
+
+ const KexiDB::Field::Type t = d->field->type();
+ if (t == KexiDB::Field::Date)
+ return d->dateFormatter->stringToVariant( text ).isValid();
+ else if (t == KexiDB::Field::Time)
+ return d->timeFormatter->stringToVariant( text ).isValid();
+ else if (t == KexiDB::Field::DateTime)
+ return dateTimeIsValid( *d->dateFormatter, *d->timeFormatter, text );
+
+//! @todo
+ return true;
+}
+
+QString KexiTextFormatter::inputMask() const
+{
+ const KexiDB::Field::Type t = d->field->type();
+ if (t==KexiDB::Field::Date) {
+//! @todo use KDateWidget?
+ return d->dateFormatter->inputMask();
+ }
+ else if (t==KexiDB::Field::Time) {
+//! @todo use KTimeWidget
+ d->timeFormatter->inputMask();
+ }
+ else if (t==KexiDB::Field::DateTime) {
+ dateTimeInputMask( *d->dateFormatter, *d->timeFormatter );
+ }
+ return QString::null;
+}
+
diff --git a/kexi/widget/tableview/kexitextformatter.h b/kexi/widget/tableview/kexitextformatter.h
new file mode 100644
index 00000000..3ea611a4
--- /dev/null
+++ b/kexi/widget/tableview/kexitextformatter.h
@@ -0,0 +1,64 @@
+/* This file is part of the KDE project
+ Copyright (C) 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 KEXITEXTFORMATTER_H
+#define KEXITEXTFORMATTER_H
+
+#include <kexidb/field.h>
+
+//! @short Text formatter used to format QVariant values to text for displaying and back to QVariant
+/*! Used by KexiInputTableEdit, KexiDateTableEdit, KexiTimeTableEdit, KexiDateTimeTableEdit,
+ KexiDBLineEdit (forms), etc. */
+class KEXIDATATABLE_EXPORT KexiTextFormatter
+{
+ public:
+ KexiTextFormatter();
+ ~KexiTextFormatter();
+
+ //! Assigns \a field to the formatter. This affects its behaviour.
+ void setField( KexiDB::Field* field );
+
+ /*! \return text for \a value.
+ A field schema set using setField() is used to perform the formatting.
+ \a add is a text that should be added to the value if possible.
+ Used in KexiInputTableEdit::setValueInternal(), by form widgets and for reporting/printing. */
+ QString valueToText(const QVariant& value, const QString& add) const;
+
+ /*! \return value cnverted from \a text
+ A field schema set using setField() is used to perform the formatting.
+ Used in KexiInputTableEdit::setValueInternal(), by form widgets and for reporting/printing. */
+ QVariant textToValue(const QString& text) const;
+
+ /*! \return true if value formatted as \a text is empty.
+ A field schema set using setField() is used to perform the calculation. */
+ bool valueIsEmpty(const QString& text) const;
+
+ /*! \return true if value formatted as \a text is valid.
+ A field schema set using setField() is used to perform the calculation. */
+ bool valueIsValid(const QString& text) const;
+
+ /*! \return input mask for intering values related to a field schema
+ which has been set using setField(). */
+ QString inputMask() const;
+
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kexi/widget/tableview/kexitimetableedit.cpp b/kexi/widget/tableview/kexitimetableedit.cpp
new file mode 100644
index 00000000..3238c58e
--- /dev/null
+++ b/kexi/widget/tableview/kexitimetableedit.cpp
@@ -0,0 +1,158 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004,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 "kexitimetableedit.h"
+
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qvariant.h>
+#include <qrect.h>
+#include <qpalette.h>
+#include <qcolor.h>
+#include <qfontmetrics.h>
+#include <qdatetime.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qlayout.h>
+#include <qtoolbutton.h>
+#include <qdatetimeedit.h>
+#include <qclipboard.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>
+
+#include <kexiutils/utils.h>
+
+KexiTimeTableEdit::KexiTimeTableEdit(KexiTableViewColumn &column, QWidget *parent)
+ : KexiInputTableEdit(column, parent)
+{
+ setName("KexiTimeTableEdit");
+
+//! @todo add QValidator so time like "99:88:77" cannot be even entered
+
+ m_lineedit->setInputMask( m_formatter.inputMask() );
+}
+
+KexiTimeTableEdit::~KexiTimeTableEdit()
+{
+}
+
+void KexiTimeTableEdit::setValueInInternalEditor(const QVariant &value)
+{
+ if (value.isValid() && value.toTime().isValid())
+ m_lineedit->setText( m_formatter.timeToString( value.toTime() ) );
+ else
+ m_lineedit->setText( QString::null );
+}
+
+void KexiTimeTableEdit::setValueInternal(const QVariant& add_, bool removeOld)
+{
+ if (removeOld) {
+ //new time entering... just fill the line edit
+//! @todo cut string if too long..
+ QString add(add_.toString());
+ m_lineedit->setText(add);
+ m_lineedit->setCursorPosition(add.length());
+ return;
+ }
+ setValueInInternalEditor( m_origValue );
+ m_lineedit->setCursorPosition(0); //ok?
+}
+
+void KexiTimeTableEdit::setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h )
+{
+ Q_UNUSED(p);
+ Q_UNUSED(focused);
+ Q_UNUSED(x);
+ Q_UNUSED(w);
+ Q_UNUSED(h);
+#ifdef Q_WS_WIN
+ y_offset = -1;
+#else
+ y_offset = 0;
+#endif
+ if (!val.isNull() && val.canCast(QVariant::Time))
+ txt = m_formatter.timeToString(val.toTime());
+ align |= AlignLeft;
+}
+
+bool KexiTimeTableEdit::valueIsNull()
+{
+ if (m_formatter.isEmpty( m_lineedit->text() )) //empty time is null
+ return true;
+ return !timeValue().isValid();
+}
+
+bool KexiTimeTableEdit::valueIsEmpty()
+{
+ return valueIsNull();// OK? TODO (nonsense?)
+}
+
+QTime KexiTimeTableEdit::timeValue()
+{
+ return m_formatter.stringToTime( m_lineedit->text() );
+}
+
+QVariant KexiTimeTableEdit::value()
+{
+ return m_formatter.stringToVariant( m_lineedit->text() );
+}
+
+bool KexiTimeTableEdit::valueIsValid()
+{
+ if (m_formatter.isEmpty( m_lineedit->text() )) //empty time is valid
+ return true;
+ return m_formatter.stringToTime( m_lineedit->text() ).isValid();
+}
+
+void KexiTimeTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
+{
+ Q_UNUSED(visibleValue);
+ if (!value.isNull() && value.toTime().isValid())
+ qApp->clipboard()->setText( m_formatter.timeToString(value.toTime()) );
+ else
+ qApp->clipboard()->setText( QString::null );
+}
+
+void KexiTimeTableEdit::handleAction(const QString& actionName)
+{
+ const bool alreadyVisible = m_lineedit->isVisible();
+
+ if (actionName=="edit_paste") {
+ const QVariant newValue( m_formatter.stringToTime( qApp->clipboard()->text() ) );
+ if (!alreadyVisible) { //paste as the entire text if the cell was not in edit mode
+ emit editRequested();
+ m_lineedit->clear();
+ }
+ setValueInInternalEditor( newValue );
+ }
+ else
+ KexiInputTableEdit::handleAction(actionName);
+}
+
+KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiTimeEditorFactoryItem, KexiTimeTableEdit)
+
+#include "kexitimetableedit.moc"
diff --git a/kexi/widget/tableview/kexitimetableedit.h b/kexi/widget/tableview/kexitimetableedit.h
new file mode 100644
index 00000000..4daa68ec
--- /dev/null
+++ b/kexi/widget/tableview/kexitimetableedit.h
@@ -0,0 +1,64 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004,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 KEXITIMETABLEEDIT_H
+#define KEXITIMETABLEEDIT_H
+
+#include "kexiinputtableedit.h"
+#include <widget/utils/kexidatetimeformatter.h>
+
+/*! @short Editor class for Time type.
+ It is a replacement QTimeEdit due to usability problems:
+ people are accustomed to use single-character cursor.
+ Time format is retrieved from the KDE global settings
+ and input/output is performed using KLineEdit (from KexiInputTableEdit).
+*/
+class KexiTimeTableEdit : public KexiInputTableEdit
+{
+ Q_OBJECT
+
+ public:
+ KexiTimeTableEdit(KexiTableViewColumn &column, QWidget *parent=0);
+ virtual ~KexiTimeTableEdit();
+ virtual void setupContents( QPainter *p, bool focused, const QVariant& val,
+ QString &txt, int &align, int &x, int &y_offset, int &w, int &h );
+ virtual QVariant value();
+ virtual bool valueIsNull();
+ virtual bool valueIsEmpty();
+ virtual bool valueIsValid();
+
+ /*! Reimplemented after KexiInputTableEdit. */
+ virtual void handleAction(const QString& actionName);
+
+ /*! Reimplemented after KexiInputTableEdit. */
+ virtual void handleCopyAction(const QVariant& value, const QVariant& visibleValue);
+
+ protected:
+ //! helper
+ void setValueInInternalEditor(const QVariant &value);
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+ QTime timeValue();
+
+ //! Used to format and convert time values
+ KexiTimeFormatter m_formatter;
+};
+
+KEXI_DECLARE_CELLEDITOR_FACTORY_ITEM(KexiTimeEditorFactoryItem)
+
+#endif
diff --git a/kexi/widget/utils/Makefile.am b/kexi/widget/utils/Makefile.am
new file mode 100644
index 00000000..5d210f1a
--- /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 00000000..cdffcb02
--- /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 00000000..d6de6186
--- /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 00000000..407bc6fe
--- /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 00000000..da53a7e2
--- /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 00000000..727cef6f
--- /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 00000000..95258e96
--- /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 00000000..d8f642ca
--- /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 00000000..252bc535
--- /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 00000000..c7d238b1
--- /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 00000000..8790b662
--- /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 00000000..a17e5cfb
--- /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 00000000..fccbd409
--- /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 00000000..b8a8601e
--- /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 00000000..173ddad5
--- /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 00000000..0411318d
--- /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 00000000..0032e7b1
--- /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 00000000..d434fcaf
--- /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 00000000..1408f83b
--- /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 00000000..f0dff087
--- /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 00000000..674746e2
--- /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 00000000..4dbd9299
--- /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 00000000..80181bc7
--- /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 00000000..69a8b583
--- /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 00000000..cbb0931f
--- /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 00000000..1c89f96c
--- /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