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 import xml.dom.minidom
00030
00031 from Koo.Rpc import RpcProxy
00032 from Koo import Rpc
00033
00034 from Koo.Model.Group import RecordGroup
00035 from Koo.Model.Record import Record
00036 from Koo.View.ViewFactory import ViewFactory
00037
00038 from Koo.Common import Common
00039 from Koo.Common.Settings import *
00040 from Koo.Common.ViewSettings import *
00041
00042 from PyQt4.QtCore import *
00043 from PyQt4.QtGui import *
00044
00045 from Koo.Search import SearchFormWidget
00046 from Koo.Plugins import *
00047 from ToolBar import ToolBar
00048 from Action import *
00049 from ViewQueue import *
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069 class Screen(QScrollArea):
00070
00071 def __init__(self, parent=None):
00072 QScrollArea.__init__(self, parent)
00073 self.setFocusPolicy( Qt.NoFocus )
00074
00075
00076 self.setFrameShape( QFrame.NoFrame )
00077 self.setWidgetResizable( True )
00078 self.container = QWidget( self )
00079 self.setWidget( self.container )
00080
00081 self.container.show()
00082
00083 self.searchForm = SearchFormWidget(self.container)
00084 self.connect( self.searchForm, SIGNAL('search()'), self.search )
00085 self.connect( self.searchForm, SIGNAL('keyDownPressed()'), self.setFocusToView )
00086 self.searchForm.hide()
00087 self.containerView = None
00088
00089 self.toolBar = ToolBar(self)
00090 self.setToolbarVisible( False )
00091
00092 self.viewLayout = QVBoxLayout()
00093
00094 self.layout = QHBoxLayout()
00095 self.layout.setSpacing( 0 )
00096 self.layout.setContentsMargins( 0, 0, 0, 0 )
00097 self.layout.addLayout( self.viewLayout )
00098 self.layout.addWidget( self.toolBar )
00099
00100 vLay = QVBoxLayout( self.container )
00101 vLay.setContentsMargins( 0, 0, 0, 0 )
00102 vLay.addWidget( self.searchForm )
00103 vLay.addLayout( self.layout )
00104
00105
00106 self.actions = []
00107
00108 self._embedded = True
00109
00110 self.views_preload = {}
00111 self.rpc = None
00112 self.name = None
00113 self.views = {}
00114 self.fields = {}
00115 self.group = None
00116 self._currentRecordPosition = None
00117 self._currentRecord = None
00118 self._currentView = -1
00119 self._previousView = -1
00120
00121 self._viewQueue = ViewQueue()
00122 self._readOnly = False
00123
00124
00125 self._firstTimeShown = True
00126
00127 def showEvent(self, event):
00128 if self._firstTimeShown:
00129 self._firstTimeShown = False
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143 if self.group and self.group.count() and not self.currentRecord():
00144 self.setCurrentRecord( self.group.recordByIndex( 0 ) )
00145 return QScrollArea.showEvent(self, event)
00146
00147
00148 def setFocusToView(self):
00149 self.currentView().setFocus()
00150
00151 def sizeHint(self):
00152 return self.container.sizeHint()
00153
00154 def setPreloadedViews(self, views):
00155 self.views_preload = views
00156
00157 def preloadedViews(self, views):
00158 return self.views_preload
00159
00160
00161
00162
00163
00164
00165 def setupViews(self, types, ids):
00166 self._viewQueue.setup( types, ids )
00167
00168 if self.name:
00169 self.switchView()
00170
00171 def setViewIds(self, ids):
00172 self._viewQueue.setViewIds( ids )
00173
00174 if self.name:
00175 self.switchView()
00176
00177 def viewIds(self):
00178 return self._viewIds
00179
00180 def setViewTypes(self, types):
00181 self._viewQueue.setViewTypes( types )
00182
00183 if self.name:
00184 self.switchView()
00185
00186
00187
00188
00189
00190 def setEmbedded(self, value):
00191 self._embedded = value
00192 self.setToolbarVisible( not value )
00193 self.setSearchFormVisible( not value )
00194
00195
00196 def embedded(self):
00197 return self._embedded
00198
00199
00200 def setToolbarVisible(self, value):
00201 self._toolbarVisible = value
00202 self.toolBar.setVisible( value )
00203
00204
00205 def setSearchFormVisible(self, value):
00206 self._searchFormVisible = value
00207 self.searchForm.setVisible( value )
00208 if value:
00209 if self.currentView() and self.currentView().showsMultipleRecords():
00210 self.loadSearchForm()
00211
00212 def loadSearchForm(self):
00213 if self.currentView().showsMultipleRecords() and not self._embedded:
00214 if not self.searchForm.isLoaded():
00215 form = Rpc.session.execute('/object', 'execute', self.resource, 'fields_view_get', False, 'form', self.context)
00216 tree = Rpc.session.execute('/object', 'execute', self.resource, 'fields_view_get', False, 'tree', self.context)
00217 fields = form['fields']
00218 fields.update( tree['fields'] )
00219 arch = form['arch']
00220
00221
00222
00223
00224
00225 dom = xml.dom.minidom.parseString(tree['arch'].encode('utf-8'))
00226 children = dom.childNodes[0].childNodes
00227 tempArch = ''
00228 for i in range(1,len(children)):
00229 tempArch += children[i].toxml()
00230
00231 arch = arch[0:form['arch'].rfind('</form>')]
00232
00233 index = arch.rfind('</form>')
00234 if index > 0:
00235 arch = arch[0:index]
00236 arch = arch + tempArch + '\n</form>'
00237
00238 self.searchForm.setup( arch, fields, self.resource, self.group.domain() )
00239
00240 if self.searchForm.isEmpty():
00241 self.searchForm.hide()
00242 else:
00243 self.searchForm.show()
00244 else:
00245 self.searchForm.hide()
00246
00247 def setReadOnly(self, value):
00248 self._readOnly = value
00249 self.display()
00250
00251 def isReadOnly(self):
00252 return self._readOnly
00253
00254
00255
00256 def triggerAction(self):
00257
00258 action = self.sender()
00259
00260
00261
00262
00263 if self.isModified():
00264 if not self.save():
00265 return
00266
00267
00268
00269 if not self.currentId() and action.type() != 'plugin':
00270 return
00271
00272 id = self.currentId()
00273 ids = self.selectedIds()
00274
00275 if action.type() != 'relate':
00276 self.save()
00277 self.display()
00278
00279 context = self.context.copy()
00280 if self.currentRecord():
00281
00282 context.update( self.currentRecord().get() )
00283
00284 action.execute( id, ids, context )
00285
00286 if action.type() != 'relate':
00287 self.reload()
00288
00289
00290 def setView(self, widget):
00291 if self.containerView:
00292 self.disconnect(self.containerView, SIGNAL("activated()"), self.activate )
00293 self.disconnect(self.containerView, SIGNAL("currentChanged(PyQt_PyObject)"), self.currentChanged)
00294 self.disconnect(self.containerView, SIGNAL("statusMessage(QString)"), self, SIGNAL("statusMessage(QString)") )
00295 self.containerView.hide()
00296
00297 self.containerView = widget
00298
00299
00300
00301 self.loadSearchForm()
00302 self.containerView.show()
00303 self.connect(widget, SIGNAL("activated()"), self.activate )
00304 self.connect(widget, SIGNAL("currentChanged(PyQt_PyObject)"), self.currentChanged)
00305 self.connect(widget, SIGNAL("statusMessage(QString)"), self, SIGNAL("statusMessage(QString)") )
00306
00307
00308
00309 self.setFocusProxy( self.containerView )
00310
00311 self.ensureWidgetVisible( widget )
00312 self.updateGeometry()
00313
00314 def activate( self ):
00315 self.emit( SIGNAL('activated()') )
00316
00317 def close( self ):
00318 self.emit( SIGNAL('closed()') )
00319
00320
00321
00322 def search( self ):
00323 QApplication.setOverrideCursor( Qt.WaitCursor )
00324 try:
00325 value = self.searchForm.value()
00326 self.group.setFilter( value )
00327
00328
00329
00330 self.setCurrentRecord( None )
00331 self.group.update()
00332 if self.group.count() > 0:
00333 self.setCurrentRecord( self.group.recordByIndex( 0 ) )
00334 else:
00335 self.setCurrentRecord( None )
00336 self.display()
00337 except Rpc.RpcException, e:
00338 pass
00339 QApplication.restoreOverrideCursor()
00340
00341 def updateSearchFormStatus(self):
00342
00343
00344 if self.group.isModified():
00345 self.searchForm.setEnabled( False )
00346 else:
00347 self.searchForm.setEnabled( True )
00348
00349
00350 def currentChanged(self, model):
00351 self.setCurrentRecord( model )
00352 self.emit( SIGNAL('currentChanged()') )
00353 self.updateSearchFormStatus()
00354
00355
00356
00357 def setRecordGroup(self, group):
00358 if not group:
00359 self.group = None
00360 self._currentRecord = None
00361 self._currentRecordPosition = None
00362
00363
00364
00365 self.setCurrentRecord( None )
00366 return
00367
00368 self.name = group.resource
00369 self.resource = group.resource
00370 self.context = group.context()
00371 self.rpc = RpcProxy(self.resource)
00372
00373 self.group = group
00374 self._currentRecord = None
00375 self._currentRecordPosition = None
00376
00377 group.addFields(self.fields)
00378 self.fields.update(group.fields)
00379
00380 if self.isVisible():
00381 if self.group and self.group.count() and not self.currentRecord():
00382 self.setCurrentRecord( self.group.recordByIndex( 0 ) )
00383 else:
00384
00385
00386 self.setCurrentRecord( None )
00387
00388 self._firstTimeShown = False
00389 else:
00390 self._firstTimeShown = True
00391
00392
00393 def currentRecord(self):
00394
00395
00396
00397
00398
00399 if self._currentRecordPosition >= 0 and self.group.count():
00400
00401
00402 if self._currentRecordPosition >= self.group.count():
00403 self.setCurrentRecord( None )
00404 return None
00405
00406
00407
00408 return self.group.modelByIndex( self._currentRecordPosition )
00409 else:
00410
00411
00412 return self._currentRecord
00413
00414
00415
00416
00417 def setCurrentRecord(self, value):
00418 if self.group and self.group.recordExists( value ):
00419 pos = self.group.indexOfRecord( value )
00420 else:
00421 pos = -1
00422
00423
00424
00425
00426
00427
00428 if self._currentRecordPosition == pos:
00429 return
00430 self._currentRecord = value
00431 self._currentRecordPosition = pos
00432 if value and value.id:
00433 id = value.id
00434 else:
00435 id = -1
00436 if self.group:
00437 count = self.group.count()
00438 else:
00439 count = 0
00440 self.emit(SIGNAL('recordMessage(int,int,int)'), pos, count, id)
00441 if self._currentRecord:
00442 if self.currentView():
00443 self.currentView().setSelected( self._currentRecord )
00444
00445
00446
00447 def switchView(self, viewType = None):
00448 if self.currentView():
00449 self.currentView().store()
00450
00451 if self.currentRecord() and ( not self.group.recordExists( self.currentRecord() ) ):
00452 self.setCurrentRecord( None )
00453
00454 if viewType:
00455 self._previousView = self._currentView
00456 self._currentView = self._viewQueue.indexFromType( viewType )
00457 else:
00458 if self._currentView >= 0:
00459
00460 self._currentView, self._previousView = self._previousView, self._currentView
00461 else:
00462 self._currentView = 0
00463
00464
00465 self._currentView = abs(self._currentView)
00466
00467 self._currentView = min(self._currentView, self._viewQueue.count()-1)
00468
00469
00470
00471
00472
00473 if self.currentView().showsMultipleRecords():
00474 self.group.setLoadOneByOne( False )
00475 else:
00476 self.group.setLoadOneByOne( True )
00477
00478 self.setView( self.currentView() )
00479 if self.currentRecord():
00480 self.currentRecord().setValidate()
00481 self.display()
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492 def addViewByIdAndType(self, id, type, display=False):
00493 if type in self.views_preload:
00494 return self.addView( self.views_preload[type]['arch'], self.views_preload[type]['fields'], display, toolbar=self.views_preload[type].get('toolbar', False), id=self.views_preload[type].get('view_id',False) )
00495 else:
00496
00497
00498
00499 view = self.rpc.fields_view_get(id, type, self.context, True)
00500 return self.addView( view['arch'], view['fields'], display, toolbar=view.get('toolbar', False), id=view.get('view_id',False) )
00501
00502
00503
00504
00505
00506
00507
00508
00509 def addViewById(self, id, display=False):
00510
00511
00512
00513 view = self.rpc.fields_view_get(id, False, self.context, True)
00514 return self.addView(view['arch'], view['fields'], display, toolbar=view.get('toolbar', False), id=id)
00515
00516
00517
00518
00519
00520
00521
00522
00523 def addViewByType(self, type, display=False):
00524 if type in self.views_preload:
00525 return self.addView( self.views_preload[type]['arch'], self.views_preload[type]['fields'], display, toolbar=self.views_preload[type].get('toolbar', False), id=self.views_preload[type].get('view_id',False) )
00526 else:
00527
00528
00529
00530 view = self.rpc.fields_view_get(False, type, self.context, True)
00531 return self.addView( view['arch'], view['fields'], display, toolbar=view.get('toolbar', False), id=view.get('view_id',False) )
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541 def addView(self, arch, fields, display=False, toolbar=None, id=False):
00542 if toolbar is None:
00543 toolbar = {}
00544 def _parse_fields(node, fields):
00545 if node.nodeType == node.ELEMENT_NODE:
00546 if node.localName=='field':
00547 attrs = Common.nodeAttributes(node)
00548 if attrs.get('widget', False):
00549 if attrs['widget']=='one2many_list':
00550 attrs['widget']='one2many'
00551 attrs['type'] = attrs['widget']
00552 try:
00553 fields[attrs['name']].update(attrs)
00554 except:
00555 print "-"*30,"\n malformed tag for :", attrs
00556 print "-"*30
00557 raise
00558 for node2 in node.childNodes:
00559 _parse_fields(node2, fields)
00560 dom = xml.dom.minidom.parseString(arch.encode('utf-8'))
00561 _parse_fields(dom, fields)
00562
00563 self.group.addFields(fields)
00564
00565 self.fields = self.group.fields
00566
00567 dom = xml.dom.minidom.parseString(arch.encode('utf-8'))
00568 view = ViewFactory.create(id, self, self.resource, dom, self.fields)
00569 self.viewLayout.addWidget( view )
00570 self.setOnWriteFunction( view.onWriteFunction() )
00571
00572 if not self.group.updated:
00573 domain = self.group.domain()
00574 self.group.setDomainForEmptyGroup()
00575 view.setViewSettings( ViewSettings.load( view.id ) )
00576 self.group.setDomain( domain )
00577 else:
00578 view.setViewSettings( ViewSettings.load( view.id ) )
00579
00580 self.views[ view.viewType() ] = view
00581 self.loadActions( toolbar )
00582
00583 if display:
00584 if not self._viewQueue.typeExists( view.viewType() ):
00585 self._viewQueue.addViewType( view.viewType() )
00586 self._currentView = self._viewQueue.indexFromType( view.viewType() )
00587 self.currentView().display(self.currentRecord(), self.group)
00588 self.setView(view)
00589 return view
00590
00591
00592 def loadActions( self, actions ):
00593 self.actions = ActionFactory.create( self, actions, self.resource )
00594 if self.actions:
00595 for action in self.actions:
00596 self.connect( action, SIGNAL('triggered()'), self.triggerAction )
00597
00598
00599
00600
00601
00602
00603
00604 if len(self.actions) > 1 + len(Plugins.list(self.resource)) and Settings.value('koo.show_toolbar') and self._toolbarVisible:
00605 self.toolBar.setup( self.actions )
00606 self.toolBar.show()
00607 else:
00608 self.toolBar.hide()
00609
00610
00611
00612 def new(self, default=True, context=None):
00613 if context is None:
00614 context = {}
00615
00616 if self.currentView() and self.currentView().showsMultipleRecords() \
00617 and self.currentView().isReadOnly():
00618 self.switchView( 'form' )
00619
00620 ctx = self.context.copy()
00621 ctx.update( context )
00622 record = self.group.create( default, self.newRecordPosition(), self.group.domain(), ctx )
00623
00624 if self.currentView():
00625 self.currentView().reset()
00626
00627 self.setCurrentRecord( record )
00628 self.display()
00629
00630 if self.currentView():
00631 self.currentView().startEditing()
00632 return self.currentRecord()
00633
00634
00635
00636 def newRecordPosition(self):
00637 if self.currentView() and self.currentView().addOnTop():
00638 return 0
00639 else:
00640 return -1
00641
00642
00643
00644
00645
00646 def addOnTop(self):
00647 if self.currentView():
00648 return self.currentView().addOnTop()
00649 else:
00650 return False
00651
00652
00653
00654 def setOnWriteFunction(self, functionName):
00655 self.group.setOnWriteFunction( functionName )
00656
00657
00658 def save(self):
00659 if not self.currentRecord():
00660 return False
00661 self.currentView().store()
00662
00663 id = False
00664 if self.currentRecord().validate():
00665 id = self.currentRecord().save(reload=True)
00666 else:
00667 self.currentView().display(self.currentRecord(), self.group)
00668 return False
00669
00670 if self.currentView().showsMultipleRecords():
00671 for record in self.group.modifiedRecords():
00672 if record.validate():
00673 id = record.save(reload=True)
00674 else:
00675 self.setCurrentRecord( record )
00676 self.display()
00677 return False
00678 self.display()
00679
00680 self.display()
00681 return id
00682
00683
00684
00685
00686
00687 def reload(self):
00688 if not self.currentView().showsMultipleRecords():
00689 if self.currentRecord():
00690 self.currentRecord().reload()
00691 self.display()
00692 return
00693
00694 id = 0
00695 idx = -1
00696 if self.currentRecord():
00697 id = self.currentId()
00698 idx = self.group.indexOfRecord(self.currentRecord())
00699
00700
00701
00702
00703
00704
00705 self.cancel()
00706 self.group.update()
00707 if id:
00708 record = self.group.modelById( id )
00709
00710 if record:
00711 self.setCurrentRecord( record )
00712 else:
00713
00714
00715 idx = min( idx, self.group.count() - 1 )
00716 if idx >= 0:
00717 self.setCurrentRecord( self.group.recordByIndex( idx ) )
00718 else:
00719 self.setCurrentRecord( None )
00720 else:
00721 self.setCurrentRecord( None )
00722 self.display()
00723
00724
00725 def cancel(self):
00726 idx = -1
00727 if self.currentRecord():
00728 idx = self.group.indexOfRecord( self.currentRecord() )
00729
00730 self.group.cancel()
00731 if idx != -1:
00732 idx = min( idx, self.group.count() - 1 )
00733 if idx >= 0:
00734 self.setCurrentRecord( self.group.recordByIndex( idx ) )
00735 else:
00736 self.setCurrentRecord( None )
00737 self.display()
00738
00739
00740 def currentView(self):
00741 if self._currentView < 0:
00742 return None
00743 type = self._viewQueue.typeFromIndex( self._currentView )
00744 if not type in self.views:
00745 (id, type) = self._viewQueue.viewFromType( type )
00746 self.addViewByIdAndType( id, type )
00747 return self.views[ type ]
00748
00749
00750 def get(self):
00751 if not self.currentRecord():
00752 return None
00753 self.currentView().store()
00754 return self.currentRecord().get()
00755
00756
00757 def isModified(self):
00758 if not self.currentRecord():
00759 return False
00760 self.currentView().store()
00761 res = False
00762 if self.currentView().showsMultipleRecords():
00763 return self.group.isModified()
00764 else:
00765 return self.currentRecord().isModified()
00766
00767
00768
00769
00770
00771 def remove(self, unlink = False):
00772 records = self.selectedRecords()
00773 if unlink and records:
00774
00775
00776 idsToUnlink = [x.id for x in records if x.id != None]
00777
00778
00779
00780 if idsToUnlink:
00781 unlinked = self.rpc.unlink( idsToUnlink )
00782
00783
00784
00785
00786
00787 if not unlinked:
00788 return False
00789
00790 if records:
00791
00792
00793
00794
00795 idx = self._currentRecordPosition
00796 self.setCurrentRecord( None )
00797 self.group.remove( records )
00798 if self.group.count():
00799 idx = min(idx, self.group.count() - 1)
00800 self.setCurrentRecord( self.group.recordByIndex( idx ) )
00801 else:
00802 self.setCurrentRecord( None )
00803 self.display()
00804 if records:
00805 return True
00806 else:
00807 return False
00808
00809
00810 def load(self, ids, addOnTop=False):
00811 self.currentView().reset()
00812 self.group.load( ids, addOnTop )
00813 if ids:
00814 self.display(ids[0])
00815 else:
00816 self.setCurrentRecord( None )
00817 self.display()
00818
00819
00820
00821 def display(self, id=None):
00822 if id:
00823 self.setCurrentRecord( self.group[id] )
00824 if self.views:
00825 self.currentView().setReadOnly( self.isReadOnly() )
00826 self.currentView().display(self.currentRecord(), self.group)
00827
00828
00829
00830 def displayNext(self):
00831 self.currentView().store()
00832 if self.group.recordExists( self.currentRecord() ):
00833 idx = self.group.indexOfRecord(self.currentRecord())
00834 idx = (idx+1) % self.group.count()
00835 self.setCurrentRecord( self.group.modelByIndex(idx) )
00836 else:
00837 self.setCurrentRecord( self.group.count() and self.group.modelByIndex(0) or None )
00838 if self.currentRecord():
00839 self.currentRecord().setValidate()
00840 self.display()
00841
00842
00843
00844 def displayPrevious(self):
00845 self.currentView().store()
00846 if self.group.recordExists( self.currentRecord() ):
00847
00848 idx = self.group.indexOfRecord(self.currentRecord())-1
00849 if idx<0:
00850 idx = self.group.count()-1
00851 self.setCurrentRecord( self.group.modelByIndex(idx) )
00852 else:
00853 self.setCurrentRecord( self.group.count() and self.group.modelByIndex(-1) or None )
00854
00855 if self.currentRecord():
00856 self.currentRecord().setValidate()
00857 self.display()
00858
00859
00860
00861
00862
00863
00864
00865 def selectedIds(self):
00866 records = self.currentView().selectedRecords()
00867 ids = [record.id for record in records]
00868 return ids
00869
00870
00871 def selectedRecords(self):
00872 return self.currentView().selectedRecords()
00873
00874
00875 def currentId(self):
00876 if self.currentRecord():
00877 return self.currentRecord().id
00878 else:
00879 return None
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889 def clear(self):
00890 self.group.clear()
00891 self.display()
00892
00893
00894 def storeViewSettings(self):
00895 for view in self.views.values():
00896 ViewSettings.store( view.id, view.viewSettings() )
00897
00898