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 
00029 
00030 from Koo.Model import KooModel
00031 from Koo.Model.Group import RecordGroup
00032 from Koo.View.AbstractView import *
00033 from Koo.Fields.AbstractFieldDelegate import *
00034 from PyQt4.QtCore import *
00035 from PyQt4.QtGui import *
00036 from Koo.Common import Numeric
00037 from Koo import Rpc
00038 
00039 class KooTreeView(QTreeView):
00040         def contextMenuEvent( self, event ):
00041                 index = self.indexAt( event.pos() )
00042                 delegate = self.itemDelegate( index )
00043                 if not isinstance(delegate, AbstractFieldDelegate):
00044                         return
00045                 delegate.showPopupMenu( self, self.mapToGlobal( event.pos() ) )
00046 
00047         def sizeHintForColumn( self, column ):
00048                 QApplication.setOverrideCursor( Qt.WaitCursor )
00049                 try:
00050                         model = self.model()
00051                         
00052                         group = model.recordGroup()
00053                         records = group.loadedRecords()
00054                         
00055                         if len(records) == group.count():
00056                                 QApplication.restoreOverrideCursor()
00057                                 return QTreeView.sizeHintForColumn( self, column )
00058                         viewOptions = self.viewOptions()
00059                         delegate = self.itemDelegateForColumn( column )
00060                         hint = 0
00061                         for record in records:
00062                                 index = model.indexFromRecord( record )
00063                                 index = model.index( index.row(), column, index.parent() )
00064                                 hint = max( hint, delegate.sizeHint( viewOptions, index ).width() )
00065                 except Rpc.RpcException, e:
00066                         hint = 100
00067                 QApplication.restoreOverrideCursor()
00068                 return hint
00069 
00070         def moveCursor( self, action, modifiers ):
00071                 index = self.currentIndex()
00072                 if not index.isValid():
00073                         return QModelIndex()
00074                 row = index.row()
00075                 column = index.column()
00076                 lastRow = index.model().rowCount() - 1
00077                 lastColumn = index.model().columnCount() - 1
00078                 if action == QAbstractItemView.MoveUp:
00079                         row -= 1
00080                 elif action == QAbstractItemView.MoveDown:
00081                         row += 1
00082                 elif action == QAbstractItemView.MoveLeft:
00083                         column -= 1
00084                 elif action == QAbstractItemView.MoveRight:
00085                         column += 1
00086                 elif action == QAbstractItemView.MoveHome:
00087                         if column == 0:
00088                                 row = 0
00089                         else:
00090                                 column = 0 
00091                 elif action == QAbstractItemView.MoveEnd:
00092                         if column == lastColumn:
00093                                 row = lastRow
00094                         else:
00095                                 column = lastColumn
00096                 elif action == QAbstractItemView.MovePageUp:
00097                         row -= 12 
00098                 elif action == QAbstractItemView.MovePageDown:
00099                         row += 12 
00100                 elif action == QAbstractItemView.MoveNext:
00101                         column += 1
00102                 elif action == QAbstractItemView.MovePrevious:
00103                         column -= 1
00104 
00105                 if column > lastColumn:
00106                         column = 0
00107                         row += 1
00108                 elif column < 0:
00109                         column = lastColumn
00110                         row -= 1
00111                 if row < 0:
00112                         column = 0
00113                         row = 0
00114                 elif row > lastRow:
00115                         row = lastRow
00116                         column = 0
00117                 return self.model().createIndex( row, column, index.internalPointer() ) 
00118 
00119 class TreeView( AbstractView ):
00120 
00121         def __init__(self, parent, widgetType='tree'):
00122                 AbstractView.__init__(self, parent)
00123                 self._widgetType = widgetType
00124                 self.treeModel = None
00125                 self.reload = False
00126                 self.title=""
00127                 self.selecting = False
00128                 self.setAddOnTop( False )
00129 
00130                 if self._widgetType == 'tree':
00131                         self.widget = KooTreeView( self )
00132                         self.widget.setAllColumnsShowFocus( False )
00133                         self.widget.setSortingEnabled(True)
00134                         self.widget.setRootIsDecorated( False )
00135                         self.widget.setAlternatingRowColors( True )
00136                         self.widget.setVerticalScrollMode( QAbstractItemView.ScrollPerItem )
00137                         self.widget.sortByColumn( 0, Qt.AscendingOrder )
00138                         self.widget.setDragEnabled( True )
00139                         self.widget.setAcceptDrops( True )
00140                         self.widget.setDropIndicatorShown( True );
00141                         self.widget.setDragDropMode( QAbstractItemView.InternalMove )
00142 
00143                         
00144                         
00145                         
00146                         
00147                         
00148                         
00149                         
00150                         self.widget.setUniformRowHeights( True )
00151                 elif self._widgetType == 'list':
00152                         self.widget = QListView( self )
00153                         self.widget.setViewMode( QListView.IconMode )
00154                         self.widget.setFlow( QListView.LeftToRight )
00155                         self.widget.setGridSize( QSize( 200, 20 ) )
00156                         self.widget.setResizeMode( QListView.Adjust )
00157                 elif self._widgetType == 'table':
00158                         self._widgetType = 'table'
00159                         self.widget = QTableView( self )
00160 
00161                 
00162                 
00163                 self.setFocusProxy( self.widget )
00164 
00165                 
00166                 self.aggregates = []
00167                 self.aggregatesContainer = QWidget( self )
00168                 self.aggregatesLayout = QHBoxLayout( self.aggregatesContainer )
00169                 self.aggregatesLayout.setContentsMargins( 0, 0, 0, 0 )
00170 
00171                 self.setAllowMultipleSelection(True)
00172 
00173                 self.connect( self.widget, SIGNAL('activated(QModelIndex)'), self.activated )
00174 
00175                 self.currentRecord = None
00176 
00177                 layout = QVBoxLayout()
00178                 layout.setContentsMargins(0, 0, 0, 0)
00179                 layout.addWidget( self.widget )
00180                 layout.addWidget( self.aggregatesContainer )
00181                 self.setLayout( layout )
00182                 self._readOnly = True
00183 
00184         def sizeHint(self):
00185                 return QSize(10, 10)
00186         
00187         def viewType(self):
00188                 return 'tree'
00189 
00190         def setGridWidth( self, width ):
00191                 if self._widgetType == 'list':
00192                         self.widget.setGridSize( QSize( width, self.widget.gridSize().height() ) )
00193 
00194         def setGridHeight( self, height ):
00195                 if self._widgetType == 'list':
00196                         self.widget.setGridSize( QSize( self.widget.gridSize().width(), height ) )
00197 
00198         def setModel( self, model ):
00199                 self.currentRecord = None
00200                 self.treeModel = model  
00201                 self.widget.setModel( self.treeModel )
00202                 self.connect( self.widget.selectionModel(),SIGNAL('currentChanged(QModelIndex, QModelIndex)'),self.currentChanged)
00203                 self.connect( self.treeModel, SIGNAL('rowsInserted(const QModelIndex &,int,int)'), self.updateAggregates )
00204                 self.connect( self.treeModel, SIGNAL('rowsRemoved(const QModelIndex &,int,int)'), self.updateAggregates )
00205                 self.connect( self.treeModel, SIGNAL('modelReset()'), self.updateAggregates )
00206                 self.connect( self.treeModel.recordGroup(), SIGNAL('sorting'), self.sorting )
00207 
00208         def sorting(self, value):
00209                 if value == RecordGroup.SortingNotPossible:
00210                         self.emit( SIGNAL('statusMessage(QString)'), _("<font color='red'>Sorting not possible.</font>") )
00211                 elif value == RecordGroup.SortingOnlyGroups:
00212                         self.emit( SIGNAL('statusMessage(QString)'), _("<font color='red'>Sorting only groups.</font>") )
00213                 elif value == RecordGroup.SortingNotPossibleModified:
00214                         self.emit( SIGNAL('statusMessage(QString)'), _("<font color='red'>Save changes before sorting.</font>") )
00215                 else:
00216                         self.emit( SIGNAL('statusMessage(QString)'), '' )
00217 
00218         def addAggregate( self, name, label, bold, digits ):
00219                 aggLabel = QLabel( label + ':', self.aggregatesContainer )
00220                 aggLabel.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed )
00221                 aggValue = QLabel( self.aggregatesContainer )
00222                 aggValue.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed )
00223                 if bold:
00224                         font = QFont()
00225                         font.setBold( True )
00226                         aggLabel.setFont( font )
00227                         aggValue.setFont( font )
00228                 self.aggregatesLayout.addWidget( aggLabel )
00229                 self.aggregatesLayout.addWidget( aggValue )
00230                 self.aggregatesLayout.addSpacing( 10 )
00231                 self.aggregates.append( { 'name': name, 'widget': aggValue, 'digits': digits } )
00232 
00233         def finishAggregates(self):
00234                 self.aggregatesLayout.addSpacing( 10 )
00235                 
00236                 
00237                 
00238                 self.uiUpdateAggregates = QLabel( _('<a href="update">Update totals</a>'), self.aggregatesContainer )
00239                 self.uiUpdateAggregates.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed )
00240                 self.connect( self.uiUpdateAggregates, SIGNAL('linkActivated(QString)'), self.forceAggregatesUpdate )
00241                 self.aggregatesLayout.addWidget( self.uiUpdateAggregates )
00242                 self.aggregatesLayout.addStretch( 0 )
00243 
00244         
00245         def forceAggregatesUpdate(self, url):
00246                 QApplication.setOverrideCursor( Qt.WaitCursor )
00247                 try:
00248                         self.treeModel.group.ensureAllLoaded()
00249                         self.updateAggregates()
00250                 except Rpc.RpcException, e:
00251                         pass
00252                 QApplication.restoreOverrideCursor()
00253 
00254         
00255         
00256         
00257         
00258         def activated(self, index):
00259                 if self._readOnly:
00260                         self.emit( SIGNAL('activated()') )
00261 
00262         def currentChanged(self, current, previous):
00263                 if self.selecting:
00264                         return
00265                 self.currentRecord = self.treeModel.recordFromIndex( current )
00266                 
00267                 
00268                 self.emit( SIGNAL("currentChanged(PyQt_PyObject)"), self.currentRecord )
00269                 self.updateAggregates()
00270 
00271         def store(self):
00272                 
00273                 
00274                 
00275                 
00276                 
00277                 self.widget.openPersistentEditor( self.widget.currentIndex() )
00278                 self.widget.closePersistentEditor( self.widget.currentIndex() )
00279 
00280         def reset(self):
00281                 pass
00282 
00283         def setSelected(self, record):
00284                 if self.currentRecord == record:
00285                         return
00286                 self.currentRecord = record
00287                 idx = self.treeModel.indexFromRecord( record )
00288                 if idx != None:
00289                         self.selecting = True
00290                         self.widget.selectionModel().setCurrentIndex( idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
00291                         self.selecting = False
00292 
00293         def display(self, currentRecord, recordGroup ):
00294                 
00295                 self.treeModel.setRecordGroup( recordGroup )
00296                 if self._widgetType != 'tree':
00297                         self.treeModel.sort( 0, Qt.AscendingOrder )
00298                 self.updateAggregates()
00299                 if currentRecord:
00300                         idx = self.treeModel.indexFromRecord( currentRecord )
00301                         if idx:
00302                                 self.widget.setCurrentIndex( idx )
00303                                 self.widget.selectionModel().select( self.widget.currentIndex(), QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows )
00304 
00305         
00306         def selectFirst(self):
00307                 index = self.treeModel.index( 0, 0 )
00308                 if not index.isValid():
00309                         return
00310                 self.widget.setCurrentIndex( index )
00311                 self.widget.selectionModel().select( self.widget.currentIndex(), QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows )
00312 
00313         
00314         def selectAll(self):
00315                 start = self.treeModel.index( 0, 0 )
00316                 end = self.treeModel.index( self.treeModel.rowCount() - 1, 0 )
00317                 
00318                 
00319                 
00320                 
00321                 
00322                 self.selectFirst()
00323                 selection = QItemSelection( start, end )
00324                 self.widget.selectionModel().select( selection, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows )
00325 
00326         def selectedRecords(self):
00327                 records = []
00328                 selectedIndexes = self.widget.selectionModel().selectedRows()
00329                 for i in selectedIndexes:
00330                         record = self.treeModel.recordFromIndex( i )
00331                         
00332                         
00333                         if record:
00334                                 records.append( self.treeModel.recordFromIndex( i ) )
00335                 return records
00336 
00337         def setAllowMultipleSelection(self, value):
00338                 if value:
00339                         self.widget.setSelectionMode( QAbstractItemView.ExtendedSelection )
00340                 else:
00341                         self.widget.setSelectionMode( QAbstractItemView.SingleSelection )
00342 
00343         def setReadOnly(self, value):
00344                 
00345                 if not self.treeModel:
00346                         return
00347                 if self.treeModel.isReadOnly():
00348                         return
00349                 self._readOnly = value
00350                 if self._readOnly:
00351                         self.widget.setEditTriggers( QAbstractItemView.NoEditTriggers ) 
00352                         self.widget.setTabKeyNavigation( False )
00353                 else:
00354                         self.widget.setEditTriggers( QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.AnyKeyPressed | QAbstractItemView.SelectedClicked )
00355                         
00356                         
00357                         self.widget.setTabKeyNavigation( True )
00358         
00359         def isReadOnly(self):
00360                 return self._readOnly
00361 
00362         
00363         
00364         
00365         def updateAggregates(self):
00366                 if self.treeModel.group and self.aggregates:
00367                         if self.treeModel.group.unloadedIds():
00368                                 self.uiUpdateAggregates.show()
00369                                 calculate = False
00370                         else:
00371                                 self.uiUpdateAggregates.hide()
00372                                 calculate = True
00373                 else:
00374                         calculate = False
00375                         self.uiUpdateAggregates.hide()
00376 
00377                 for agg in self.aggregates:
00378                         
00379                         
00380                         if calculate:
00381                                 value = 0.0
00382                                 for model in self.treeModel.group:
00383                                         value += model.value(agg['name'])
00384                                 agg['widget'].setText( Numeric.floatToText( value, agg['digits'], True ) )
00385                         else:
00386                                 agg['widget'].setText( '-' )
00387 
00388         def startEditing(self):
00389                 self.widget.edit( self.widget.currentIndex() )
00390 
00391         def addOnTop(self):
00392                 return self._addOnTop
00393 
00394         def setAddOnTop(self, add):
00395                 self._addOnTop = add
00396 
00397         def viewSettings(self):
00398                 if self._widgetType in ('list','table'):
00399                         return ''
00400                 header = self.widget.header()
00401                 return str( header.saveState().toBase64() )
00402 
00403         def setViewSettings(self, settings):
00404                 if not settings or self._widgetType in ('list','table'):
00405                         return
00406                 header = self.widget.header()
00407                 header.restoreState( QByteArray.fromBase64( settings ) )
00408