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 )