1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/dbgrid.cpp
3 // Purpose: Displays a wxDbTable in a wxGrid.
4 // Author: Roger Gammans, Paul Gammans
8 // Copyright: (c) 1999 The Computer Surgery (roger@computer-surgery.co.uk)
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11 // Branched From : dbgrid.cpp,v 1.18 2000/12/19 13:00:58
12 ///////////////////////////////////////////////////////////////////////////////
14 #include "wx/wxprec.h"
20 #if wxUSE_ODBC && wxUSE_GRID
23 #include "wx/textctrl.h"
28 #include "wx/generic/gridctrl.h"
29 #include "wx/dbgrid.h"
31 // DLL options compatibility check:
32 WX_CHECK_BUILD_OPTIONS("wxDbGrid")
35 wxDbGridCellAttrProvider::wxDbGridCellAttrProvider()
41 wxDbGridCellAttrProvider::wxDbGridCellAttrProvider(wxDbTable
*tab
, wxDbGridColInfoBase
* ColInfo
)
47 wxDbGridCellAttrProvider::~wxDbGridCellAttrProvider()
51 wxGridCellAttr
*wxDbGridCellAttrProvider::GetAttr(int row
, int col
,
52 wxGridCellAttr::wxAttrKind kind
) const
54 wxGridCellAttr
*attr
= wxGridCellAttrProvider::GetAttr(row
,col
,kind
);
56 if (m_data
&& m_ColInfo
&& (m_data
->GetNumberOfColumns() > m_ColInfo
[col
].DbCol
))
58 //FIXME: this test could.
59 // ??::InsertPending == m_data->get_ModifiedStatus()
60 // and if InsertPending use colDef[].InsertAllowed
61 if (!(m_data
->GetColDefs()[(m_ColInfo
[col
].DbCol
)].Updateable
))
65 case (wxGridCellAttr::Any
):
68 attr
= new wxGridCellAttr
;
69 // Store so we don't keep creating / deleting this...
70 wxDbGridCellAttrProvider
* self
= wxConstCast(this, wxDbGridCellAttrProvider
) ;
72 self
->SetColAttr(attr
, col
);
77 //We now must check what we were returned. and do the right thing (tm)
78 wxGridCellAttr::wxAttrKind attrkind
= attr
->GetKind();
79 if ((attrkind
== (wxGridCellAttr::Default
)) || (attrkind
== (wxGridCellAttr::Cell
)) ||
80 (attrkind
== (wxGridCellAttr::Col
)))
82 wxGridCellAttr
*attrtomerge
= attr
;
83 attr
= new wxGridCellAttr
;
84 attr
->SetKind(wxGridCellAttr::Merged
);
85 attr
->MergeWith(attrtomerge
);
87 attrtomerge
->DecRef();
92 case (wxGridCellAttr::Col
):
93 //As we must have a Coll, and were setting Coll attributes
94 // we can based on wxdbTable's so just set RO if attr valid
97 attr
= new wxGridCellAttr
;
98 wxDbGridCellAttrProvider
* self
= wxConstCast(this, wxDbGridCellAttrProvider
) ;
100 self
->SetColAttr(attr
, col
);
106 // wxGridCellAttr::Cell - Not required, will inherit on merge from row.
107 // wxGridCellAttr::Row - If wxDbtable ever supports row locking could add
108 // support to make RO on a row basis also.
109 // wxGridCellAttr::Default - Don't edit this ! or all cell with a attr will become readonly
110 // wxGridCellAttr::Merged - This should never be asked for.
119 void wxDbGridCellAttrProvider::AssignDbTable(wxDbTable
*tab
)
124 wxDbGridTableBase::wxDbGridTableBase(wxDbTable
*tab
, wxDbGridColInfo
* ColInfo
,
125 int count
, bool takeOwnership
) :
128 m_dbowner(takeOwnership
),
132 if (count
== wxUSE_QUERY
)
134 m_rowtotal
= m_data
? m_data
->Count() : 0;
140 // m_keys.Size(m_rowtotal);
144 m_nocols
= ColInfo
->Length();
145 m_ColInfo
= new wxDbGridColInfoBase
[m_nocols
];
147 wxDbGridColInfo
*ptr
= ColInfo
;
149 while (ptr
&& i
< m_nocols
)
151 m_ColInfo
[i
] = ptr
->m_data
;
158 wxLogDebug(wxT("NoCols over length after traversing %i items"),i
);
162 wxLogDebug(wxT("NoCols under length after traversing %i items"),i
);
168 wxDbGridTableBase::~wxDbGridTableBase()
170 wxDbGridCellAttrProvider
*provider
;
172 //Can't check for update here as
174 //FIXME: should i remove m_ColInfo and m_data from m_attrProvider if a wxDbGridAttrProvider
175 // if ((provider = dynamic_cast<wxDbGridCellAttrProvider *>(GetAttrProvider())))
176 // Using C casting for now until we can support dynamic_cast with wxWidgets
177 provider
= (wxDbGridCellAttrProvider
*)(GetAttrProvider());
180 provider
->AssignDbTable(NULL
);
191 bool wxDbGridTableBase::CanHaveAttributes()
193 if (!GetAttrProvider())
195 // use the default attr provider by default
196 SetAttrProvider(new wxDbGridCellAttrProvider(m_data
, m_ColInfo
));
202 bool wxDbGridTableBase::AssignDbTable(wxDbTable
*tab
, int count
, bool takeOwnership
)
204 wxDbGridCellAttrProvider
*provider
;
206 //Remove Information from grid about old data
209 wxGrid
*grid
= GetView();
211 grid
->ClearSelection();
212 if (grid
->IsCellEditControlEnabled())
214 grid
->DisableCellEditControl();
216 wxGridTableMessage
msg(this, wxGRIDTABLE_NOTIFY_ROWS_DELETED
,0,m_rowtotal
);
217 grid
->ProcessTableMessage(msg
);
220 //reset our internals...
228 //FIXME: Remove dynamic_cast before sumision to wxwin
229 // if ((provider = dynamic_cast<wxDbGridCellAttrProvider *> (GetAttrProvider())))
230 // Using C casting for now until we can support dynamic_cast with wxWidgets
231 provider
= (wxDbGridCellAttrProvider
*)(GetAttrProvider());
234 provider
->AssignDbTable(m_data
);
237 if (count
== wxUSE_QUERY
)
239 m_rowtotal
= m_data
? m_data
->Count() : 0;
247 //Add Information to grid about new data
250 wxGrid
* grid
= GetView();
251 wxGridTableMessage
msg(this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED
, m_rowtotal
);
252 grid
->ProcessTableMessage(msg
);
255 m_dbowner
= takeOwnership
;
256 m_rowmodified
= false;
260 wxString
wxDbGridTableBase::GetTypeName(int WXUNUSED(row
), int col
)
262 if (GetNumberCols() > col
)
264 if (m_ColInfo
[col
].wxtypename
== wxGRID_VALUE_DBAUTO
)
266 if (m_data
->GetNumberOfColumns() <= m_ColInfo
[col
].DbCol
)
268 wxFAIL_MSG (_T("You can not use wxGRID_VALUE_DBAUTO for virtual columns"));
270 switch(m_data
->GetColDefs()[(m_ColInfo
[col
].DbCol
)].SqlCtype
)
276 return wxGRID_VALUE_STRING
;
279 return wxGRID_VALUE_NUMBER
;
281 return wxGRID_VALUE_NUMBER
;
284 return wxGRID_VALUE_NUMBER
;
286 return wxGRID_VALUE_NUMBER
;
288 return wxGRID_VALUE_FLOAT
;
290 return wxGRID_VALUE_FLOAT
;
292 return wxGRID_VALUE_DATETIME
;
294 return wxGRID_VALUE_DATETIME
;
295 case SQL_C_TIMESTAMP
:
296 return wxGRID_VALUE_DATETIME
;
298 return wxGRID_VALUE_STRING
;
303 return m_ColInfo
[col
].wxtypename
;
306 wxFAIL_MSG (_T("unknown column"));
310 bool wxDbGridTableBase::CanGetValueAs(int row
, int col
, const wxString
& typeName
)
312 wxLogDebug(wxT("CanGetValueAs() on %i,%i"),row
,col
);
313 //Is this needed? As it will be validated on GetValueAsXXXX
316 if (typeName
== wxGRID_VALUE_STRING
)
318 //FIXME ummm What about blob field etc.
322 if (m_data
->IsColNull((UWORD
)m_ColInfo
[col
].DbCol
))
327 if (m_data
->GetNumberOfColumns() <= m_ColInfo
[col
].DbCol
)
329 //If a virtual column then we can't find it's type. we have to
330 // return false to get using wxVariant.
333 int sqltype
= m_data
->GetColDefs()[(m_ColInfo
[col
].DbCol
)].SqlCtype
;
335 if (typeName
== wxGRID_VALUE_DATETIME
)
337 if ((sqltype
== SQL_C_DATE
) ||
338 (sqltype
== SQL_C_TIME
) ||
339 (sqltype
== SQL_C_TIMESTAMP
))
345 if (typeName
== wxGRID_VALUE_NUMBER
)
347 if ((sqltype
== SQL_C_SSHORT
) ||
348 (sqltype
== SQL_C_USHORT
) ||
349 (sqltype
== SQL_C_SLONG
) ||
350 (sqltype
== SQL_C_ULONG
))
356 if (typeName
== wxGRID_VALUE_FLOAT
)
358 if ((sqltype
== SQL_C_SSHORT
) ||
359 (sqltype
== SQL_C_USHORT
) ||
360 (sqltype
== SQL_C_SLONG
) ||
361 (sqltype
== SQL_C_ULONG
) ||
362 (sqltype
== SQL_C_FLOAT
) ||
363 (sqltype
== SQL_C_DOUBLE
))
372 bool wxDbGridTableBase::CanSetValueAs(int WXUNUSED(row
), int col
, const wxString
& typeName
)
374 if (typeName
== wxGRID_VALUE_STRING
)
376 //FIXME ummm What about blob field etc.
380 if (!(m_data
->GetColDefs()[(m_ColInfo
[col
].DbCol
)].Updateable
))
385 if (m_data
->GetNumberOfColumns() <= m_ColInfo
[col
].DbCol
)
387 //If a virtual column then we can't find it's type. we have to faulse to
388 //get using wxVairent.
392 int sqltype
= m_data
->GetColDefs()[(m_ColInfo
[col
].DbCol
)].SqlCtype
;
393 if (typeName
== wxGRID_VALUE_DATETIME
)
395 if ((sqltype
== SQL_C_DATE
) ||
396 (sqltype
== SQL_C_TIME
) ||
397 (sqltype
== SQL_C_TIMESTAMP
))
403 if (typeName
== wxGRID_VALUE_NUMBER
)
405 if ((sqltype
== SQL_C_SSHORT
) ||
406 (sqltype
== SQL_C_USHORT
) ||
407 (sqltype
== SQL_C_SLONG
) ||
408 (sqltype
== SQL_C_ULONG
))
414 if (typeName
== wxGRID_VALUE_FLOAT
)
416 if ((sqltype
== SQL_C_SSHORT
) ||
417 (sqltype
== SQL_C_USHORT
) ||
418 (sqltype
== SQL_C_SLONG
) ||
419 (sqltype
== SQL_C_ULONG
) ||
420 (sqltype
== SQL_C_FLOAT
) ||
421 (sqltype
== SQL_C_DOUBLE
))
430 long wxDbGridTableBase::GetValueAsLong(int row
, int col
)
434 if (m_data
->GetNumberOfColumns() <= m_ColInfo
[col
].DbCol
)
436 wxFAIL_MSG (_T("You can not use GetValueAsLong for virtual columns"));
439 int sqltype
= m_data
->GetColDefs()[(m_ColInfo
[col
].DbCol
)].SqlCtype
;
440 if ((sqltype
== SQL_C_SSHORT
) ||
441 (sqltype
== SQL_C_USHORT
) ||
442 (sqltype
== SQL_C_SLONG
) ||
443 (sqltype
== SQL_C_ULONG
))
445 wxVariant val
= m_data
->GetColumn(m_ColInfo
[col
].DbCol
);
446 return val
.GetLong();
448 wxFAIL_MSG (_T("unknown column, "));
452 double wxDbGridTableBase::GetValueAsDouble(int row
, int col
)
454 wxLogDebug(wxT("GetValueAsDouble() on %i,%i"),row
,col
);
457 if (m_data
->GetNumberOfColumns() <= m_ColInfo
[col
].DbCol
)
459 wxFAIL_MSG (_T("You can not use GetValueAsDouble for virtual columns"));
462 int sqltype
= m_data
->GetColDefs()[(m_ColInfo
[col
].DbCol
)].SqlCtype
;
463 if ((sqltype
== SQL_C_SSHORT
) ||
464 (sqltype
== SQL_C_USHORT
) ||
465 (sqltype
== SQL_C_SLONG
) ||
466 (sqltype
== SQL_C_ULONG
) ||
467 (sqltype
== SQL_C_FLOAT
) ||
468 (sqltype
== SQL_C_DOUBLE
))
470 wxVariant val
= m_data
->GetColumn(m_ColInfo
[col
].DbCol
);
471 return val
.GetDouble();
473 wxFAIL_MSG (_T("unknown column"));
477 bool wxDbGridTableBase::GetValueAsBool(int row
, int col
)
479 wxLogDebug(wxT("GetValueAsBool() on %i,%i"),row
,col
);
482 if (m_data
->GetNumberOfColumns() <= m_ColInfo
[col
].DbCol
)
484 wxFAIL_MSG (_T("You can not use GetValueAsBool for virtual columns"));
487 int sqltype
= m_data
->GetColDefs()[(m_ColInfo
[col
].DbCol
)].SqlCtype
;
488 if ((sqltype
== SQL_C_SSHORT
) ||
489 (sqltype
== SQL_C_USHORT
) ||
490 (sqltype
== SQL_C_SLONG
) ||
491 (sqltype
== SQL_C_ULONG
))
493 wxVariant val
= m_data
->GetColumn(m_ColInfo
[col
].DbCol
);
494 return val
.GetBool();
496 wxFAIL_MSG (_T("unknown column, "));
500 void* wxDbGridTableBase::GetValueAsCustom(int row
, int col
, const wxString
& typeName
)
502 wxLogDebug(wxT("GetValueAsCustom() on %i,%i"),row
,col
);
505 if (m_data
->GetNumberOfColumns() <= m_ColInfo
[col
].DbCol
)
507 wxFAIL_MSG (_T("You can not use GetValueAsCustom for virtual columns"));
510 if (m_data
->IsColNull((UWORD
)m_ColInfo
[col
].DbCol
))
513 if (typeName
== wxGRID_VALUE_DATETIME
)
515 wxDbColDef
*pColDefs
= m_data
->GetColDefs();
516 int sqltype
= pColDefs
[(m_ColInfo
[col
].DbCol
)].SqlCtype
;
518 if ((sqltype
== SQL_C_DATE
) ||
519 (sqltype
== SQL_C_TIME
) ||
520 (sqltype
== SQL_C_TIMESTAMP
))
522 wxVariant val
= m_data
->GetColumn(m_ColInfo
[col
].DbCol
);
523 return new wxDateTime(val
.GetDateTime());
526 wxFAIL_MSG (_T("unknown column data type "));
531 void wxDbGridTableBase::SetValueAsCustom(int row
, int col
, const wxString
& typeName
, void* value
)
533 wxLogDebug(wxT("SetValueAsCustom() on %i,%i"),row
,col
);
536 if (m_data
->GetNumberOfColumns() <= m_ColInfo
[col
].DbCol
)
538 wxFAIL_MSG (_T("You can not use SetValueAsCustom for virtual columns"));
542 if (typeName
== wxGRID_VALUE_DATETIME
)
544 int sqltype
= m_data
->GetColDefs()[(m_ColInfo
[col
].DbCol
)].SqlCtype
;
545 if ((sqltype
== SQL_C_DATE
) ||
546 (sqltype
== SQL_C_TIME
) ||
547 (sqltype
== SQL_C_TIMESTAMP
))
549 //FIXME: you can't dynamic_cast from (void *)
550 //wxDateTime *date = wxDynamicCast(value, wxDateTime);
551 wxDateTime
*date
= (wxDateTime
*)value
;
554 wxFAIL_MSG (_T("Failed to convert data"));
558 m_rowmodified
= true;
559 m_data
->SetColumn(m_ColInfo
[col
].DbCol
,val
);
562 wxFAIL_MSG (_T("unknown column data type"));
567 wxString
wxDbGridTableBase::GetColLabelValue(int col
)
569 if (GetNumberCols() > col
)
571 return m_ColInfo
[col
].Title
;
573 wxFAIL_MSG (_T("unknown column"));
577 bool wxDbGridTableBase::IsEmptyCell(int row
, int col
)
579 wxLogDebug(wxT("IsEmtpyCell on %i,%i"),row
,col
);
582 return m_data
->IsColNull((UWORD
)m_ColInfo
[col
].DbCol
);
586 wxString
wxDbGridTableBase::GetValue(int row
, int col
)
588 wxLogDebug(wxT("GetValue() on %i,%i"),row
,col
);
591 wxVariant val
= m_data
->GetColumn(m_ColInfo
[col
].DbCol
);
592 wxLogDebug(wxT("\tReturning \"%s\"\n"),val
.GetString().c_str());
594 return val
.GetString();
598 void wxDbGridTableBase::SetValue(int row
, int col
,const wxString
& value
)
600 wxLogDebug(wxT("SetValue() on %i,%i"),row
,col
);
603 wxVariant
val(value
);
605 m_rowmodified
= true;
606 m_data
->SetColumn(m_ColInfo
[col
].DbCol
,val
);
610 void wxDbGridTableBase::SetValueAsLong(int row
, int col
, long value
)
612 wxLogDebug(wxT("SetValueAsLong() on %i,%i"),row
,col
);
615 wxVariant
val(value
);
617 m_rowmodified
= true;
618 m_data
->SetColumn(m_ColInfo
[col
].DbCol
,val
);
622 void wxDbGridTableBase::SetValueAsDouble(int row
, int col
, double value
)
624 wxLogDebug(wxT("SetValueAsDouble() on %i,%i"),row
,col
);
627 wxVariant
val(value
);
629 m_rowmodified
= true;
630 m_data
->SetColumn(m_ColInfo
[col
].DbCol
,val
);
635 void wxDbGridTableBase::SetValueAsBool(int row
, int col
, bool value
)
637 wxLogDebug(wxT("SetValueAsBool() on %i,%i"),row
,col
);
640 wxVariant
val(value
);
642 m_rowmodified
= true;
643 m_data
->SetColumn(m_ColInfo
[col
].DbCol
,val
);
647 void wxDbGridTableBase::ValidateRow(int row
)
649 wxLogDebug(wxT("ValidateRow(%i) currently on row (%i). Array count = %i"),row
,m_row
,m_keys
.GetCount());
655 //We add to row as Count is unsigned!
656 if ((unsigned)(row
+1) > m_keys
.GetCount())
658 wxLogDebug(wxT("\trow key unknown"));
659 // Extend Array, iterate through data filling with keys
660 m_data
->SetRowMode(wxDbTable::WX_ROW_MODE_QUERY
);
662 for (trow
= m_keys
.GetCount(); trow
<= row
; trow
++)
664 wxLogDebug(wxT("Fetching row %i.."), trow
);
665 bool ret
= m_data
->GetNext();
667 wxLogDebug(wxT(" ...success=(%i)"),ret
);
668 GenericKey k
= m_data
->GetKey();
675 wxLogDebug(wxT("\trow key known centering data"));
676 GenericKey k
= m_keys
.Item(row
);
677 m_data
->SetRowMode(wxDbTable::WX_ROW_MODE_INDIVIDUAL
);
678 m_data
->ClearMemberVars();
680 if (!m_data
->QueryOnKeyFields())
682 wxDbLogExtendedErrorMsg(_T("ODBC error during Query()\n\n"), m_data
->GetDb(),__TFILE__
,__LINE__
);
689 m_rowmodified
= false;
692 bool wxDbGridTableBase::Writeback() const
700 wxLogDebug(wxT("\trow key unknown"));
702 // FIXME: this code requires dbtable support for record status
704 switch (m_data
->get_ModifiedStatus())
706 case wxDbTable::UpdatePending
:
707 result
= m_data
->Update();
709 case wxDbTable::InsertPending
:
710 result
= (m_data
->Insert() == SQL_SUCCESS
);
717 wxLogDebug(wxT("WARNING : Row writeback not implemented "));
722 #include "wx/arrimpl.cpp"
724 WX_DEFINE_EXPORTED_OBJARRAY(keyarray
)
726 #endif // wxUSE_GRID && wxUSE_ODBC