00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 from PyQt4.QtCore import *
00029 from PyQt4.QtGui import *
00030
00031 from Koo.Common import Icons
00032 from Koo.Common import Calendar
00033 from Koo.Common import Numeric
00034 from Koo.Common import Common
00035 from Koo.Rpc import Rpc
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053 class KooModel(QAbstractItemModel):
00054
00055
00056 TreeMode = 1
00057 ListMode = 2
00058
00059
00060
00061
00062 IdRole = Qt.UserRole
00063
00064
00065
00066
00067
00068 ValueRole = Qt.UserRole + 1
00069
00070 def __init__(self, parent=None):
00071 QAbstractItemModel.__init__(self, parent)
00072 self.group = None
00073 self.fields = {}
00074 self.icon = ''
00075 self.iconField = ''
00076 self.child = ''
00077 self.childField = ''
00078 self.mode = self.TreeMode
00079 self.colors = {}
00080 self.showBackgroundColor = True
00081 self._readOnly = True
00082 self._updatesEnabled = True
00083 self._showToolTips = True
00084
00085
00086
00087
00088 self.visibleFields = []
00089
00090
00091
00092
00093
00094 def setRecordGroup(self, group):
00095 self.emit( SIGNAL('modelAboutToBeReset()') )
00096 if self.group:
00097 self.disconnect( self.group, SIGNAL('recordsInserted(int,int)'), self.recordsInserted )
00098 self.disconnect( self.group, SIGNAL('recordChanged(PyQt_PyObject)'), self.recordChanged )
00099 self.disconnect( self.group, SIGNAL('recordsRemoved(int,int)'), self.recordsRemoved )
00100
00101 self.group = group
00102 if self.group:
00103 self.connect( self.group, SIGNAL('recordsInserted(int,int)'), self.recordsInserted )
00104 self.connect( self.group, SIGNAL('recordChanged(PyQt_PyObject)'), self.recordChanged )
00105 self.connect( self.group, SIGNAL('recordsRemoved(int,int)'), self.recordsRemoved )
00106
00107
00108
00109 self.emit( SIGNAL('modelReset()') )
00110 self.updateVisibleFields()
00111
00112
00113 def recordGroup(self):
00114 return self.group
00115
00116
00117 def setReadOnly(self, value):
00118 self._readOnly = value
00119
00120
00121 def isReadOnly(self):
00122 return self._readOnly
00123
00124 def recordsInserted(self, start, end):
00125 if self._updatesEnabled:
00126 self.reset()
00127
00128
00129
00130 def recordChanged(self, record):
00131 if not record:
00132 return
00133 leftIndex = self.indexFromId( record.id )
00134 if not leftIndex.isValid():
00135 self.reset()
00136 return
00137 rightIndex = self.index( leftIndex.row(), self.columnCount() - 1 )
00138 self.emit( SIGNAL('dataChanged(QModelIndex,QModelIndex)'), leftIndex, rightIndex )
00139
00140 def recordsRemoved(self, start, end):
00141 if self._updatesEnabled:
00142 self.reset()
00143
00144
00145
00146
00147 def setFields(self, fields):
00148 self.fields = fields
00149 self.updateVisibleFields()
00150
00151
00152
00153
00154
00155 def setFieldsOrder(self, fields):
00156 self.visibleFields = fields
00157 self.updateVisibleFields()
00158
00159
00160
00161
00162
00163
00164 def setColors(self, colors):
00165 self.colors = colors
00166
00167
00168
00169
00170
00171
00172
00173 def setShowBackgroundColor(self, showBackgroundColor):
00174 self.showBackgroundColor = showBackgroundColor
00175
00176
00177
00178
00179
00180
00181 def setIconForField(self, icon, iconField):
00182 self.icon = icon
00183 self.iconField = iconField
00184 self.updateVisibleFields()
00185
00186
00187
00188 def setChildrenForField(self, child, childField):
00189 self.child = child
00190 self.childField = childField
00191 self.updateVisibleFields()
00192
00193
00194
00195 def updateVisibleFields(self):
00196 if not self.visibleFields:
00197 self.visibleFields = self.fields.keys()[:]
00198 if self.icon in self.visibleFields:
00199 del self.visibleFields[self.visibleFields.index(self.icon)]
00200 if self.child in self.visibleFields:
00201 del self.visibleFields[self.visibleFields.index(self.child)]
00202
00203
00204
00205
00206
00207
00208 def setMode(self, mode):
00209 self.mode = mode
00210
00211
00212 def setShowToolTips(self, show):
00213 self._showToolTips = show
00214
00215
00216 def id(self, index):
00217 if not self.group:
00218 return 0
00219 if not index.isValid():
00220 return 0
00221
00222 model = self.record( index.row(), index.internalPointer() )
00223 if model:
00224 return model.id
00225 else:
00226 return 0
00227
00228
00229
00230 def rowCount(self, parent = QModelIndex()):
00231 if not self.group:
00232 return 0
00233
00234
00235 if self.mode == self.ListMode and parent.isValid() and parent.internalPointer() != self.group:
00236 return 0
00237
00238 if parent.isValid():
00239
00240 field = self.field( parent.column() )
00241 if field == self.childField:
00242 fieldType = self.fieldTypeByName(self.child, parent.internalPointer())
00243 if fieldType in ['one2many', 'many2many']:
00244 value = self.valueByName(parent.row(), self.child, parent.internalPointer())
00245 if value:
00246 return value.count()
00247 else:
00248 return 0
00249 else:
00250 return 0
00251
00252
00253 fieldType = self.fieldType( parent.column(), parent.internalPointer() )
00254 if fieldType in ['one2many', 'many2many']:
00255 value = self.value( parent.row(), parent.column(), parent.internalPointer() )
00256 return value.count()
00257 else:
00258 return 0
00259 else:
00260 return self.group.count()
00261
00262 def columnCount(self, parent = QModelIndex()):
00263 if not self.group:
00264 return 0
00265
00266
00267 if self.mode == self.ListMode and parent.isValid() and parent.internalPointer() != self.group:
00268 return 0
00269
00270
00271
00272 return len(self.visibleFields)
00273
00274 def flags(self, index):
00275 defaultFlags = QAbstractItemModel.flags(self, index)
00276 if 'sequence' in self.fields:
00277 defaultFlags = defaultFlags | Qt.ItemIsDropEnabled
00278 if index.isValid():
00279 defaultFlags = defaultFlags | Qt.ItemIsDragEnabled
00280
00281 field = self.fields[self.field( index.column() )]
00282 if self._readOnly or ( 'readonly' in field and field['readonly'] ):
00283 return defaultFlags
00284 else:
00285 return defaultFlags | Qt.ItemIsEditable
00286
00287 def setData(self, index, value, role):
00288
00289 if role != Qt.EditRole:
00290 return True
00291 if not index.isValid():
00292 return True
00293 model = self.record( index.row(), index.internalPointer() )
00294 if not model:
00295 return True
00296 field = self.field( index.column() )
00297 fieldType = self.fieldType( index.column(), index.internalPointer() )
00298
00299 if fieldType == 'boolean':
00300 model.setValue( field, value.toBool() )
00301 elif fieldType in ('float', 'float_time'):
00302 model.setValue( field, value.toDouble()[0] )
00303 elif fieldType == 'integer':
00304 model.setValue( field, value.toInt()[0] )
00305 elif fieldType == 'selection':
00306 value = unicode( value.toString() )
00307 modelField = self.fields[self.field( index.column() )]
00308 for x in modelField['selection']:
00309 if x[1] == value:
00310 model.setValue( field, x[0] )
00311 elif fieldType in ('char', 'text'):
00312 model.setValue( field, unicode( value.toString() ) )
00313 elif fieldType == 'date':
00314 model.setValue( field, Calendar.dateToStorage( value.toDate() ) )
00315 elif fieldType == 'datetime' and value:
00316 model.setValue( field, Calendar.dateTimeToStorage( value.toDateTime() ) )
00317 elif fieldType == 'time' and value:
00318 model.setValue( field, Calendar.timeToStorage( value.toTime() ) )
00319 elif fieldType == 'many2many':
00320 m = model.value( field )
00321 m.clear()
00322 ids = [x.toInt()[0] for x in value.toList()]
00323 m.load( ids )
00324 elif fieldType == 'many2one':
00325 value = value.toList()
00326 if value:
00327 value = [ int(value[0].toInt()[0]), unicode(value[1].toString()) ]
00328 model.setValue( field, value )
00329 else:
00330 print "Unable to store value of type: ", fieldType
00331
00332 return True
00333
00334 def data(self, index, role=Qt.DisplayRole ):
00335 if not self.group:
00336 return QVariant()
00337 if role in (Qt.DisplayRole, Qt.EditRole) or (self._showToolTips and role == Qt.ToolTipRole):
00338 value = self.value( index.row(), index.column(), index.internalPointer() )
00339 fieldType = self.fieldType( index.column(), index.internalPointer() )
00340 if fieldType in ['one2many', 'many2many']:
00341 return QVariant( '(%d)' % value.count() )
00342 elif fieldType == 'selection':
00343 field = self.fields[self.field( index.column() )]
00344 for x in field['selection']:
00345 if x[0] == value:
00346 return QVariant( unicode(x[1]) )
00347 return QVariant()
00348 elif fieldType == 'date' and value:
00349 return QVariant( Calendar.dateToText( Calendar.storageToDate( value ) ) )
00350 elif fieldType == 'datetime' and value:
00351 return QVariant( Calendar.dateTimeToText( Calendar.storageToDateTime( value ) ) )
00352 elif fieldType == 'float':
00353
00354
00355
00356 field = self.fields[self.field( index.column() )]
00357 if role == Qt.EditRole:
00358 thousands = False
00359 else:
00360 thousands = True
00361 return QVariant( Numeric.floatToText(value, field.get('digits',None), thousands) )
00362 elif fieldType == 'integer':
00363 return QVariant( Numeric.integerToText(value) )
00364 elif fieldType == 'float_time':
00365 return QVariant( Calendar.floatTimeToText(value) )
00366 elif fieldType == 'binary':
00367 if value:
00368 return QVariant( _('%d bytes') % len(value) )
00369 else:
00370 return QVariant()
00371 elif fieldType == 'boolean':
00372 return QVariant( bool(value) )
00373 else:
00374 if value == False or value == None:
00375 return QVariant()
00376 else:
00377
00378 return QVariant( unicode(value).replace('\n', ' ') )
00379 elif role == Qt.DecorationRole:
00380 if self.field( index.column() ) == self.iconField:
00381 model = self.record( index.row(), index.internalPointer() )
00382
00383 if model and self.icon in model.values:
00384 return QVariant( Icons.kdeIcon( model.value( self.icon ) ) )
00385 else:
00386 return QVariant()
00387 else:
00388 return QVariant()
00389 elif role == Qt.BackgroundRole:
00390 if not self.showBackgroundColor:
00391 return QVariant()
00392 field = self.fields[self.field( index.column() )]
00393 model = self.record( index.row(), index.internalPointer() )
00394
00395
00396
00397
00398
00399
00400 if not model:
00401 return QVariant()
00402
00403
00404
00405 if not model.isFieldValid( self.field( index.column() ) ):
00406 color = '#FF6969'
00407 elif 'readonly' in field and field['readonly']:
00408 color = 'lightgrey'
00409 elif 'required' in field and field['required']:
00410 color = '#ddddff'
00411 else:
00412 color = 'white'
00413 return QVariant( QBrush( QColor( color ) ) )
00414 elif role == Qt.ForegroundRole:
00415 if not self.colors:
00416 return QVariant()
00417 model = self.record( index.row(), index.internalPointer() )
00418
00419
00420
00421
00422
00423
00424 if not model:
00425 return QVariant()
00426 palette = QPalette()
00427 color = palette.color( QPalette.WindowText )
00428 for (c, expression) in self.colors:
00429 if model.evaluateExpression( expression, checkLoad=False ):
00430 color = c
00431 break
00432 return QVariant( QBrush( QColor( color ) ) )
00433 elif role == Qt.TextAlignmentRole:
00434 fieldType = self.fieldType( index.column(), index.internalPointer() )
00435 if fieldType in ['integer', 'float', 'float_time', 'time', 'date', 'datetime']:
00436 return QVariant( Qt.AlignRight | Qt.AlignVCenter )
00437 else:
00438 return QVariant( Qt.AlignLeft | Qt.AlignVCenter )
00439 elif role == KooModel.IdRole:
00440 model = self.record( index.row(), index.internalPointer() )
00441 return QVariant( model.id )
00442 elif role == KooModel.ValueRole:
00443 value = self.value( index.row(), index.column(), index.internalPointer() )
00444 fieldType = self.fieldType( index.column(), index.internalPointer() )
00445 if fieldType in ['one2many', 'many2many']:
00446
00447 return QVariant( '(%d)' % value.count() )
00448 elif fieldType == 'selection':
00449
00450 field = self.fields[self.field( index.column() )]
00451 for x in field['selection']:
00452 if x[0] == value:
00453 return QVariant( unicode(x[1]) )
00454 return QVariant()
00455 elif fieldType == 'date' and value:
00456 return QVariant( Calendar.storageToDate( value ) )
00457 elif fieldType == 'datetime' and value:
00458 return QVariant( Calendar.storageToDateTime( value ) )
00459 elif fieldType == 'float':
00460
00461
00462
00463 field = self.fields[self.field( index.column() )]
00464 return QVariant( Numeric.floatToText(value, field.get('digits',None) ) )
00465 elif fieldType == 'float_time':
00466 return QVariant( value )
00467 elif fieldType == 'binary':
00468 if value:
00469 return QVariant( QByteArray.fromBase64( value ) )
00470 else:
00471 return QVariant()
00472 elif fieldType == 'boolean':
00473 return QVariant( bool(value) )
00474 else:
00475 if value == False or value == None:
00476 return QVariant()
00477 else:
00478 return QVariant( unicode(value) )
00479 else:
00480 return QVariant()
00481
00482 def index(self, row, column, parent = QModelIndex() ):
00483 if not self.group:
00484 return QModelIndex()
00485 if parent.isValid():
00486
00487 field = self.field( parent.column() )
00488 if field == self.childField:
00489 field = self.child
00490
00491 value = self.valueByName( parent.row(), field, parent.internalPointer() )
00492 return self.createIndex( row, column, value )
00493 else:
00494 return self.createIndex( row, column, self.group )
00495
00496 def parent(self, index):
00497 if not self.group:
00498 return QModelIndex()
00499 if not index.isValid():
00500 return QModelIndex()
00501 if self.mode == self.ListMode:
00502 return QModelIndex()
00503
00504
00505 group = index.internalPointer()
00506
00507
00508 if group == self.group:
00509 return QModelIndex()
00510
00511
00512
00513
00514 model = group.parent
00515 parent = group.parent.group
00516
00517 if not parent.recordExists( model ):
00518
00519
00520
00521 return QModelIndex()
00522
00523 row = parent.indexOfRecord( model )
00524 for x, y in model.values.items():
00525 if y == group:
00526 field = x
00527 break
00528
00529
00530 if field == self.child:
00531 field = self.childField
00532
00533
00534
00535
00536
00537
00538 if field in self.visibleFields:
00539 column = self.visibleFields.index(field)
00540 return self.createIndex( row, column, parent )
00541 else:
00542 return QModelIndex()
00543
00544 def mimeTypes(self):
00545 return QStringList( [ 'text/plain' ] )
00546
00547 def mimeData(self, indexes):
00548 data = QMimeData()
00549 d = []
00550 for index in indexes:
00551 if index.column() == 0:
00552 d.append( self.id( index ) )
00553 data.setText( str( d[0] ) )
00554 return data
00555
00556 def dropMimeData(self, data, action, row, column, parent):
00557 if action != Qt.MoveAction:
00558 return False
00559 if not parent.isValid():
00560 return False
00561 if row != -1 or column != -1:
00562 return False
00563 group = parent.internalPointer()
00564
00565
00566
00567
00568
00569 if group.sortedField and group.sortedField != 'sequence':
00570 return False
00571 record = self.recordFromIndex( parent )
00572 seq = record.value( 'sequence' )
00573 seq = seq or 0
00574 if group.sortedOrder == Qt.AscendingOrder:
00575 increment = -1
00576 else:
00577 increment = 1
00578 seq = seq + increment
00579 id = int( str( data.text() ) )
00580 movedRecord = self.recordFromIndex( self.indexFromId( id ) )
00581 movedRecord.setValue( 'sequence', seq )
00582 group.records.remove( movedRecord )
00583 group.records.insert( group.records.index(record), movedRecord )
00584
00585 if group.count():
00586 idx = group.indexOfId( id )
00587 idx -= 1
00588 while idx >= 0 and group.records[idx].value('sequence') == seq:
00589 group.records[idx].setValue('sequence', seq + increment)
00590 idx -= 1
00591 self.reset()
00592 return True
00593
00594 def supportedDropActions(self):
00595 return Qt.CopyAction | Qt.MoveAction
00596
00597
00598
00599 def sort(self, column, order):
00600 QApplication.setOverrideCursor( Qt.WaitCursor )
00601 try:
00602 self.group.sort( self.field( column ), order )
00603 except Rpc.RpcException, e:
00604 pass
00605 QApplication.restoreOverrideCursor()
00606
00607 def headerData(self, section, orientation, role):
00608 if orientation == Qt.Vertical:
00609 return QVariant()
00610 if role == Qt.DisplayRole:
00611 field = self.fields[ self.field( section ) ]
00612 return QVariant( Common.normalizeLabel( unicode( field['string'] ) ) )
00613 elif role == Qt.FontRole and not self._readOnly:
00614 if self.group.isFieldRequired( self.field( section ) ):
00615 font = QFont()
00616 font.setBold( True )
00617 return QVariant( font )
00618 return QVariant()
00619
00620
00621 def field(self, column):
00622 if column >= len(self.visibleFields):
00623 return None
00624 else:
00625 return self.visibleFields[column]
00626
00627
00628
00629
00630
00631
00632 def fieldType(self, column, group):
00633 field = self.field(column)
00634 if field:
00635 return self.group.fields[ field ]['type']
00636 else:
00637 return None
00638
00639
00640 def fieldTypeByName(self, field, group):
00641 if field in self.group.fields:
00642 return self.group.fields[ field ]['type']
00643 else:
00644 return None
00645
00646
00647 def record(self, row, group):
00648 if not group:
00649 return None
00650
00651
00652
00653
00654
00655 if not group.fields:
00656 group.addFields( self.fields )
00657 if row >= group.count():
00658 return None
00659 else:
00660 return group.modelByIndex( row )
00661
00662
00663
00664
00665 def value(self, row, column, group):
00666
00667
00668 if not group.fields:
00669 group.addFields( self.fields )
00670 model = self.record(row, group)
00671 field = self.field(column)
00672 if not field or not model:
00673 return None
00674 else:
00675 return model.value( field )
00676
00677 def setValue(self, value, row, column, group):
00678
00679
00680 if not group.fields:
00681 group.addFields( self.fields )
00682 model = self.record(row, group)
00683 field = self.field(column)
00684 if field and model:
00685 model.setValue( field, value )
00686
00687
00688 def valueByName(self, row, field, group):
00689
00690
00691 if not group.fields:
00692 group.addFields( self.fields )
00693
00694 model = self.record( row, group )
00695 if not model:
00696 return None
00697 else:
00698 return model.value( field )
00699
00700
00701
00702
00703 def id(self, index):
00704 model = self.record( index.row(), index.internalPointer() )
00705 if model:
00706 return model.id
00707 else:
00708 return -1
00709
00710
00711
00712 def indexFromId(self, id):
00713 if not self.group:
00714 return QModelIndex()
00715
00716 row = self.group.indexOfId( id )
00717 if row >= 0:
00718 return self.index( row, 0 )
00719 return QModelIndex()
00720
00721 def recordFromIndex(self, index):
00722 return self.record( index.row(), index.internalPointer() )
00723
00724
00725
00726 def indexFromRecord(self, record):
00727 if not self.group:
00728 return QModelIndex()
00729 row = self.group.indexOfRecord( record )
00730 if row >= 0:
00731 return self.index( row, 0 )
00732 return QModelIndex()
00733
00734 class KooGroupedModel( QAbstractProxyModel ):
00735 def __getattr__(self, name):
00736 if name == 'group':
00737 return self.sourceModel().group
00738 return QAbstractProxyModel.__getattr__(name)
00739
00740 def __setattr__(self, name, value):
00741 if name == 'group':
00742 self.sourceModel().group = value
00743 return
00744 return QAbstractProxyModel.__setattr__(self, name, value)
00745
00746 def setSourceModel(self, model):
00747 QAbstractProxyModel.setSourceModel(self, model)
00748 self.group = model.group
00749
00750 def mapFromSource( self, index ):
00751 model = self.sourceModel()
00752 newRow = 0
00753 previous = False
00754 for y in xrange(0, index.row()):
00755 value = model.value( y, 0, self.group)
00756 if value != previous:
00757 newRow += 1
00758 previous = value
00759 return self.createIndex( newRow, index.column() )
00760
00761 def mapToSource( self, index ):
00762 model = self.sourceModel()
00763 previous = None
00764 newRow = 0
00765 for y in xrange(0, self.group.count()):
00766 value = model.value( y, 0, self.group)
00767 if value != previous:
00768 newRow += 1
00769 if newRow == index.row():
00770 break
00771 return self.createIndex( y, index.column() )
00772
00773 def recordGroup(self):
00774 return self.sourceModel().recordGroup()
00775
00776 def setRecordGroup(self, recordGroup):
00777 return self.sourceModel().setRecordGroup( recordGroup )
00778
00779 def isReadOnly(self):
00780 return self.sourceModel().isReadOnly()
00781
00782 def rowCount(self, parent = QModelIndex()):
00783 model = self.sourceModel()
00784 newRow = 0
00785 previous = None
00786 for y in xrange(0, self.group.count()):
00787 value = model.value( y, 0, self.group )
00788 if value != previous:
00789 newRow += 1
00790 return newRow
00791
00792 def columnCount(self, parent = QModelIndex()):
00793 return self.sourceModel().columnCount( parent )
00794
00795 def index(self, row, column, parent = QModelIndex() ):
00796 return self.createIndex( row, column, parent )