KOffice – TDE office suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

kexitabledesignerview.cpp 71KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943
  1. /* This file is part of the KDE project
  2. Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Library General Public
  5. License as published by the Free Software Foundation; either
  6. version 2 of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Library General Public License for more details.
  11. You should have received a copy of the GNU Library General Public License
  12. along with this program; see the file COPYING. If not, write to
  13. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  14. * Boston, MA 02110-1301, USA.
  15. */
  16. #include "kexitabledesignerview.h"
  17. #include "kexitabledesignerview_p.h"
  18. #include "kexilookupcolumnpage.h"
  19. #include "kexitabledesignercommands.h"
  20. #include <tqlayout.h>
  21. #include <tqlabel.h>
  22. #include <tqsplitter.h>
  23. #include <kiconloader.h>
  24. #include <kdebug.h>
  25. #include <tdelocale.h>
  26. #include <tdeaction.h>
  27. #include <tdepopupmenu.h>
  28. #include <tdemessagebox.h>
  29. #include <kiconeffect.h>
  30. #include <koproperty/set.h>
  31. #include <koproperty/utils.h>
  32. #include <kexidb/cursor.h>
  33. #include <kexidb/tableschema.h>
  34. #include <kexidb/connection.h>
  35. #include <kexidb/utils.h>
  36. #include <kexidb/roweditbuffer.h>
  37. #include <kexidb/error.h>
  38. #include <kexidb/lookupfieldschema.h>
  39. #include <kexiutils/identifier.h>
  40. #include <kexiproject.h>
  41. #include <keximainwindow.h>
  42. #include <widget/tableview/kexidataawarepropertyset.h>
  43. #include <widget/kexicustompropertyfactory.h>
  44. #include <kexiutils/utils.h>
  45. #include <kexidialogbase.h>
  46. #include <kexitableview.h>
  47. //#define MAX_FIELDS 101 //nice prime number
  48. //! used only for BLOBs
  49. #define DEFAULT_OBJECT_TYPE_VALUE "image"
  50. //#define KexiTableDesignerView_DEBUG
  51. //! @todo remove this when BLOBs are implemented
  52. //#define KEXI_NO_BLOB_FIELDS
  53. using namespace KexiTableDesignerCommands;
  54. //! @internal Used in tryCastTQVariant() anf canCastTQVariant()
  55. static bool isIntegerTQVariant(TQVariant::Type t)
  56. {
  57. return t==TQVariant::LongLong
  58. || t==TQVariant::ULongLong
  59. || t==TQVariant::Int
  60. || t==TQVariant::UInt;
  61. }
  62. //! @internal Used in tryCastTQVariant()
  63. static bool canCastTQVariant(TQVariant::Type fromType, TQVariant::Type toType)
  64. {
  65. return (fromType==TQVariant::Int && toType==TQVariant::UInt)
  66. || (fromType==TQVariant::CString && toType==TQVariant::String)
  67. || (fromType==TQVariant::LongLong && toType==TQVariant::ULongLong)
  68. || ((fromType==TQVariant::String || fromType==TQVariant::CString)
  69. && (isIntegerTQVariant(toType) || toType==TQVariant::Double));
  70. }
  71. /*! @internal
  72. \return a variant value converted from \a fromVal to \a toType type.
  73. Null TQVariant is returned if \a fromVal's type and \a toType type
  74. are incompatible. */
  75. static TQVariant tryCastTQVariant( const TQVariant& fromVal, TQVariant::Type toType )
  76. {
  77. const TQVariant::Type fromType = fromVal.type();
  78. if (fromType == toType)
  79. return fromVal;
  80. if (canCastTQVariant(fromType, toType) || canCastTQVariant(toType, fromType)
  81. || (isIntegerTQVariant(fromType) && toType==TQVariant::Double))
  82. {
  83. TQVariant res( fromVal );
  84. if (res.cast(toType))
  85. return res;
  86. }
  87. return TQVariant();
  88. }
  89. KexiTableDesignerView::KexiTableDesignerView(KexiMainWindow *win, TQWidget *parent)
  90. : KexiDataTable(win, parent, "KexiTableDesignerView", false/*not db-aware*/)
  91. , KexiTableDesignerInterface()
  92. , d( new KexiTableDesignerViewPrivate(this) )
  93. {
  94. //needed for custom "identifier" property editor widget
  95. KexiCustomPropertyFactory::init();
  96. KexiDB::Connection *conn = mainWin()->project()->dbConnection();
  97. d->view = dynamic_cast<KexiTableView*>(mainWidget());
  98. d->data = new KexiTableViewData();
  99. if (conn->isReadOnly())
  100. d->data->setReadOnly(true);
  101. d->data->setInsertingEnabled( false );
  102. KexiTableViewColumn *col = new KexiTableViewColumn("pk", KexiDB::Field::Text, TQString(),
  103. i18n("Additional information about the field"));
  104. col->setIcon( KexiUtils::colorizeIconToTextColor( SmallIcon("application-vnd.tde.info"), d->view->palette() ) );
  105. col->setHeaderTextVisible(false);
  106. col->field()->setSubType("TDEIcon");
  107. col->setReadOnly(true);
  108. d->data->addColumn( col );
  109. // col = new KexiTableViewColumn("name", KexiDB::Field::Text, i18n("Field Name"),
  110. col = new KexiTableViewColumn("caption", KexiDB::Field::Text, i18n("Field Caption"),
  111. i18n("Describes caption for the field"));
  112. // KexiUtils::Validator *vd = new KexiUtils::IdentifierValidator();
  113. // vd->setAcceptsEmptyValue(true);
  114. // col->setValidator( vd );
  115. d->data->addColumn( col );
  116. col = new KexiTableViewColumn("type", KexiDB::Field::Enum, i18n("Data Type"),
  117. i18n("Describes data type for the field"));
  118. d->data->addColumn( col );
  119. #ifdef KEXI_NO_BLOB_FIELDS
  120. //! @todo remove this later
  121. TQValueVector<TQString> types(KexiDB::Field::LastTypeGroup-1); //don't show last type (BLOB)
  122. #else
  123. TQValueVector<TQString> types(KexiDB::Field::LastTypeGroup);
  124. #endif
  125. d->maxTypeNameTextWidth = 0;
  126. TQFontMetrics fm(font());
  127. for (uint i=1; i<=types.count(); i++) {
  128. types[i-1] = KexiDB::Field::typeGroupName(i);
  129. d->maxTypeNameTextWidth = TQMAX(d->maxTypeNameTextWidth, fm.width(types[i-1]));
  130. }
  131. col->field()->setEnumHints(types);
  132. d->data->addColumn( col = new KexiTableViewColumn("comments", KexiDB::Field::Text, i18n("Comments"),
  133. i18n("Describes additional comments for the field")) );
  134. d->view->setSpreadSheetMode();
  135. connect(d->data, TQT_SIGNAL(aboutToChangeCell(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*)),
  136. TQT_TQOBJECT(this), TQT_SLOT(slotBeforeCellChanged(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*)));
  137. connect(d->data, TQT_SIGNAL(rowUpdated(KexiTableItem*)),
  138. TQT_TQOBJECT(this), TQT_SLOT(slotRowUpdated(KexiTableItem*)));
  139. //connect(d->data, TQT_SIGNAL(aboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)),
  140. // TQT_TQOBJECT(this), TQT_SLOT(slotAboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)));
  141. connect(d->data, TQT_SIGNAL(aboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)),
  142. TQT_TQOBJECT(this), TQT_SLOT(slotAboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)));
  143. setMinimumSize(d->view->minimumSizeHint().width(), d->view->minimumSizeHint().height());
  144. d->view->setFocus();
  145. d->sets = new KexiDataAwarePropertySet( this, d->view );
  146. connect(d->sets, TQT_SIGNAL(rowDeleted()), TQT_TQOBJECT(this), TQT_SLOT(updateActions()));
  147. connect(d->sets, TQT_SIGNAL(rowInserted()), TQT_TQOBJECT(this), TQT_SLOT(slotRowInserted()));
  148. d->contextMenuTitle = new TDEPopupTitle(d->view->contextMenu());
  149. d->view->contextMenu()->insertItem(d->contextMenuTitle, -1, 0);
  150. connect(d->view->contextMenu(), TQT_SIGNAL(aboutToShow()), TQT_TQOBJECT(this), TQT_SLOT(slotAboutToShowContextMenu()));
  151. plugSharedAction("tablepart_toggle_pkey", TQT_TQOBJECT(this), TQT_SLOT(slotTogglePrimaryKey()));
  152. d->action_toggle_pkey = static_cast<TDEToggleAction*>( sharedAction("tablepart_toggle_pkey") );
  153. d->action_toggle_pkey->plug(d->view->contextMenu(), 1); //add at the beginning
  154. d->view->contextMenu()->insertSeparator(2);
  155. setAvailable("tablepart_toggle_pkey", !conn->isReadOnly());
  156. #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
  157. plugSharedAction("edit_undo", TQT_TQOBJECT(this), TQT_SLOT(slotUndo()));
  158. plugSharedAction("edit_redo", TQT_TQOBJECT(this), TQT_SLOT(slotRedo()));
  159. setAvailable("edit_undo", false);
  160. setAvailable("edit_redo", false);
  161. connect(d->history, TQT_SIGNAL(commandExecuted(KCommand*)), TQT_TQOBJECT(this), TQT_SLOT(slotCommandExecuted(KCommand*)));
  162. #endif
  163. #ifdef KEXI_DEBUG_GUI
  164. KexiUtils::addAlterTableActionDebug(TQString()); //to create the tab
  165. KexiUtils::connectPushButtonActionForDebugWindow(
  166. "simulateAlterTableExecution", TQT_TQOBJECT(this), TQT_SLOT(slotSimulateAlterTableExecution()));
  167. KexiUtils::connectPushButtonActionForDebugWindow(
  168. "executeRealAlterTable", TQT_TQOBJECT(this), TQT_SLOT(executeRealAlterTable()));
  169. #endif
  170. }
  171. KexiTableDesignerView::~KexiTableDesignerView()
  172. {
  173. // removeCurrentPropertySet();
  174. delete d;
  175. }
  176. void KexiTableDesignerView::initData()
  177. {
  178. //add column data
  179. // d->data->clear();
  180. d->data->deleteAllRows();
  181. int tableFieldCount = 0;
  182. d->primaryKeyExists = false;
  183. if (tempData()->table) {
  184. tableFieldCount = tempData()->table->fieldCount();
  185. //not needed d->sets->clear(tableFieldCount);
  186. //recreate table data rows
  187. for(int i=0; i < tableFieldCount; i++) {
  188. KexiDB::Field *field = tempData()->table->field(i);
  189. KexiTableItem *item = d->data->createItem(); //new KexiTableItem(0);
  190. if (field->isPrimaryKey()) {
  191. (*item)[COLUMN_ID_ICON] = "key";
  192. d->primaryKeyExists = true;
  193. }
  194. else {
  195. KexiDB::LookupFieldSchema *lookupFieldSchema
  196. = field->table() ? field->table()->lookupFieldSchema(*field) : 0;
  197. if (lookupFieldSchema && lookupFieldSchema->rowSource().type()!=KexiDB::LookupFieldSchema::RowSource::NoType
  198. && !lookupFieldSchema->rowSource().name().isEmpty())
  199. {
  200. (*item)[COLUMN_ID_ICON] = "combo";
  201. }
  202. }
  203. (*item)[COLUMN_ID_CAPTION] = field->captionOrName();
  204. (*item)[COLUMN_ID_TYPE] = field->typeGroup()-1; //-1 because type groups are counted from 1
  205. (*item)[COLUMN_ID_DESC] = field->description();
  206. d->data->append(item);
  207. //later! createPropertySet( i, field );
  208. }
  209. }
  210. // else {
  211. // d->sets->clear();//default size
  212. // }
  213. //add empty space
  214. // const int columnsCount = d->data->columnsCount();
  215. for (int i=tableFieldCount; i<(int)d->sets->size(); i++) {
  216. // KexiTableItem *item = new KexiTableItem(columnsCount);//3 empty fields
  217. d->data->append(d->data->createItem());
  218. }
  219. //set data for our spreadsheet: this will clear our sets
  220. d->view->setData(d->data);
  221. //now recreate property sets
  222. if (tempData()->table) {
  223. for(int i=0; i < tableFieldCount; i++) {
  224. KexiDB::Field *field = tempData()->table->field(i);
  225. createPropertySet( i, *field );
  226. }
  227. }
  228. //column widths
  229. d->view->setColumnWidth(COLUMN_ID_ICON, IconSize( TDEIcon::Small ) + 10);
  230. d->view->adjustColumnWidthToContents(COLUMN_ID_CAPTION); //adjust column width
  231. d->view->setColumnWidth(COLUMN_ID_TYPE, d->maxTypeNameTextWidth + 2 * d->view->rowHeight());
  232. d->view->setColumnStretchEnabled( true, COLUMN_ID_DESC ); //last column occupies the rest of the area
  233. const int minCaptionColumnWidth = d->view->fontMetrics().width("wwwwwwwwwww");
  234. if (minCaptionColumnWidth > d->view->columnWidth(COLUMN_ID_CAPTION))
  235. d->view->setColumnWidth(COLUMN_ID_CAPTION, minCaptionColumnWidth);
  236. setDirty(false);
  237. d->view->setCursorPosition(0, COLUMN_ID_CAPTION); //set @ name column
  238. propertySetSwitched();
  239. }
  240. //! Gets subtype strings and names for type \a fieldType
  241. void
  242. KexiTableDesignerView::getSubTypeListData(KexiDB::Field::TypeGroup fieldTypeGroup,
  243. TQStringList& stringsList, TQStringList& namesList)
  244. {
  245. /* disabled - "mime" is moved from subType to "objectType" custom property
  246. if (fieldTypeGroup==KexiDB::Field::BLOBGroup) {
  247. // special case: BLOB type uses "mime-based" subtypes
  248. //! @todo hardcoded!
  249. stringsList << "image";
  250. namesList << i18n("Image object type", "Image");
  251. }
  252. else {*/
  253. stringsList = KexiDB::typeStringsForGroup(fieldTypeGroup);
  254. namesList = KexiDB::typeNamesForGroup(fieldTypeGroup);
  255. // }
  256. kexipluginsdbg << "KexiTableDesignerView::getSubTypeListData(): subType strings: " <<
  257. stringsList.join("|") << "\nnames: " << namesList.join("|") << endl;
  258. }
  259. KoProperty::Set *
  260. KexiTableDesignerView::createPropertySet( int row, const KexiDB::Field& field, bool newOne )
  261. {
  262. TQString typeName = "KexiDB::Field::" + field.typeGroupString();
  263. KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
  264. if (mainWin()->project()->dbConnection()->isReadOnly())
  265. set->setReadOnly( true );
  266. // connect(buff,TQT_SIGNAL(propertyChanged(KexiPropertyBuffer&,KexiProperty&)),
  267. // TQT_TQOBJECT(this), TQT_SLOT(slotPropertyChanged(KexiPropertyBuffer&,KexiProperty&)));
  268. KoProperty::Property *prop;
  269. set->addProperty(prop = new KoProperty::Property("uid", d->generateUniqueId(), ""));
  270. prop->setVisible(false);
  271. //meta-info for property editor
  272. set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Table field")) );
  273. prop->setVisible(false);
  274. set->addProperty(prop = new KoProperty::Property("this:iconName",
  275. //! \todo add table_field icon
  276. "lineedit" //"table_field"
  277. ));
  278. prop->setVisible(false);
  279. set->addProperty(prop = new KoProperty::Property("this:useCaptionAsObjectName",
  280. TQVariant(true, 1), TQString())); //we want "caption" to be displayed in the header, not name
  281. prop->setVisible(false);
  282. //name
  283. set->addProperty(prop
  284. = new KoProperty::Property("name", TQVariant(field.name()), i18n("Name"),
  285. TQString(), KexiCustomPropertyFactory::Identifier) );
  286. //type
  287. set->addProperty( prop
  288. = new KoProperty::Property("type", TQVariant(field.type()), i18n("Type")) );
  289. #ifndef KexiTableDesignerView_DEBUG
  290. prop->setVisible(false);//always hidden
  291. #endif
  292. //subtype
  293. TQStringList typeStringList, typeNameList;
  294. getSubTypeListData(field.typeGroup(), typeStringList, typeNameList);
  295. /* disabled - "mime" is moved from subType to "objectType" custom property
  296. TQString subTypeValue;
  297. if (field.typeGroup()==KexiDB::Field::BLOBGroup) {
  298. // special case: BLOB type uses "mime-based" subtypes
  299. //! @todo this should be retrieved from KexiDB::Field when BLOB supports many different mimetypes
  300. subTypeValue = slist.first();
  301. }
  302. else {*/
  303. TQString subTypeValue = field.typeString();
  304. //}
  305. set->addProperty(prop = new KoProperty::Property("subType",
  306. typeStringList, typeNameList, subTypeValue, i18n("Subtype")));
  307. // objectType
  308. TQStringList objectTypeStringList, objectTypeNameList;
  309. //! @todo this should be retrieved from KexiDB::Field when BLOB supports many different mimetypes
  310. objectTypeStringList << "image";
  311. objectTypeNameList << i18n("Image object type", "Image");
  312. TQString objectTypeValue( field.customProperty("objectType").toString() );
  313. if (objectTypeValue.isEmpty())
  314. objectTypeValue = DEFAULT_OBJECT_TYPE_VALUE;
  315. set->addProperty(prop = new KoProperty::Property("objectType",
  316. objectTypeStringList, objectTypeNameList, objectTypeValue, i18n("Subtype")/*todo other i18n string?*/));
  317. set->addProperty( prop
  318. = new KoProperty::Property("caption", TQVariant(field.caption()), i18n("Caption") ) );
  319. prop->setVisible(false);//always hidden
  320. set->addProperty( prop
  321. = new KoProperty::Property("description", TQVariant(field.description())) );
  322. prop->setVisible(false);//always hidden
  323. set->addProperty(prop
  324. = new KoProperty::Property("unsigned", TQVariant(field.isUnsigned(), 4), i18n("Unsigned Number")));
  325. set->addProperty( prop
  326. = new KoProperty::Property("length", (int)field.length()/*200?*/, i18n("Length")));
  327. set->addProperty( prop
  328. = new KoProperty::Property("precision", (int)field.precision()/*200?*/, i18n("Precision")));
  329. #ifdef KEXI_NO_UNFINISHED
  330. prop->setVisible(false);
  331. #endif
  332. set->addProperty( prop
  333. = new KoProperty::Property("visibleDecimalPlaces", field.visibleDecimalPlaces(), i18n("Visible Decimal Places")));
  334. prop->setOption("min", -1);
  335. prop->setOption("minValueText", i18n("Auto Decimal Places","Auto"));
  336. //! @todo set reasonable default for column width
  337. set->addProperty( prop
  338. = new KoProperty::Property("width", (int)field.width()/*200?*/, i18n("Column Width")));
  339. #ifdef KEXI_NO_UNFINISHED
  340. prop->setVisible(false);
  341. #endif
  342. set->addProperty( prop
  343. = new KoProperty::Property("defaultValue", field.defaultValue(), i18n("Default Value"),
  344. TQString(),
  345. //! @todo use "Variant" type here when supported by KoProperty
  346. (KoProperty::PropertyType)field.variantType()) );
  347. prop->setOption("3rdState", i18n("None"));
  348. // prop->setVisible(false);
  349. set->addProperty( prop
  350. = new KoProperty::Property("primaryKey", TQVariant(field.isPrimaryKey(), 4), i18n("Primary Key")));
  351. prop->setIcon("key");
  352. set->addProperty( prop
  353. = new KoProperty::Property("unique", TQVariant(field.isUniqueKey(), 4), i18n("Unique")));
  354. set->addProperty( prop
  355. = new KoProperty::Property("notNull", TQVariant(field.isNotNull(), 4), i18n("Required")));
  356. set->addProperty( prop
  357. = new KoProperty::Property("allowEmpty", TQVariant(!field.isNotEmpty(), 4), i18n("Allow Zero\nSize")));
  358. set->addProperty( prop
  359. = new KoProperty::Property("autoIncrement", TQVariant(field.isAutoIncrement(), 4), i18n("Autonumber")));
  360. prop->setIcon("autonumber");
  361. set->addProperty( prop
  362. = new KoProperty::Property("indexed", TQVariant(field.isIndexed(), 4), i18n("Indexed")));
  363. //- properties related to lookup columns (used and set by the "lookup column" tab in the property pane)
  364. KexiDB::LookupFieldSchema *lookupFieldSchema = field.table() ? field.table()->lookupFieldSchema(field) : 0;
  365. set->addProperty( prop = new KoProperty::Property("rowSource",
  366. lookupFieldSchema ? lookupFieldSchema->rowSource().name() : TQString(), i18n("Row Source")));
  367. prop->setVisible(false);
  368. set->addProperty( prop = new KoProperty::Property("rowSourceType",
  369. lookupFieldSchema ? lookupFieldSchema->rowSource().typeName() : TQString(), i18n("Row Source\nType")));
  370. prop->setVisible(false);
  371. set->addProperty( prop
  372. = new KoProperty::Property("boundColumn",
  373. lookupFieldSchema ? lookupFieldSchema->boundColumn() : -1, i18n("Bound Column")));
  374. prop->setVisible(false);
  375. //! @todo this is backward-compatible code for "single visible column" implementation
  376. //! for multiple columns, only the first is displayed, so there is a data loss is GUI is used
  377. //! -- special koproperty editor needed
  378. int visibleColumn = -1;
  379. if (lookupFieldSchema && !lookupFieldSchema->visibleColumns().isEmpty())
  380. visibleColumn = lookupFieldSchema->visibleColumns().first();
  381. set->addProperty( prop
  382. = new KoProperty::Property("visibleColumn", visibleColumn, i18n("Visible Column")));
  383. prop->setVisible(false);
  384. //! @todo support columnWidths(), columnHeadersVisible(), maximumListRows(), limitToList(), displayWidget()
  385. //----
  386. d->updatePropertiesVisibility(field.type(), *set);
  387. connect(set, TQT_SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
  388. TQT_TQOBJECT(this), TQT_SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
  389. d->sets->insert(row, set, newOne);
  390. return set;
  391. }
  392. void KexiTableDesignerView::updateActions(bool activated)
  393. {
  394. Q_UNUSED(activated);
  395. /*! \todo check if we can set pkey for this column type (eg. BLOB?) */
  396. setAvailable("tablepart_toggle_pkey", propertySet()!=0 && !mainWin()->project()->dbConnection()->isReadOnly());
  397. if (!propertySet())
  398. return;
  399. KoProperty::Set &set = *propertySet();
  400. d->slotTogglePrimaryKeyCalled = true;
  401. d->action_toggle_pkey->setChecked(set["primaryKey"].value().toBool());
  402. d->slotTogglePrimaryKeyCalled = false;
  403. }
  404. void KexiTableDesignerView::slotUpdateRowActions(int row)
  405. {
  406. KexiDataTable::slotUpdateRowActions(row);
  407. updateActions();
  408. }
  409. void KexiTableDesignerView::slotTogglePrimaryKey()
  410. {
  411. if (d->slotTogglePrimaryKeyCalled)
  412. return;
  413. d->slotTogglePrimaryKeyCalled = true;
  414. if (!propertySet())
  415. return;
  416. KoProperty::Set &set = *propertySet();
  417. bool isSet = !set["primaryKey"].value().toBool();
  418. set.changeProperty("primaryKey", TQVariant(isSet,1)); //this will update all related properties as well
  419. /* CommandGroup *setPrimaryKeyCommand;
  420. if (isSet) {
  421. setPrimaryKeyCommand = new CommandGroup(i18n("Set primary key for field \"%1\"")
  422. .arg(set["name"].value().toString()) );
  423. }
  424. else {
  425. setPrimaryKeyCommand = new CommandGroup(i18n("Unset primary key for field \"%1\"")
  426. .arg(set["name"].value().toString()) );
  427. }
  428. switchPrimaryKey(set, isSet, false, setPrimaryKeyCommand);*/
  429. //addHistoryCommand( setPrimaryKeyCommand, false /* !execute */ );
  430. d->slotTogglePrimaryKeyCalled = false;
  431. }
  432. void KexiTableDesignerView::switchPrimaryKey(KoProperty::Set &propertySet,
  433. bool set, bool aWasPKey, CommandGroup* commandGroup)
  434. {
  435. const bool was_pkey = aWasPKey || propertySet["primaryKey"].value().toBool();
  436. // propertySet["primaryKey"] = TQVariant(set, 1);
  437. d->setPropertyValueIfNeeded( propertySet, "primaryKey", TQVariant(set,1), commandGroup );
  438. if (&propertySet==this->propertySet()) {
  439. //update action and icon @ column 0 (only if we're changing current property set)
  440. d->action_toggle_pkey->setChecked(set);
  441. if (d->view->selectedItem()) {
  442. //show key in the table
  443. d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
  444. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_ICON,
  445. TQVariant(set ? "key" : ""));
  446. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem(), true);
  447. }
  448. if (was_pkey || set) //change flag only if we're setting pk or really clearing it
  449. d->primaryKeyExists = set;
  450. }
  451. if (set) {
  452. //primary key is set, remove old pkey if exists
  453. KoProperty::Set *s = 0;
  454. int i;
  455. const int count = (int)d->sets->size();
  456. for (i=0; i<count; i++) {
  457. s = d->sets->at(i);
  458. if (s && s!=&propertySet && (*s)["primaryKey"].value().toBool() && i!=d->view->currentRow())
  459. break;
  460. }
  461. if (i<count) {//remove
  462. //(*s)["autoIncrement"] = TQVariant(false, 0);
  463. d->setPropertyValueIfNeeded( *s, "autoIncrement", TQVariant(false,0), commandGroup );
  464. //(*s)["primaryKey"] = TQVariant(false, 0);
  465. d->setPropertyValueIfNeeded( *s, "primaryKey", TQVariant(false,0), commandGroup );
  466. //remove key from table
  467. d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
  468. KexiTableItem *item = d->view->itemAt(i);
  469. if (item) {
  470. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_ICON, TQVariant());
  471. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item, true);
  472. }
  473. }
  474. //set unsigned big-integer type
  475. // d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem());
  476. d->slotBeforeCellChanged_enabled = false;
  477. d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
  478. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
  479. TQVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
  480. // TQVariant(KexiDB::Field::typeGroupName(KexiDB::Field::IntegerGroup)));
  481. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem(), true);
  482. //propertySet["subType"] = KexiDB::Field::typeString(KexiDB::Field::BigInteger);
  483. d->setPropertyValueIfNeeded( propertySet, "subType", KexiDB::Field::typeString(KexiDB::Field::BigInteger),
  484. commandGroup );
  485. //propertySet["unsigned"] = TQVariant(true,4);
  486. d->setPropertyValueIfNeeded( propertySet, "unsigned", TQVariant(true,4), commandGroup );
  487. /*todo*/
  488. d->slotBeforeCellChanged_enabled = true;
  489. }
  490. updateActions();
  491. }
  492. /*void KexiTableDesignerView::slotCellSelected(int, int row)
  493. {
  494. kdDebug() << "KexiTableDesignerView::slotCellSelected()" << endl;
  495. if(row == m_row)
  496. return;
  497. m_row = row;
  498. propertyBufferSwitched();
  499. }*/
  500. tristate KexiTableDesignerView::beforeSwitchTo(int mode, bool &dontStore)
  501. {
  502. if (!d->view->acceptRowEdit())
  503. return false;
  504. /* if (mode==Kexi::DesignViewMode) {
  505. initData();
  506. return true;
  507. }
  508. else */
  509. tristate res = true;
  510. if (mode==Kexi::DataViewMode) {
  511. if (!dirty() && parentDialog()->neverSaved()) {
  512. KMessageBox::sorry(this, i18n("Cannot switch to data view, because table design is empty.\n"
  513. "First, please create your design.") );
  514. return cancelled;
  515. }
  516. //<temporary>
  517. else if (dirty() && !parentDialog()->neverSaved()) {
  518. // cancelled = (KMessageBox::No == KMessageBox::questionYesNo(this, i18n("Saving changes for existing table design is not yet supported.\nDo you want to discard your changes now?")));
  519. // KexiDB::Connection *conn = mainWin()->project()->dbConnection();
  520. bool emptyTable;
  521. int r = KMessageBox::warningYesNoCancel(this,
  522. i18n("Saving changes for existing table design is now required.")
  523. + "\n" + d->messageForSavingChanges(emptyTable, /* skip warning? */!isPhysicalAlteringNeeded()),
  524. TQString(),
  525. KStdGuiItem::save(), KStdGuiItem::discard(), TQString(),
  526. KMessageBox::Notify|KMessageBox::Dangerous);
  527. if (r == KMessageBox::Cancel)
  528. res = cancelled;
  529. else
  530. res = true;
  531. dontStore = (r!=KMessageBox::Yes);
  532. if (!dontStore)
  533. d->dontAskOnStoreData = true;
  534. // if (dontStore)
  535. // setDirty(false);
  536. }
  537. //</temporary>
  538. //todo
  539. return res;
  540. }
  541. else if (mode==Kexi::TextViewMode) {
  542. //todo
  543. }
  544. return res;
  545. }
  546. tristate KexiTableDesignerView::afterSwitchFrom(int mode)
  547. {
  548. if (mode==Kexi::NoViewMode || mode==Kexi::DataViewMode) {
  549. initData();
  550. }
  551. return true;
  552. }
  553. KoProperty::Set *KexiTableDesignerView::propertySet()
  554. {
  555. return d->sets ? d->sets->currentPropertySet() : 0;
  556. }
  557. /*
  558. void KexiTableDesignerView::removeCurrentPropertySet()
  559. {
  560. const int r = d->view->currentRow();
  561. KoProperty::Set *buf = d->sets.at(r);
  562. if (!buf)
  563. return;
  564. buf->debug();
  565. // m_currentBufferCleared = true;
  566. d->sets.remove(r);
  567. propertysetswitched();
  568. // delete buf;
  569. // m_currentBufferCleared = false;
  570. }
  571. */
  572. void KexiTableDesignerView::slotBeforeCellChanged(
  573. KexiTableItem *item, int colnum, TQVariant& newValue, KexiDB::ResultInfo* /*result*/)
  574. {
  575. if (!d->slotBeforeCellChanged_enabled)
  576. return;
  577. // kdDebug() << d->view->selectedItem() << " " << item
  578. //<< " " << d->sets->at( d->view->currentRow() ) << " " << propertySet() << endl;
  579. if (colnum==COLUMN_ID_CAPTION) {//'caption'
  580. // if (!item->at(1).toString().isEmpty() && item->at(1).isNull()) {
  581. //if 'type' is not filled yet
  582. if (item->at(COLUMN_ID_TYPE).isNull()) {
  583. //auto select 1st row of 'type' column
  584. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, TQVariant((int)0));
  585. }
  586. KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
  587. if (propertySetForItem) {
  588. d->addHistoryCommand_in_slotPropertyChanged_enabled = false; //because we'll add the two changes as one KMacroCommand
  589. TQString oldName( propertySetForItem->property("name").value().toString() );
  590. TQString oldCaption( propertySetForItem->property("caption").value().toString() );
  591. //we need to create the action now as set["name"] will be changed soon..
  592. ChangeFieldPropertyCommand *changeCaptionCommand
  593. = new ChangeFieldPropertyCommand( this, *propertySetForItem, "caption", oldCaption, newValue);
  594. //update field caption and name
  595. propertySetForItem->changeProperty("caption", newValue);
  596. propertySetForItem->changeProperty("name", newValue); // "name" prop. is of custom type Identifier, so this assignment
  597. // will automatically convert newValue to an valid identifier
  598. //remember this action containing 2 subactions
  599. CommandGroup *changeCaptionAndNameCommand = new CommandGroup(
  600. i18n("Change \"%1\" field's name to \"%2\" and caption from \"%3\" to \"%4\"")
  601. .arg(oldName).arg(propertySetForItem->property("name").value().toString())
  602. .arg(oldCaption).arg(newValue.toString() ));
  603. changeCaptionAndNameCommand->addCommand( changeCaptionCommand );
  604. // new ChangeFieldPropertyCommand( this, *propertySetForItem,
  605. // "caption", oldCaption, newValue)
  606. // );
  607. changeCaptionAndNameCommand->addCommand(
  608. new ChangeFieldPropertyCommand( this, *propertySetForItem,
  609. "name", oldName, propertySetForItem->property("name").value().toString())
  610. );
  611. addHistoryCommand( changeCaptionAndNameCommand, false /* !execute */ );
  612. d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
  613. }
  614. }
  615. else if (colnum==COLUMN_ID_TYPE) {//'type'
  616. if (newValue.isNull()) {
  617. //'type' col will be cleared: clear all other columns as well
  618. d->slotBeforeCellChanged_enabled = false;
  619. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_ICON, TQVariant());
  620. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, TQVariant(TQString()));
  621. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_DESC, TQVariant());
  622. d->slotBeforeCellChanged_enabled = true;
  623. return;
  624. }
  625. KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
  626. if (!propertySetForItem)
  627. return;
  628. KoProperty::Set &set = *propertySetForItem; //propertySet();
  629. //'type' col is changed (existed before)
  630. //-get type group number
  631. KexiDB::Field::TypeGroup fieldTypeGroup;
  632. int i_fieldTypeGroup = newValue.toInt()+1/*counting from 1*/;
  633. if (i_fieldTypeGroup < 1 || i_fieldTypeGroup >
  634. #ifdef KEXI_NO_BLOB_FIELDS
  635. //! @todo remove this later
  636. (int)KexiDB::Field::LastTypeGroup-1) //don't show last (BLOB) type
  637. #else
  638. (int)KexiDB::Field::LastTypeGroup)
  639. #endif
  640. return;
  641. fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(i_fieldTypeGroup);
  642. //-get 1st type from this group, and update 'type' property
  643. KexiDB::Field::Type fieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
  644. if (fieldType==KexiDB::Field::InvalidType)
  645. fieldType = KexiDB::Field::Text;
  646. //moved down set["type"] = (int)fieldType;
  647. // set["subType"] = KexiDB::Field::typeName(fieldType);
  648. //-get subtypes for this type: keys (slist) and names (nlist)
  649. TQStringList slist, nlist;
  650. getSubTypeListData(fieldTypeGroup, slist, nlist);
  651. TQString subTypeValue;
  652. /* disabled - "mime" is moved from subType to "objectType" custom property
  653. if (fieldType==KexiDB::Field::BLOB) {
  654. // special case: BLOB type uses "mime-based" subtypes
  655. subTypeValue = slist.first();
  656. }
  657. else {*/
  658. subTypeValue = KexiDB::Field::typeString(fieldType);
  659. //}
  660. KoProperty::Property *subTypeProperty = &set["subType"];
  661. kexipluginsdbg << subTypeProperty->value() << endl;
  662. // *** this action contains subactions ***
  663. CommandGroup *changeDataTypeCommand = new CommandGroup(
  664. i18n("Change data type for field \"%1\" to \"%2\"")
  665. .arg(set["name"].value().toString()).arg( KexiDB::Field::typeName( fieldType ) ) );
  666. //kexipluginsdbg << "++++++++++" << slist << nlist << endl;
  667. //update subtype list and value
  668. const bool forcePropertySetReload
  669. = KexiDB::Field::typeGroup( KexiDB::Field::typeForString(subTypeProperty->value().toString()) )
  670. != fieldTypeGroup; //<-- ?????
  671. // const bool forcePropertySetReload = set["type"].value().toInt() != (int)fieldTypeGroup;
  672. const bool useListData = slist.count() > 1; //disabled-> || fieldType==KexiDB::Field::BLOB;
  673. if (!useListData) {
  674. slist.clear(); //empty list will be passed
  675. nlist.clear();
  676. }
  677. d->setPropertyValueIfNeeded( set, "type", (int)fieldType, changeDataTypeCommand,
  678. false /*!forceAddCommand*/, true /*rememberOldValue*/);
  679. // notNull and defaultValue=false is reasonable for boolean type
  680. if (fieldType == KexiDB::Field::Boolean) {
  681. //! @todo maybe this is good for other data types as well?
  682. d->setPropertyValueIfNeeded( set, "notNull", TQVariant(true, 1), changeDataTypeCommand,
  683. false /*!forceAddCommand*/, false /*!rememberOldValue*/);
  684. d->setPropertyValueIfNeeded( set, "defaultValue", TQVariant(false, 1), changeDataTypeCommand,
  685. false /*!forceAddCommand*/, false /*!rememberOldValue*/);
  686. }
  687. /* if (useListData) {
  688. {
  689. subTypeProperty->setListData( slist, nlist );
  690. }
  691. else {
  692. subTypeProperty->setListData( 0 );
  693. }*/
  694. if (set["primaryKey"].value().toBool()==true) {
  695. //primary keys require big int, so if selected type is not integer- remove PK
  696. if (fieldTypeGroup != KexiDB::Field::IntegerGroup) {
  697. /*not needed, line below will do the work
  698. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_ICON, TQVariant());
  699. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item); */
  700. //set["primaryKey"] = TQVariant(false, 1);
  701. d->setPropertyValueIfNeeded( set, "primaryKey", TQVariant(false, 1), changeDataTypeCommand );
  702. //! @todo should we display (passive?) dialog informing about cleared pkey?
  703. }
  704. }
  705. // if (useListData)
  706. // subTypeProperty->setValue( subTypeValue, false/*!rememberOldValue*/ );
  707. d->setPropertyValueIfNeeded( set, "subType", subTypeValue,
  708. changeDataTypeCommand, false, false /*!rememberOldValue*/,
  709. &slist, &nlist );
  710. if (d->updatePropertiesVisibility(fieldType, set, changeDataTypeCommand) || forcePropertySetReload) {
  711. //properties' visiblility changed: refresh prop. set
  712. propertySetReloaded(true);
  713. }
  714. addHistoryCommand( changeDataTypeCommand, false /* !execute */ );
  715. }
  716. else if (colnum==COLUMN_ID_DESC) {//'description'
  717. KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
  718. if (!propertySetForItem)
  719. return;
  720. //update field desc.
  721. TQVariant oldValue((*propertySetForItem)["description"].value());
  722. kexipluginsdbg << oldValue << endl;
  723. propertySetForItem->changeProperty("description", newValue);
  724. /*moved addHistoryCommand(
  725. new ChangeFieldPropertyCommand( this, *propertySetForItem,
  726. "description", oldValue, newValue ), false);*/
  727. }
  728. }
  729. void KexiTableDesignerView::slotRowUpdated(KexiTableItem *item)
  730. {
  731. const int row = d->view->KexiDataAwareObjectInterface::data()->findRef(item);
  732. if (row < 0)
  733. return;
  734. setDirty();
  735. //-check if the row was empty before updating
  736. //if yes: we want to add a property set for this new row (field)
  737. TQString fieldCaption( item->at(COLUMN_ID_CAPTION).toString() );
  738. const bool prop_set_allowed = !item->at(COLUMN_ID_TYPE).isNull();
  739. if (!prop_set_allowed && d->sets->at(row)/*propertySet()*/) {
  740. //there is a property set, but it's not allowed - remove it:
  741. d->sets->remove( row ); //d->sets->removeCurrentPropertySet();
  742. //clear 'type' column:
  743. d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
  744. // d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE, TQVariant());
  745. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, TQVariant());
  746. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
  747. } else if (prop_set_allowed && !d->sets->at(row)/*propertySet()*/) {
  748. //-- create a new field:
  749. KexiDB::Field::TypeGroup fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(
  750. item->at(COLUMN_ID_TYPE).toInt()+1/*counting from 1*/ );
  751. int intFieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
  752. if (intFieldType==0)
  753. return;
  754. TQString description( item->at(COLUMN_ID_DESC).toString() );
  755. //todo: check uniqueness:
  756. TQString fieldName( KexiUtils::string2Identifier(fieldCaption) );
  757. KexiDB::Field::Type fieldType = KexiDB::intToFieldType( intFieldType );
  758. KexiDB::Field field( //tmp
  759. fieldName,
  760. fieldType,
  761. KexiDB::Field::NoConstraints,
  762. KexiDB::Field::NoOptions,
  763. /*length*/0,
  764. /*precision*/0,
  765. /*defaultValue*/TQVariant(),
  766. fieldCaption,
  767. description,
  768. /*width*/0);
  769. // m_newTable->addField( field );
  770. // reasonable case for boolean type: set notNull flag and "false" as default value
  771. if (fieldType == KexiDB::Field::Boolean) {
  772. field.setNotNull( true );
  773. field.setDefaultValue( TQVariant(false, 0) );
  774. }
  775. kexipluginsdbg << "KexiTableDesignerView::slotRowUpdated(): " << field.debugString() << endl;
  776. //create a new property set:
  777. KoProperty::Set *newSet = createPropertySet( row, field, true );
  778. //moved
  779. //add a special property indicating that this is brand new buffer,
  780. //not just changed
  781. // KoProperty::Property* prop = new KoProperty::Property("newrow", TQVariant());
  782. // prop->setVisible(false);
  783. // newbuff->addProperty( prop );
  784. //refresh property editor:
  785. propertySetSwitched();
  786. if (row>=0) {
  787. if (d->addHistoryCommand_in_slotRowUpdated_enabled) {
  788. addHistoryCommand( new InsertFieldCommand( this, row, *newSet /*propertySet()*/ ), //, field /*will be copied*/
  789. false /* !execute */ );
  790. }
  791. }
  792. else {
  793. kexipluginswarn << "KexiTableDesignerView::slotRowUpdated() row # not found !" << endl;
  794. }
  795. }
  796. }
  797. void KexiTableDesignerView::updateActions()
  798. {
  799. updateActions(false);
  800. }
  801. void KexiTableDesignerView::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
  802. {
  803. // if (!d->slotPropertyChanged_enabled)
  804. // return;
  805. const TQCString pname = property.name();
  806. kexipluginsdbg << "KexiTableDesignerView::slotPropertyChanged(): " << pname << " = " << property.value()
  807. << " (oldvalue = " << property.oldValue() << ")" << endl;
  808. // true is PK should be altered
  809. bool changePrimaryKey = false;
  810. // true is PK should be set to true, otherwise unset
  811. bool setPrimaryKey = false;
  812. if (pname=="primaryKey" && d->slotPropertyChanged_primaryKey_enabled) {
  813. changePrimaryKey = true;
  814. setPrimaryKey = property.value().toBool();
  815. }
  816. // update "lookup column" icon
  817. if (pname=="rowSource" || pname=="rowSourceType") {
  818. //! @todo indicate invalid definitions of lookup columns as well using a special icon
  819. //! (e.g. due to missing data source)
  820. const int row = d->sets->findRowForPropertyValue("uid", set["uid"].value().toInt());
  821. KexiTableItem *item = d->view->itemAt(row);
  822. if (item)
  823. d->updateIconForItem(*item, set);
  824. }
  825. //setting autonumber requires setting PK as well
  826. CommandGroup *setAutonumberCommand = 0;
  827. CommandGroup *toplevelCommand = 0;
  828. if (pname=="autoIncrement" && property.value().toBool()==true) {
  829. if (set["primaryKey"].value().toBool()==false) {//we need PKEY here!
  830. TQString msg = TQString("<p>")
  831. +i18n("Setting autonumber requires primary key to be set for current field.")+"</p>";
  832. if (d->primaryKeyExists)
  833. msg += (TQString("<p>")+ i18n("Previous primary key will be removed.")+"</p>");
  834. msg += (TQString("<p>")
  835. +i18n("Do you want to create primary key for current field? "
  836. "Click \"Cancel\" to cancel setting autonumber.")+"</p>");
  837. if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
  838. i18n("Setting Autonumber Field"),
  839. KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
  840. {
  841. changePrimaryKey = true;
  842. setPrimaryKey = true;
  843. //switchPrimaryKey(set, true);
  844. // this will be toplevel command
  845. setAutonumberCommand = new CommandGroup(
  846. i18n("Assign autonumber for field \"%1\"").arg(set["name"].value().toString()) );
  847. toplevelCommand = setAutonumberCommand;
  848. d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(true,1), setAutonumberCommand );
  849. }
  850. else {
  851. setAutonumberCommand = new CommandGroup(
  852. i18n("Remove autonumber from field \"%1\"").arg(set["name"].value().toString()) );
  853. //d->slotPropertyChanged_enabled = false;
  854. // set["autoIncrement"].setValue( TQVariant(false,1), false/*don't save old*/);
  855. // d->slotPropertyChanged_enabled = true;
  856. d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(false,1), setAutonumberCommand,
  857. true /*forceAddCommand*/, false/*rememberOldValue*/ );
  858. addHistoryCommand( setAutonumberCommand, false /* !execute */ );
  859. return;
  860. }
  861. }
  862. }
  863. //clear PK when these properties were set to false:
  864. if ((pname=="indexed" || pname=="unique" || pname=="notNull")
  865. && set["primaryKey"].value().toBool() && property.value().toBool()==false)
  866. {
  867. //! @todo perhaps show a hint in help panel telling what happens?
  868. changePrimaryKey = true;
  869. setPrimaryKey = false;
  870. // this will be toplevel command
  871. CommandGroup *unsetIndexedOrUniquOrNotNullCommand = new CommandGroup(
  872. i18n("Set \"%1\" property for field \"%2\"").arg(property.caption()).arg(set["name"].value().toString()) );
  873. toplevelCommand = unsetIndexedOrUniquOrNotNullCommand;
  874. d->setPropertyValueIfNeeded( set, pname, TQVariant(false,1), unsetIndexedOrUniquOrNotNullCommand );
  875. if (pname=="notNull") {
  876. //? d->setPropertyValueIfNeeded( set, "notNull", TQVariant(true,1), unsetIndexedOrUniquOrNotNullCommand );
  877. d->setPropertyValueIfNeeded( set, "unique", TQVariant(false,1), unsetIndexedOrUniquOrNotNullCommand );
  878. }
  879. }
  880. if (pname=="defaultValue") {
  881. KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
  882. set["defaultValue"].setType((KoProperty::PropertyType)KexiDB::Field::variantType(type));
  883. }
  884. if (pname=="subType" && d->slotPropertyChanged_subType_enabled) {
  885. d->slotPropertyChanged_subType_enabled = false;
  886. if (set["primaryKey"].value().toBool()==true
  887. && property.value().toString()!=KexiDB::Field::typeString(KexiDB::Field::BigInteger))
  888. {
  889. kexipluginsdbg << "INVALID " << property.value().toString() << endl;
  890. // if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
  891. // i18n("This field has promary key assigned. Setting autonumber field"),
  892. // KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
  893. }
  894. KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
  895. TQString typeName;
  896. /* disabled - "mime" is moved from subType to "objectType" custom property
  897. if (type==KexiDB::Field::BLOB) { //special case
  898. //find i18n'd text
  899. TQStringList stringsList, namesList;
  900. getSubTypeListData(KexiDB::Field::BLOBGroup, stringsList, namesList);
  901. const int stringIndex = stringsList.findIndex( property.value().toString() );
  902. if (-1 == stringIndex || stringIndex>=(int)namesList.count())
  903. typeName = property.value().toString(); //for sanity
  904. else
  905. typeName = namesList[stringIndex];
  906. }
  907. else {*/
  908. typeName = KexiDB::Field::typeName( KexiDB::Field::typeForString(property.value().toString()) );
  909. // }
  910. // kdDebug() << property.value().toString() << endl;
  911. // kdDebug() << set["type"].value() << endl;
  912. // if (KexiDB::Field::typeGroup( set["type"].value().toInt() ) == (int)KexiDB::Field::TextGroup) {
  913. CommandGroup* changeFieldTypeCommand = new CommandGroup(
  914. i18n("Change type for field \"%1\" to \"%2\"").arg(set["name"].value().toString())
  915. .arg(typeName) );
  916. d->setPropertyValueIfNeeded( set, "subType", property.value(), property.oldValue(),
  917. changeFieldTypeCommand );
  918. kexipluginsdbg << set["type"].value() << endl;
  919. const KexiDB::Field::Type newType = KexiDB::Field::typeForString(property.value().toString());
  920. set["type"].setValue( newType );
  921. // cast "defaultValue" property value to a new type
  922. TQVariant oldDefVal( set["defaultValue"].value() );
  923. TQVariant newDefVal( tryCastTQVariant(oldDefVal, KexiDB::Field::variantType(type)) );
  924. if (oldDefVal.type()!=newDefVal.type())
  925. set["defaultValue"].setType( newDefVal.type() );
  926. d->setPropertyValueIfNeeded( set, "defaultValue", newDefVal, newDefVal,
  927. changeFieldTypeCommand );
  928. d->updatePropertiesVisibility(newType, set);
  929. //properties' visiblility changed: refresh prop. set
  930. propertySetReloaded(true);
  931. d->slotPropertyChanged_subType_enabled = true;
  932. addHistoryCommand( changeFieldTypeCommand, false /* !execute */ );
  933. return;
  934. // }
  935. // d->slotPropertyChanged_subType_enabled = true;
  936. // return;
  937. }
  938. if (d->addHistoryCommand_in_slotPropertyChanged_enabled && !changePrimaryKey/*we'll add multiple commands for PK*/) {
  939. addHistoryCommand( new ChangeFieldPropertyCommand(this, set,
  940. property.name(), property.oldValue() /* ??? */, property.value()),
  941. false /* !execute */ );
  942. }
  943. if (changePrimaryKey) {
  944. d->slotPropertyChanged_primaryKey_enabled = false;
  945. if (setPrimaryKey) {
  946. //primary key implies some rules
  947. //const bool prev_addHistoryCommand_in_slotPropertyChanged_enabled = d->addHistoryCommand_in_slotPropertyChanged_enabled;
  948. // d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
  949. //this action contains subactions
  950. CommandGroup *setPrimaryKeyCommand = new CommandGroup(
  951. i18n("Set primary key for field \"%1\"")
  952. .arg(set["name"].value().toString()) );
  953. if (toplevelCommand)
  954. toplevelCommand->addCommand( setPrimaryKeyCommand );
  955. else
  956. toplevelCommand = setPrimaryKeyCommand;
  957. d->setPropertyValueIfNeeded( set, "primaryKey", TQVariant(true,1), setPrimaryKeyCommand, true /*forceAddCommand*/ );
  958. d->setPropertyValueIfNeeded( set, "unique", TQVariant(true,1), setPrimaryKeyCommand );
  959. d->setPropertyValueIfNeeded( set, "notNull", TQVariant(true,1), setPrimaryKeyCommand );
  960. d->setPropertyValueIfNeeded( set, "allowEmpty", TQVariant(false,1), setPrimaryKeyCommand );
  961. d->setPropertyValueIfNeeded( set, "indexed", TQVariant(true,1), setPrimaryKeyCommand );
  962. //! \todo: add setting for this: "Integer PKeys have autonumber set by default"
  963. d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(true,1), setPrimaryKeyCommand );
  964. /* set["unique"] = TQVariant(true,1);
  965. set["notNull"] = TQVariant(true,1);
  966. set["allowEmpty"] = TQVariant(false,1);
  967. set["indexed"] = TQVariant(true,1);
  968. set["autoIncrement"] = TQVariant(true,1);*/
  969. // d->addHistoryCommand_in_slotPropertyChanged_enabled = prev_addHistoryCommand_in_slotPropertyChanged_enabled;
  970. //down addHistoryCommand( toplevelCommand, false /* !execute */ );
  971. }
  972. else {//! set PK to false
  973. //remember this action containing 2 subactions
  974. CommandGroup *setPrimaryKeyCommand = new CommandGroup(
  975. i18n("Unset primary key for field \"%1\"")
  976. .arg(set["name"].value().toString()) );
  977. if (toplevelCommand)
  978. toplevelCommand->addCommand( setPrimaryKeyCommand );
  979. else
  980. toplevelCommand = setPrimaryKeyCommand;
  981. d->setPropertyValueIfNeeded( set, "primaryKey", TQVariant(false,1), setPrimaryKeyCommand, true /*forceAddCommand*/ );
  982. d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(false,1), setPrimaryKeyCommand );
  983. // set["autoIncrement"] = TQVariant(false,1);
  984. //down addHistoryCommand( toplevelCommand, false /* !execute */ );
  985. }
  986. switchPrimaryKey(set, setPrimaryKey, true/*wasPKey*/, toplevelCommand);
  987. d->updatePropertiesVisibility(
  988. KexiDB::Field::typeForString( set["subType"].value().toString() ), set, toplevelCommand);
  989. addHistoryCommand( toplevelCommand, false /* !execute */ );
  990. //properties' visiblility changed: refresh prop. set
  991. propertySetReloaded(true/*preservePrevSelection*/);
  992. d->slotPropertyChanged_primaryKey_enabled = true;
  993. }
  994. }
  995. void KexiTableDesignerView::slotRowInserted()
  996. {
  997. updateActions();
  998. if (d->addHistoryCommand_in_slotRowInserted_enabled) {
  999. const int row = d->view->currentRow();
  1000. if (row>=0) {
  1001. addHistoryCommand( new InsertEmptyRowCommand( this, row ), false /* !execute */ );
  1002. }
  1003. }
  1004. //TODO?
  1005. }
  1006. void KexiTableDesignerView::slotAboutToDeleteRow(
  1007. KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint)
  1008. {
  1009. Q_UNUSED(result)
  1010. Q_UNUSED(repaint)
  1011. if (item[COLUMN_ID_ICON].toString()=="key")
  1012. d->primaryKeyExists = false;
  1013. if (d->addHistoryCommand_in_slotAboutToDeleteRow_enabled) {
  1014. const int row = d->view->KexiDataAwareObjectInterface::data()->findRef(&item);
  1015. KoProperty::Set *set = row >=0 ? d->sets->at(row) : 0;
  1016. //set can be 0 here, what means "removing empty row"
  1017. addHistoryCommand(
  1018. new RemoveFieldCommand( this, row, set ),
  1019. false /* !execute */
  1020. );
  1021. }
  1022. }
  1023. KexiDB::Field * KexiTableDesignerView::buildField( const KoProperty::Set &set ) const
  1024. {
  1025. //create a map of property values
  1026. kexipluginsdbg << set["type"].value() << endl;
  1027. TQMap<TQCString, TQVariant> values = KoProperty::propertyValues(set);
  1028. //remove internal values, to avoid creating custom field's properties
  1029. TQMap<TQCString, TQVariant>::Iterator it = values.begin();
  1030. KexiDB::Field *field = new KexiDB::Field();
  1031. while (it!=values.end()) {
  1032. const TQString propName( it.key() );
  1033. if (d->internalPropertyNames.find(propName.latin1()) || propName.startsWith("this:")
  1034. || (/*sanity*/propName=="objectType" && KexiDB::Field::BLOB != KexiDB::intToFieldType( set["type"].value().toInt() )))
  1035. {
  1036. TQMap<TQCString, TQVariant>::Iterator it_tmp = it;
  1037. ++it;
  1038. values.remove(it_tmp);
  1039. }
  1040. else
  1041. ++it;
  1042. }
  1043. //assign properties to the field
  1044. // (note that "objectType" property will be saved as custom property)
  1045. if (!KexiDB::setFieldProperties( *field, values )) {
  1046. delete field;
  1047. return 0;
  1048. }
  1049. return field;
  1050. }
  1051. tristate KexiTableDesignerView::buildSchema(KexiDB::TableSchema &schema, bool beSilent)
  1052. {
  1053. if (!d->view->acceptRowEdit())
  1054. return cancelled;
  1055. tristate res = true;
  1056. //check for pkey; automatically add a pkey if user wanted
  1057. if (!d->primaryKeyExists) {
  1058. if (beSilent) {
  1059. kexipluginsdbg << "KexiTableDesignerView::buildSchema(): no primay key defined..." << endl;
  1060. }
  1061. else {
  1062. const int questionRes = KMessageBox::questionYesNoCancel(this,
  1063. i18n("<p>Table \"%1\" has no <b>primary key</b> defined.</p>"
  1064. "<p>Although a primary key is not required, it is needed "
  1065. "for creating relations between database tables. "
  1066. "Do you want to add primary key automatically now?</p>"
  1067. "<p>If you want to add a primary key by hand, press \"Cancel\" "
  1068. "to cancel saving table design.</p>").arg(schema.name()),
  1069. TQString(), KGuiItem(i18n("&Add Primary Key"), "key"), KStdGuiItem::no(),
  1070. "autogeneratePrimaryKeysOnTableDesignSaving");
  1071. if (questionRes==KMessageBox::Cancel) {
  1072. return cancelled;
  1073. }
  1074. else if (questionRes==KMessageBox::Yes) {
  1075. //-find unique name, starting with, "id", "id2", ....
  1076. int i=0;
  1077. int idIndex = 1; //means "id"
  1078. TQString pkFieldName("id%1");
  1079. TQString pkFieldCaption(i18n("Identifier%1", "Id%1"));
  1080. while (i<(int)d->sets->size()) {
  1081. KoProperty::Set *set = d->sets->at(i);
  1082. if (set) {
  1083. if ((*set)["name"].value().toString()
  1084. == pkFieldName.arg(idIndex==1?TQString() : TQString::number(idIndex))
  1085. || (*set)["caption"].value().toString()
  1086. == pkFieldCaption.arg(idIndex==1?TQString() : TQString::number(idIndex)))
  1087. {
  1088. //try next id index
  1089. i = 0;
  1090. idIndex++;
  1091. continue;
  1092. }
  1093. }
  1094. i++;
  1095. }
  1096. pkFieldName = pkFieldName.arg(idIndex==1?TQString() : TQString::number(idIndex));
  1097. pkFieldCaption = pkFieldCaption.arg(idIndex==1?TQString() : TQString::number(idIndex));
  1098. //ok, add PK with such unique name
  1099. d->view->insertEmptyRow(0);
  1100. d->view->setCursorPosition(0, COLUMN_ID_CAPTION);
  1101. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_CAPTION,
  1102. TQVariant(pkFieldCaption));
  1103. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
  1104. TQVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
  1105. if (!d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem(), true)) {
  1106. return cancelled;
  1107. }
  1108. slotTogglePrimaryKey();
  1109. }
  1110. }
  1111. }
  1112. //check for duplicates
  1113. KoProperty::Set *b = 0;
  1114. bool no_fields = true;
  1115. int i;
  1116. TQDict<char> names(1009, false);
  1117. char dummy;
  1118. for (i=0;i<(int)d->sets->size();i++) {
  1119. b = d->sets->at(i);
  1120. if (b) {
  1121. no_fields = false;
  1122. const TQString name = (*b)["name"].value().toString();
  1123. if (name.isEmpty()) {
  1124. if (beSilent) {
  1125. kexipluginswarn <<
  1126. TQString("KexiTableDesignerView::buildSchema(): no field caption entered at row %1...")
  1127. .arg(i+1) << endl;
  1128. }
  1129. else {
  1130. d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
  1131. d->view->startEditCurrentCell();
  1132. KMessageBox::information(this, i18n("You should enter field caption.") );
  1133. }
  1134. res = cancelled;
  1135. break;
  1136. }
  1137. if (names[name]) {
  1138. break;
  1139. }
  1140. names.insert( name, &dummy ); //remember
  1141. }
  1142. }
  1143. if (res == true && no_fields) {//no fields added
  1144. if (beSilent) {
  1145. kexipluginswarn <<
  1146. "KexiTableDesignerView::buildSchema(): no field defined..." << endl;
  1147. }
  1148. else {
  1149. KMessageBox::sorry(this,
  1150. i18n("You have added no fields.\nEvery table should have at least one field.") );
  1151. }
  1152. res = cancelled;
  1153. }
  1154. if (res == true && b && i<(int)d->sets->size()) {//found a duplicate
  1155. if (beSilent) {
  1156. kexipluginswarn <<
  1157. TQString("KexiTableDesignerView::buildSchema(): duplicated field name '%1'")
  1158. .arg((*b)["name"].value().toString()) << endl;
  1159. }
  1160. else {
  1161. d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
  1162. d->view->startEditCurrentCell();
  1163. //! @todo for "names hidden" mode we won't get this error because user is unable to change names
  1164. KMessageBox::sorry(this,
  1165. i18n("You have added \"%1\" field name twice.\nField names cannot be repeated. "
  1166. "Correct name of the field.")
  1167. .arg((*b)["name"].value().toString()) );
  1168. }
  1169. res = cancelled;
  1170. }
  1171. if (res == true) {
  1172. //for every field, create KexiDB::Field definition
  1173. for (i=0;i<(int)d->sets->size();i++) {
  1174. KoProperty::Set *s = d->sets->at(i);
  1175. if (!s)
  1176. continue;
  1177. KexiDB::Field * f = buildField( *s );
  1178. if (!f)
  1179. continue; //hmm?
  1180. schema.addField(f);
  1181. if (!(*s)["rowSource"].value().toString().isEmpty() && !(*s)["rowSourceType"].value().toString().isEmpty()) {
  1182. //add lookup column
  1183. KexiDB::LookupFieldSchema *lookupFieldSchema = new KexiDB::LookupFieldSchema();
  1184. lookupFieldSchema->rowSource().setTypeByName( (*s)["rowSourceType"].value().toString() );
  1185. lookupFieldSchema->rowSource().setName( (*s)["rowSource"].value().toString() );
  1186. lookupFieldSchema->setBoundColumn( (*s)["boundColumn"].value().toInt() );
  1187. //! @todo this is backward-compatible code for "single visible column" implementation
  1188. //! for multiple columns, only the first is displayed, so there is a data loss is GUI is used
  1189. //! -- special koproperty editor needed
  1190. TQValueList<uint> visibleColumns;
  1191. const int visibleColumn = (*s)["visibleColumn"].value().toInt();
  1192. if (visibleColumn >= 0)
  1193. visibleColumns.append( (uint)visibleColumn );
  1194. lookupFieldSchema->setVisibleColumns( visibleColumns );
  1195. //! @todo support columnWidths(), columnHeadersVisible(), maximumListRows(), limitToList(), displayWidget()
  1196. if (!schema.setLookupFieldSchema(f->name(), lookupFieldSchema)) {
  1197. kexipluginswarn <<
  1198. "KexiTableDesignerView::buildSchema(): !schema.setLookupFieldSchema()" << endl;
  1199. delete lookupFieldSchema;
  1200. return false;
  1201. }
  1202. }
  1203. }
  1204. }
  1205. return res;
  1206. }
  1207. //! @internal
  1208. //! A recursive function for copying alter table actions from undo/redo commands.
  1209. static void copyAlterTableActions(KCommand* command, KexiDB::AlterTableHandler::ActionList &actions)
  1210. {
  1211. CommandGroup* cmdGroup = dynamic_cast<CommandGroup*>( command );
  1212. if (cmdGroup) {//command group: flatten it
  1213. for (TQPtrListIterator<KCommand> it(cmdGroup->commands()); it.current(); ++it)
  1214. copyAlterTableActions(it.current(), actions);
  1215. return;
  1216. }
  1217. Command* cmd = dynamic_cast<Command*>( command );
  1218. if (!cmd) {
  1219. kexipluginswarn << "KexiTableDesignerView::copyAlterTableActions(): cmd is not of type 'Command'!" << endl;
  1220. return;
  1221. }
  1222. KexiDB::AlterTableHandler::ActionBase* action = cmd->createAction();
  1223. //some commands can contain null actions, e.g. "set visibility" command
  1224. if (action)
  1225. actions.append( action );
  1226. }
  1227. tristate KexiTableDesignerView::buildAlterTableActions(KexiDB::AlterTableHandler::ActionList &actions)
  1228. {
  1229. actions.clear();
  1230. kexipluginsdbg << "KexiTableDesignerView::buildAlterTableActions(): " << d->history->commands().count()
  1231. << " top-level command(s) to process..." << endl;
  1232. for (TQPtrListIterator<KCommand> it(d->history->commands()); it.current(); ++it) {
  1233. copyAlterTableActions(it.current(), actions);
  1234. }
  1235. return true;
  1236. }
  1237. KexiDB::SchemaData* KexiTableDesignerView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
  1238. {
  1239. if (tempData()->table || m_dialog->schemaData()) //must not be
  1240. return 0;
  1241. //create table schema definition
  1242. tempData()->table = new KexiDB::TableSchema(sdata.name());
  1243. tempData()->table->setName( sdata.name() );
  1244. tempData()->table->setCaption( sdata.caption() );
  1245. tempData()->table->setDescription( sdata.description() );
  1246. tristate res = buildSchema(*tempData()->table);
  1247. cancel = ~res;
  1248. //FINALLY: create table:
  1249. if (res == true) {
  1250. //todo
  1251. KexiDB::Connection *conn = mainWin()->project()->dbConnection();
  1252. res = conn->createTable(tempData()->table);
  1253. if (res!=true)
  1254. parentDialog()->setStatus(conn, "");
  1255. }
  1256. if (res == true) {
  1257. //we've current schema
  1258. tempData()->tableSchemaChangedInPreviousView = true;
  1259. //not needed; KexiProject emits newItemStored signal //let project know the table is created
  1260. // mainWin()->project()->emitTableCreated(*tempData()->table);
  1261. }
  1262. else {
  1263. delete tempData()->table;
  1264. tempData()->table = 0;
  1265. }
  1266. return tempData()->table;
  1267. }
  1268. tristate KexiTableDesignerView::storeData(bool dontAsk)
  1269. {
  1270. if (!tempData()->table || !m_dialog->schemaData()) {
  1271. d->recentResultOfStoreData = false;
  1272. return false;
  1273. }
  1274. KexiDB::Connection *conn = mainWin()->project()->dbConnection();
  1275. KexiDB::AlterTableHandler *alterTableHandler = 0;
  1276. KexiDB::TableSchema *newTable = 0;
  1277. //- create action list for the alter table handler
  1278. KexiDB::AlterTableHandler::ActionList actions;
  1279. tristate res = buildAlterTableActions( actions );
  1280. bool realAlterTableCanBeUsed = false; //!< @todo this is temporary flag before we switch entirely to real alter table
  1281. if (res == true) {
  1282. alterTableHandler = new KexiDB::AlterTableHandler( *conn );
  1283. alterTableHandler->setActions(actions);
  1284. if (!d->tempStoreDataUsingRealAlterTable) {
  1285. //only compute requirements
  1286. KexiDB::AlterTableHandler::ExecutionArguments args;
  1287. args.onlyComputeRequirements = true;
  1288. (void)alterTableHandler->execute(tempData()->table->name(), args);
  1289. res = args.result;
  1290. if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
  1291. realAlterTableCanBeUsed = true;
  1292. }
  1293. }
  1294. if (res == true) {
  1295. res = KexiTablePart::askForClosingObjectsUsingTableSchema(
  1296. this, *conn, *tempData()->table,
  1297. i18n("You are about to change the design of table \"%1\" "
  1298. "but following objects using this table are opened:")
  1299. .arg(tempData()->table->name()));
  1300. }
  1301. if (res == true) {
  1302. if (!d->tempStoreDataUsingRealAlterTable && !realAlterTableCanBeUsed) {
  1303. //! @todo temp; remove this case:
  1304. delete alterTableHandler;
  1305. alterTableHandler = 0;
  1306. // - inform about removing the current table and ask for confirmation
  1307. if (!d->dontAskOnStoreData && !dontAsk) {
  1308. bool emptyTable;
  1309. const TQString msg = d->messageForSavingChanges(emptyTable);
  1310. if (!emptyTable) {
  1311. if (KMessageBox::No == KMessageBox::questionYesNo(this, msg))
  1312. res = cancelled;
  1313. }
  1314. }
  1315. d->dontAskOnStoreData = false; //one-time use
  1316. if (~res) {
  1317. d->recentResultOfStoreData = res;
  1318. return res;
  1319. }
  1320. // keep old behaviour:
  1321. newTable = new KexiDB::TableSchema();
  1322. // copy the schema data
  1323. static_cast<KexiDB::SchemaData&>(*newTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
  1324. res = buildSchema(*newTable);
  1325. kexipluginsdbg << "KexiTableDesignerView::storeData() : BUILD SCHEMA:" << endl;
  1326. newTable->debug();
  1327. res = conn->alterTable(*tempData()->table, *newTable);
  1328. if (res != true)
  1329. parentDialog()->setStatus(conn, "");
  1330. }
  1331. else {
  1332. KexiDB::AlterTableHandler::ExecutionArguments args;
  1333. newTable = alterTableHandler->execute(tempData()->table->name(), args);
  1334. res = args.result;
  1335. kexipluginsdbg << "KexiTableDesignerView::storeData() : ALTER TABLE EXECUTE: "
  1336. << res.toString() << endl;
  1337. if (true != res) {
  1338. alterTableHandler->debugError();
  1339. parentDialog()->setStatus(alterTableHandler, "");
  1340. }
  1341. }
  1342. }
  1343. if (res == true) {
  1344. //change current schema
  1345. tempData()->table = newTable;
  1346. tempData()->tableSchemaChangedInPreviousView = true;
  1347. d->history->clear();
  1348. }
  1349. else {
  1350. delete newTable;
  1351. }
  1352. delete alterTableHandler;
  1353. d->recentResultOfStoreData = res;
  1354. return res;
  1355. }
  1356. tristate KexiTableDesignerView::simulateAlterTableExecution(TQString *debugTarget)
  1357. {
  1358. #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
  1359. # ifdef KEXI_DEBUG_GUI
  1360. if (mainWin()->activeWindow() != parentDialog()) //to avoid executing for multiple alter table views
  1361. return false;
  1362. if (!tempData()->table || !m_dialog->schemaData())
  1363. return false;
  1364. KexiDB::Connection *conn = mainWin()->project()->dbConnection();
  1365. KexiDB::AlterTableHandler::ActionList actions;
  1366. tristate res = buildAlterTableActions( actions );
  1367. //todo: result?
  1368. KexiDB::AlterTableHandler alterTableHandler( *conn );
  1369. alterTableHandler.setActions(actions);
  1370. KexiDB::AlterTableHandler::ExecutionArguments args;
  1371. if (debugTarget) {
  1372. args.debugString = debugTarget;
  1373. }
  1374. else {
  1375. args.simulate = true;
  1376. }
  1377. (void)alterTableHandler.execute(tempData()->table->name(), args);
  1378. return args.result;
  1379. # else
  1380. return false;
  1381. # endif
  1382. #else
  1383. return false;
  1384. #endif
  1385. }
  1386. void KexiTableDesignerView::slotSimulateAlterTableExecution()
  1387. {
  1388. (void)simulateAlterTableExecution(0);
  1389. }
  1390. tristate KexiTableDesignerView::executeRealAlterTable()
  1391. {
  1392. TQSignal signal;
  1393. signal.connect( mainWin(), TQT_SLOT(slotProjectSave()) );
  1394. d->tempStoreDataUsingRealAlterTable = true;
  1395. d->recentResultOfStoreData = false;
  1396. signal.activate(); //will call KexiMainWindowImpl::slotProjectSaveAs() and thus storeData()
  1397. d->tempStoreDataUsingRealAlterTable = false;
  1398. return d->recentResultOfStoreData;
  1399. }
  1400. KexiTablePart::TempData* KexiTableDesignerView::tempData() const
  1401. {
  1402. return static_cast<KexiTablePart::TempData*>(parentDialog()->tempData());
  1403. }
  1404. /*void KexiTableDesignerView::slotAboutToUpdateRow(
  1405. KexiTableItem* item, KexiDB::RowEditBuffer* buffer, KexiDB::ResultInfo* result)
  1406. {
  1407. KexiDB::RowEditBuffer::SimpleMap map = buffer->simpleBuffer();
  1408. buffer->debug();
  1409. TQVariant old_type = item->at(1);
  1410. TQVariant *buf_type = buffer->at( d->view->field(1)->name() );
  1411. //check if there is a type specified
  1412. // if ((old_type.isNull() && !buf_type) || (buf_type && buf_type->isNull())) {
  1413. //kdDebug() << "err" << endl;
  1414. //}
  1415. // allow = true;
  1416. // m_dirty = m_dirty | result->success;
  1417. }*/
  1418. #ifdef KEXI_DEBUG_GUI
  1419. void KexiTableDesignerView::debugCommand( KCommand* command, int nestingLevel )
  1420. {
  1421. if (dynamic_cast<Command*>(command))
  1422. KexiUtils::addAlterTableActionDebug(dynamic_cast<Command*>(command)->debugString(), nestingLevel);
  1423. else
  1424. KexiUtils::addAlterTableActionDebug(command->name(), nestingLevel);
  1425. //show subcommands
  1426. if (dynamic_cast<CommandGroup*>(command)) {
  1427. for (TQPtrListIterator<KCommand> it(dynamic_cast<CommandGroup*>(command)->commands()); it.current(); ++it) {
  1428. debugCommand(it.current(), nestingLevel + 1);
  1429. }
  1430. }
  1431. }
  1432. #endif
  1433. void KexiTableDesignerView::addHistoryCommand( KCommand* command, bool execute )
  1434. {
  1435. #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
  1436. # ifdef KEXI_DEBUG_GUI
  1437. debugCommand( command, 0 );
  1438. # endif
  1439. d->history->addCommand( command, execute );
  1440. updateUndoRedoActions();
  1441. #endif
  1442. }
  1443. void KexiTableDesignerView::updateUndoRedoActions()
  1444. {
  1445. #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
  1446. setAvailable("edit_undo", d->historyActionCollection->action("edit_undo")->isEnabled());
  1447. setAvailable("edit_redo", d->historyActionCollection->action("edit_redo")->isEnabled());
  1448. #endif
  1449. }
  1450. void KexiTableDesignerView::slotUndo()
  1451. {
  1452. #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
  1453. # ifdef KEXI_DEBUG_GUI
  1454. KexiUtils::addAlterTableActionDebug(TQString("UNDO:"));
  1455. # endif
  1456. d->history->undo();
  1457. updateUndoRedoActions();
  1458. #endif
  1459. }
  1460. void KexiTableDesignerView::slotRedo()
  1461. {
  1462. #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
  1463. # ifdef KEXI_DEBUG_GUI
  1464. KexiUtils::addAlterTableActionDebug(TQString("REDO:"));
  1465. # endif
  1466. d->history->redo();
  1467. updateUndoRedoActions();
  1468. #endif
  1469. }
  1470. void KexiTableDesignerView::slotCommandExecuted(KCommand *command)
  1471. {
  1472. #ifdef KEXI_DEBUG_GUI
  1473. debugCommand( command, 1 );
  1474. #endif
  1475. }
  1476. void KexiTableDesignerView::slotAboutToShowContextMenu()
  1477. {
  1478. //update title
  1479. if (propertySet()) {
  1480. const KoProperty::Set &set = *propertySet();
  1481. TQString captionOrName(set["caption"].value().toString());
  1482. if (captionOrName.isEmpty())
  1483. captionOrName = set["name"].value().toString();
  1484. //! @todo show "field" icon
  1485. d->contextMenuTitle->setTitle( i18n("Table field \"%1\"").arg(captionOrName) );
  1486. }
  1487. else {
  1488. d->contextMenuTitle->setTitle( i18n("Empty table row", "Empty Row") );
  1489. }
  1490. }
  1491. TQString KexiTableDesignerView::debugStringForCurrentTableSchema(tristate& result)
  1492. {
  1493. KexiDB::TableSchema tempTable;
  1494. //copy schema data
  1495. static_cast<KexiDB::SchemaData&>(tempTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
  1496. result = buildSchema(tempTable, true /*beSilent*/);
  1497. if (true!=result)
  1498. return TQString();
  1499. return tempTable.debugString(false /*without name*/);
  1500. }
  1501. // -- low-level actions used by undo/redo framework
  1502. void KexiTableDesignerView::clearRow(int row, bool addCommand)
  1503. {
  1504. if (!d->view->acceptRowEdit())
  1505. return;
  1506. KexiTableItem *item = d->view->itemAt(row);
  1507. if (!item)
  1508. return;
  1509. //remove from prop. set
  1510. d->sets->remove( row );
  1511. //clear row in table view (just clear value in COLUMN_ID_TYPE column)
  1512. // for (int i=0; i < (int)d->view->KexiDataAwareObjectInterface::data()->columnsCount(); i++) {
  1513. if (!addCommand) {
  1514. d->addHistoryCommand_in_slotRowUpdated_enabled = false;
  1515. d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
  1516. d->slotBeforeCellChanged_enabled = false;
  1517. }
  1518. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, TQVariant());
  1519. if (!addCommand) {
  1520. d->addHistoryCommand_in_slotRowUpdated_enabled = true;
  1521. d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
  1522. d->slotBeforeCellChanged_enabled = true;
  1523. }
  1524. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item, true);
  1525. }
  1526. void KexiTableDesignerView::insertField(int row, const TQString& caption, bool addCommand)
  1527. {
  1528. insertFieldInternal(row, 0, caption, addCommand);
  1529. }
  1530. void KexiTableDesignerView::insertField(int row, KoProperty::Set& set, bool addCommand)
  1531. {
  1532. insertFieldInternal(row, &set, TQString(), addCommand);
  1533. }
  1534. void KexiTableDesignerView::insertFieldInternal(int row, KoProperty::Set* set, //const KexiDB::Field& field,
  1535. const TQString& caption, bool addCommand)
  1536. {
  1537. if (set && (!set->contains("type") || !set->contains("caption"))) {
  1538. kexipluginswarn << "KexiTableDesignerView::insertField(): no 'type' or 'caption' property in set!" << endl;
  1539. return;
  1540. }
  1541. if (!d->view->acceptRowEdit())
  1542. return;
  1543. KexiTableItem *item = d->view->itemAt(row);
  1544. if (!item)
  1545. return;
  1546. if (!addCommand) {
  1547. d->addHistoryCommand_in_slotRowUpdated_enabled = false;
  1548. d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
  1549. d->slotBeforeCellChanged_enabled = false;
  1550. }
  1551. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION,
  1552. set ? (*set)["caption"].value() : TQVariant(caption));//field.caption());
  1553. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE,
  1554. set ? (int)KexiDB::Field::typeGroup( (*set)["type"].value().toInt() )-1/*counting from 0*/
  1555. : (((int)KexiDB::Field::TextGroup)-1)/*default type, counting from 0*/
  1556. );
  1557. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_DESC,
  1558. set ? (*set)["description"].value() : TQVariant());//field.description());
  1559. if (!addCommand) {
  1560. d->slotBeforeCellChanged_enabled = true;
  1561. }
  1562. //this will create a new property set:
  1563. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
  1564. if (set) {
  1565. KoProperty::Set *newSet = d->sets->at(row);
  1566. if (newSet) {
  1567. *newSet = *set; //deep copy
  1568. }
  1569. else {
  1570. kexipluginswarn << "KexiTableDesignerView::insertField() !newSet, row==" << row << endl;
  1571. }
  1572. }
  1573. if (!addCommand) {
  1574. d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
  1575. d->addHistoryCommand_in_slotRowUpdated_enabled = true;
  1576. }
  1577. d->view->updateRow( row );
  1578. propertySetReloaded(true);
  1579. }
  1580. void KexiTableDesignerView::insertEmptyRow( int row, bool addCommand )
  1581. {
  1582. if (!addCommand) {
  1583. d->addHistoryCommand_in_slotRowInserted_enabled = false;
  1584. }
  1585. d->view->insertEmptyRow( row );
  1586. if (!addCommand) {
  1587. d->addHistoryCommand_in_slotRowInserted_enabled = true;
  1588. }
  1589. }
  1590. /*void KexiTableDesignerView::deleteRow( int row )
  1591. {
  1592. d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
  1593. d->view->deleteItem( d->view->KexiDataAwareObjectInterface::data()->at(row) );
  1594. d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
  1595. }*/
  1596. void KexiTableDesignerView::deleteRow( int row, bool addCommand )
  1597. {
  1598. KexiTableItem *item = d->view->itemAt( row );
  1599. if (!item)
  1600. return;
  1601. if (!addCommand) {
  1602. d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
  1603. }
  1604. const bool res = d->view->deleteItem(item);
  1605. if (!addCommand) {
  1606. d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
  1607. }
  1608. if (!res)
  1609. return;
  1610. }
  1611. void KexiTableDesignerView::changeFieldPropertyForRow( int row,
  1612. const TQCString& propertyName, const TQVariant& newValue,
  1613. KoProperty::Property::ListData* const listData, bool addCommand )
  1614. {
  1615. #ifdef KEXI_DEBUG_GUI
  1616. KexiUtils::addAlterTableActionDebug(TQString("** changeFieldProperty: \"")
  1617. + TQString(propertyName) + "\" to \"" + newValue.toString() + "\"", 2/*nestingLevel*/);
  1618. #endif
  1619. if (!d->view->acceptRowEdit())
  1620. return;
  1621. KoProperty::Set* set = d->sets->at( row );
  1622. if (!set || !set->contains(propertyName))
  1623. return;
  1624. KoProperty::Property &property = set->property(propertyName);
  1625. if (listData) {
  1626. if (listData->keys.isEmpty())
  1627. property.setListData( 0 );
  1628. else
  1629. property.setListData( new KoProperty::Property::ListData(*listData) );
  1630. }
  1631. if (propertyName != "type") //delayed type update (we need to have subtype set properly)
  1632. property.setValue(newValue);
  1633. KexiTableItem *item = d->view->itemAt(row);
  1634. Q_ASSERT(item);
  1635. if (propertyName == "type") {
  1636. // d->addHistoryCommand_in_slotRowUpdated_enabled = false;
  1637. // d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
  1638. d->slotPropertyChanged_subType_enabled = false;
  1639. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE,
  1640. int( KexiDB::Field::typeGroup( newValue.toInt() ) )-1);
  1641. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
  1642. d->addHistoryCommand_in_slotRowUpdated_enabled = true;
  1643. // d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
  1644. // d->slotPropertyChanged_subType_enabled = true;
  1645. property.setValue(newValue); //delayed type update (we needed to have subtype set properly)
  1646. }
  1647. if (!addCommand) {
  1648. d->addHistoryCommand_in_slotRowUpdated_enabled = false;
  1649. d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
  1650. d->slotPropertyChanged_subType_enabled = false;
  1651. }
  1652. //special cases: properties displayed within the data grid:
  1653. if (propertyName == "caption") {
  1654. if (!addCommand) {
  1655. d->slotBeforeCellChanged_enabled = false;
  1656. }
  1657. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, newValue);
  1658. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
  1659. if (!addCommand) {
  1660. d->slotBeforeCellChanged_enabled = true;
  1661. }
  1662. }
  1663. else if (propertyName == "description") {
  1664. if (!addCommand) {
  1665. d->slotBeforeCellChanged_enabled = false;
  1666. }
  1667. d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_DESC, newValue);
  1668. if (!addCommand) {
  1669. d->slotBeforeCellChanged_enabled = true;
  1670. }
  1671. d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
  1672. }
  1673. if (!addCommand) {
  1674. d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
  1675. d->addHistoryCommand_in_slotRowUpdated_enabled = true;
  1676. d->slotPropertyChanged_subType_enabled = true;
  1677. }
  1678. d->view->updateRow( row );
  1679. }
  1680. void KexiTableDesignerView::changeFieldProperty( int fieldUID,
  1681. const TQCString& propertyName, const TQVariant& newValue,
  1682. KoProperty::Property::ListData* const listData, bool addCommand )
  1683. {
  1684. //find a property by UID
  1685. const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
  1686. if (row<0) {
  1687. kexipluginswarn << "KexiTableDesignerView::changeFieldProperty(): field with uid="<<fieldUID<<" not found!"<<endl;
  1688. return;
  1689. }
  1690. changeFieldPropertyForRow(row, propertyName, newValue, listData, addCommand);
  1691. }
  1692. void KexiTableDesignerView::changePropertyVisibility(
  1693. int fieldUID, const TQCString& propertyName, bool visible )
  1694. {
  1695. #ifdef KEXI_DEBUG_GUI
  1696. KexiUtils::addAlterTableActionDebug(TQString("** changePropertyVisibility: \"")
  1697. + TQString(propertyName) + "\" to \"" + (visible ? "true" : "false") + "\"", 2/*nestingLevel*/);
  1698. #endif
  1699. if (!d->view->acceptRowEdit())
  1700. return;
  1701. //find a property by name
  1702. const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
  1703. if (row<0)
  1704. return;
  1705. KoProperty::Set* set = d->sets->at( row );
  1706. if (!set || !set->contains(propertyName))
  1707. return;
  1708. KoProperty::Property &property = set->property(propertyName);
  1709. if (property.isVisible() != visible) {
  1710. property.setVisible(visible);
  1711. propertySetReloaded(true);
  1712. }
  1713. }
  1714. void KexiTableDesignerView::propertySetSwitched()
  1715. {
  1716. KexiDataTable::propertySetSwitched();
  1717. //if (parentDialog()!=parentDialog()->mainWin()->currentDialog())
  1718. // return; //this is not the current dialog's view
  1719. static_cast<KexiTablePart*>(parentDialog()->part())->lookupColumnPage()
  1720. ->assignPropertySet(propertySet());
  1721. }
  1722. bool KexiTableDesignerView::isPhysicalAlteringNeeded()
  1723. {
  1724. //- create action list for the alter table handler
  1725. KexiDB::AlterTableHandler::ActionList actions;
  1726. tristate res = buildAlterTableActions( actions );
  1727. if (res != true)
  1728. return true;
  1729. KexiDB::Connection *conn = mainWin()->project()->dbConnection();
  1730. KexiDB::AlterTableHandler *alterTableHandler = new KexiDB::AlterTableHandler( *conn );
  1731. alterTableHandler->setActions(actions);
  1732. //only compute requirements
  1733. KexiDB::AlterTableHandler::ExecutionArguments args;
  1734. args.onlyComputeRequirements = true;
  1735. (void)alterTableHandler->execute(tempData()->table->name(), args);
  1736. res = args.result;
  1737. delete alterTableHandler;
  1738. if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
  1739. return false;
  1740. return true;
  1741. }
  1742. #include "kexitabledesignerview.moc"