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 types
00030 import gettext
00031 
00032 from Koo import Rpc
00033 from SearchDialog import *
00034 from ExportDialog import *
00035 from ImportDialog import *
00036 from AttachmentDialog import *
00037 from GoToIdDialog import *
00038 from MassiveUpdateDialog import *
00039 
00040 from Koo.Common import Api
00041 from Koo.Common import Common
00042 from Koo.Common.Settings import *
00043 from Koo.Common import Help
00044 import copy
00045 
00046 from Koo.Screen.Screen import *
00047 from Koo.Model.Group import RecordGroup
00048 from PyQt4.QtCore import *
00049 from PyQt4.QtGui import *
00050 from PyQt4.uic import *
00051 
00052 (FormWidgetUi, FormWidgetBase) = loadUiType( Common.uiPath('formcontainer.ui') )
00053 
00054 class FormWidget( QWidget, FormWidgetUi ):
00055         
00056         
00057         
00058         
00059         
00060         
00061         
00062         
00063         
00064         def __init__(self, model, res_id=False, domain=None, view_type=None, view_ids=None, context=None, parent=None, name=False):
00065                 QWidget.__init__(self,parent)
00066                 FormWidgetUi.__init__(self)
00067                 self.setupUi( self )
00068 
00069                 if domain is None:
00070                         domain = []
00071                 if view_ids is None:
00072                         view_ids = []
00073                 if context is None:
00074                         context = {}
00075 
00076                 
00077                 
00078                 
00079                 self.previousId = False
00080                 self.previousAttachments = False
00081 
00082                 
00083                 
00084                 if view_type:
00085                         new_view_ids = []
00086                         new_view_type = []
00087                         for i in xrange(len(view_type)):
00088                                 if not view_type[i] in new_view_type:
00089                                         if i < len(view_ids):
00090                                                 new_view_ids.append( view_ids[i] )
00091                                         new_view_type.append( view_type[i] )
00092                         view_ids = new_view_ids
00093                         view_type = new_view_type
00094 
00095                 if not view_type:
00096                         view_type = ['form','tree']
00097                 else:
00098                         if view_type[0] in ['graph'] and not res_id:
00099                                 res_id = Rpc.session.execute('/object', 'execute', model, 'search', domain)
00100                 fields = {}
00101                 self.model = model
00102                 self.previousAction = None
00103                 self.fields = fields
00104                 self.domain = domain
00105                 self.context = context
00106                 self.viewTypes = view_type
00107                 self.viewIds = view_ids
00108 
00109                 self._switchViewMenu = QMenu( self )
00110                 self._viewActionGroup = QActionGroup( self )
00111                 self._viewActionGroup.setExclusive( True )
00112                 for view in self.viewTypes:
00113                         action = ViewFactory.viewAction( view, self )
00114                         if not action:
00115                                 continue
00116                         self.connect( action, SIGNAL('triggered()'), self.switchView )
00117                         self._switchViewMenu.addAction( action )
00118                         self._viewActionGroup.addAction( action )
00119 
00120                 self.group = RecordGroup( self.model, context=self.context )
00121                 if Settings.value('koo.sort_mode') == 'visible_items':
00122                         self.group.setSortMode( RecordGroup.SortVisibleItems )
00123                 self.group.setDomain( domain )
00124                 self.connect(self.group, SIGNAL('modified'), self.notifyRecordModified)
00125 
00126                 self.screen.setRecordGroup( self.group )
00127                 self.screen.setEmbedded( False )
00128                 self.connect( self.screen, SIGNAL('activated()'), self.switchToForm )
00129                 self.connect( self.screen, SIGNAL('currentChanged()'), self.updateStatus )
00130                 self.connect( self.screen, SIGNAL('closed()'), self.closeWidget )
00131                 self.connect( self.screen, SIGNAL('recordMessage(int,int,int)'), self.updateRecordStatus )
00132                 self.connect( self.screen, SIGNAL('statusMessage(QString)'), self.updateStatus )
00133 
00134                 self._allowOpenInNewWindow = True
00135 
00136                 
00137                 self.screen.setupViews( view_type, view_ids )
00138 
00139                 if name:
00140                         self.name = name
00141                 else:
00142                         self.name = self.screen.currentView().title
00143 
00144                 self.handlers = {
00145                         'New': self.new,
00146                         'Save': self.save,
00147                         'Export': self.export,
00148                         'Import': self.import_,
00149                         'Delete': self.remove,
00150                         'Find': self.search,
00151                         'Previous': self.previous,
00152                         'Next':  self.next,
00153                         'GoToResourceId':  self.goto,
00154                         'AccessLog':  self.showLogs,
00155                         'Reload': self.reload,
00156                         'Switch': self.switchView,
00157                         'Attach': self.showAttachments,
00158                         'Duplicate': self.duplicate,
00159                         'MassiveUpdate': self.massiveUpdate,
00160                         'StoreViewSettings': self.storeViewSettings,
00161                 }
00162 
00163                 if res_id:
00164                         if isinstance(res_id, int):
00165                                 res_id = [res_id]
00166                         self.screen.load(res_id)
00167                 else:
00168                         if len(view_type) and view_type[0]=='form':
00169                                 self.screen.new()
00170 
00171                 self.updateSwitchView()
00172 
00173                 self.reloadTimer = QTimer(self)
00174                 self.connect( self.reloadTimer, SIGNAL('timeout()'), self.autoReload )
00175                 self.pendingReload = False
00176 
00177                 
00178                 
00179                 self.subscriber = Rpc.Subscriber(Rpc.session, self)
00180                 if Settings.value('koo.auto_reload'):
00181                         self.subscriber.subscribe( 'updated_model:%s' % model, self.autoReload )
00182 
00183         def notifyRecordModified(self):
00184                 self.updateStatus( _('<font color="blue">Document modified</font>') )
00185 
00186         
00187         
00188         
00189         def setAutoReload(self, value):
00190                 if value:
00191                         
00192                         
00193                         
00194                         
00195                         
00196                         if value > 0:
00197                                 self.reloadTimer.start( int(value) * 1000 )
00198                         if not Settings.value('koo.auto_reload'):
00199                                 
00200                                 self.subscriber.subscribe( 'updated_model:%s' % self.model, self.autoReload )
00201                 else:
00202                         self.reloadTimer.stop()
00203 
00204         def setAllowOpenInNewWindow( self, value ):
00205                 self._allowOpenInNewWindow = value
00206 
00207         def goto(self):
00208                 if not self.modifiedSave():
00209                         return
00210                 dialog = GoToIdDialog( self )
00211                 if dialog.exec_() == QDialog.Rejected:
00212                         return
00213                 if not dialog.result in self.group.ids():
00214                         QMessageBox.information(self, _('Go To Id'), _("Resouce with ID '%s' not found.") % dialog.result )
00215                         return
00216                 self.screen.load( [dialog.result] )
00217                 
00218         def setStatusBarVisible(self, value):
00219                 self.uiStatusLabel.setVisible( value )
00220                 self.uiStatus.setVisible( value )
00221                 self.uiRecordStatus.setVisible( value )
00222                 
00223         def showAttachments(self):
00224                 id = self.screen.currentId()
00225                 if id:
00226                         if Settings.value('koo.attachments_dialog'):
00227                                 QApplication.setOverrideCursor( Qt.WaitCursor )
00228                                 try:
00229                                         window = AttachmentDialog(self.model, id, self)
00230                                         self.connect( window, SIGNAL('destroyed()'), self.attachmentsClosed )
00231                                 except Rpc.RpcException, e:
00232                                         QApplication.restoreOverrideCursor()
00233                                         return
00234                                 QApplication.restoreOverrideCursor()
00235                                 window.show()
00236                         else:
00237                                 context = self.context.copy()
00238                                 context.update(Rpc.session.context)
00239                                 action = Rpc.session.execute('/object', 'execute', 'ir.attachment', 'action_get', context)
00240                                 action['domain'] = [('res_model', '=', self.model), ('res_id', '=', id)]
00241                                 context['default_res_model'] = self.model
00242                                 context['default_res_id'] = id
00243                                 Api.instance.executeAction( action, {}, context )
00244                 else:
00245                         self.updateStatus(_('No resource selected !'))
00246 
00247         def attachmentsClosed(self):
00248                 self.updateStatus()
00249 
00250         def switchToForm(self):
00251                 if 'form' in self.viewTypes:
00252                         self.switchView( 'form' )
00253                 else:
00254                         self.switchView()
00255 
00256         def switchView(self, viewType=None):
00257                 if not self.modifiedSave():
00258                         return
00259                 QApplication.setOverrideCursor( Qt.WaitCursor )
00260                 try:
00261                         if ( self._allowOpenInNewWindow and QApplication.keyboardModifiers() & Qt.ControlModifier ) == Qt.ControlModifier:
00262                                 if QApplication.keyboardModifiers() & Qt.ShiftModifier:
00263                                         target = 'background'
00264                                 else:
00265                                         target = 'current'
00266                                 Api.instance.createWindow( None, self.model, [self.screen.currentId()], 
00267                                         view_type='form', mode='form,tree', target=target)
00268                         else:
00269                                 sender = self.sender()
00270                                 name = unicode( sender.objectName()  )
00271                                 if isinstance(sender, QAction) and name != 'actionSwitch':
00272                                         self.sender().setChecked( True )
00273                                         self.screen.switchView( name )
00274                                 else:
00275                                         self.screen.switchView( viewType )
00276                         if self.pendingReload:
00277                                 self.reload()
00278                         self.updateSwitchView()
00279                 except Rpc.RpcException, e:
00280                         pass
00281                 QApplication.restoreOverrideCursor()
00282 
00283         def showLogs(self):
00284                 id = self.screen.currentId()
00285                 if not id:
00286                         self.updateStatus(_('You have to select one resource!'))
00287                         return False
00288                 res = Rpc.session.execute('/object', 'execute', self.model, 'perm_read', [id])
00289                 message = ''
00290                 for line in res:
00291                         todo = [
00292                                 ('id', _('ID')),
00293                                 ('create_uid', _('Creation User')),
00294                                 ('create_date', _('Creation Date')),
00295                                 ('write_uid', _('Latest Modification by')),
00296                                 ('write_date', _('Latest Modification Date')),
00297                         ]
00298                         for (key,val) in todo:
00299                                 if line[key] and key in ('create_uid','write_uid'):
00300                                         line[key] = line[key][1]
00301                                 message += val + ': ' + str(line[key] or '-') + '\n'
00302                 QMessageBox.information(self, _('Record log'), message)
00303 
00304         def remove(self):
00305                 value = QMessageBox.question(self,_('Question'),_('Are you sure you want to remove these records?'), _("Yes"), _("No"))
00306                 if value == 0:
00307                         QApplication.setOverrideCursor( Qt.WaitCursor )
00308                         try: 
00309                                 if not self.screen.remove(unlink=True):
00310                                         self.updateStatus(_('Resource not removed !'))
00311                                 else:
00312                                         self.updateStatus(_('Resource removed.'))
00313                         except Rpc.RpcException, e:
00314                                 pass
00315                         QApplication.restoreOverrideCursor()
00316 
00317         def import_(self):
00318                 dialog = ImportDialog(self)
00319                 dialog.setModel( self.model )
00320                 dialog.setup( self.viewTypes, self.viewIds )
00321                 dialog.exec_()
00322                 if not self.screen.isModified():
00323                         self.reload()
00324 
00325         def export(self):
00326                 dialog = ExportDialog(self)
00327                 dialog.setModel( self.model )
00328                 dialog.setIds( self.screen.selectedIds() )
00329                 dialog.setup( self.viewTypes, self.viewIds )
00330                 dialog.exec_()
00331 
00332         def new(self):
00333                 if not self.modifiedSave():
00334                         return
00335                 self.screen.new()
00336         
00337         def duplicate(self):
00338                 if not self.modifiedSave():
00339                         return
00340                 res_id = self.screen.currentId()
00341                 new_id = Rpc.session.execute('/object', 'execute', self.model, 'copy', res_id, {}, Rpc.session.context)
00342                 self.screen.load( [new_id], self.screen.addOnTop() )
00343                 self.updateStatus(_('<font color="orange">Working now on the duplicated document</font>'))
00344 
00345         def save(self):
00346                 if not self.screen.currentRecord():
00347                         return
00348                 QApplication.setOverrideCursor( Qt.WaitCursor )
00349                 try:
00350                         modification = self.screen.currentRecord().id
00351                         id = self.screen.save()
00352                         if id:
00353                                 self.updateStatus(_('<font color="green">Document saved</font>'))
00354                                 if not modification and Settings.value('koo.auto_new'):
00355                                         self.screen.new()
00356                                 QApplication.restoreOverrideCursor()
00357                         else:
00358                                 self.updateStatus(_('<font color="red">Invalid form</font>'))
00359 
00360                                 QApplication.restoreOverrideCursor()
00361                                 record = self.screen.currentRecord()
00362                                 fields = []
00363                                 for field in record.invalidFields:
00364                                         attrs = record.fields()[ field ].attrs
00365                                         if 'string' in attrs:
00366                                                 name = attrs['string']
00367                                         else:
00368                                                 name = field
00369                                         fields.append( '<li>%s</li>' % name )
00370                                 fields.sort()
00371                                 fields = '<ul>%s</ul>' % ''.join( fields )
00372                                 value = QMessageBox.question( self, _('Error'), 
00373                                         _('<p>The following fields have an invalid value and have been highlighted in red:</p>%s<p>Please fix them before saving.</p>') % fields, 
00374                                         _('Ok') )
00375                 except Rpc.RpcException, e:
00376                         QApplication.restoreOverrideCursor()
00377                         id = False
00378                 return bool(id)
00379 
00380         def previous(self):
00381                 if not self.modifiedSave():
00382                         return
00383                 QApplication.setOverrideCursor( Qt.WaitCursor )
00384                 try:
00385                         self.screen.displayPrevious()
00386                         self.updateStatus()
00387                 except Rpc.RpcException, e:
00388                         pass
00389                 QApplication.restoreOverrideCursor()
00390 
00391         def next(self):
00392                 if not self.modifiedSave():
00393                         return
00394                 QApplication.setOverrideCursor( Qt.WaitCursor )
00395                 try:
00396                         self.screen.displayNext()
00397                         self.updateStatus()
00398                 except Rpc.RpcException, e:
00399                         pass
00400                 QApplication.restoreOverrideCursor()
00401 
00402         def autoReload(self):
00403                 
00404                 
00405                 
00406                 if self.screen.currentView().showsMultipleRecords() and not self.screen.currentView().isReadOnly():
00407                         return
00408                 
00409                 
00410                 
00411                 if self.screen.isModified():
00412                         self.pendingReload = True
00413                         return
00414                 self.reload()
00415                 
00416                 
00417                 
00418                 
00419                 
00420                 if not self.screen.currentView().showsMultipleRecords():
00421                         self.pendingReload = True
00422 
00423         def reload(self):
00424                 if not self.modifiedSave():
00425                         return
00426                 QApplication.setOverrideCursor( Qt.WaitCursor )
00427                 try:
00428                         
00429                         self.previousId = False
00430                         self.screen.reload()
00431                         self.updateStatus()
00432                         self.pendingReload = False
00433                 except Rpc.RpcException, e:
00434                         pass
00435                 QApplication.restoreOverrideCursor()
00436 
00437         def cancel(self):
00438                 QApplication.setOverrideCursor( Qt.WaitCursor )
00439                 try:
00440                         self.screen.cancel()
00441                         self.updateStatus()
00442                 except Rpc.RpcException, e:
00443                         pass
00444                 QApplication.restoreOverrideCursor()
00445 
00446         def search(self):
00447                 if not self.modifiedSave():
00448                         return
00449                 dom = self.domain
00450                 dialog = SearchDialog(self.model, domain=self.domain, context=self.context, parent=self)
00451                 if dialog.exec_() == QDialog.Rejected:
00452                         return
00453                 self.screen.clear()
00454                 self.screen.load( dialog.result )
00455 
00456         def updateStatus(self, message=''):
00457                 if self.model and self.screen.currentRecord() and self.screen.currentRecord().id:
00458                         
00459                         
00460                         id = self.screen.currentRecord().id
00461                         if id != self.previousId:
00462                                 ids = Rpc.session.execute('/object', 'execute', 'ir.attachment', 'search', [('res_model','=',self.model),('res_id','=',id)])
00463                                 self.previousAttachments = ids
00464                                 self.previousId = id
00465                         else:
00466                                 ids = self.previousAttachments
00467                 else:
00468                         ids = []
00469                         self.previousId = False
00470                         self.previousAttachments = ids
00471                 message = ( _("(%s attachments) ") % len(ids) ) + message
00472                 self.uiStatus.setText( message )
00473 
00474         def updateSwitchView(self):
00475                 for action in self._viewActionGroup.actions():
00476                         if action.objectName() == self.screen.currentView().viewType():
00477                                 action.setChecked( True )
00478                                 break
00479 
00480         def updateRecordStatus(self, position, count, value):
00481                 if not count:
00482                         msg = _('No records')
00483                 else:
00484                         pos = '_'
00485                         if position >= 0:
00486                                 pos = str(position+1)
00487                         if value == None:
00488                                 
00489                                 edit = _('No document selected')
00490                         else:
00491                                 
00492                                 
00493                                 edit = _('New document')
00494                                 if value > 0:
00495                                         edit = _('Editing document (id: %s)') % str(value)
00496                         msg = _('Record: %(name)s / %(count)s - %(name2)s') % { 'name': pos, 'count': str(count), 'name2': edit }
00497 
00498                 self.uiRecordStatus.setText( msg )
00499 
00500         def modifiedSave(self):
00501                 if self.screen.isModified():
00502                         record = self.screen.currentRecord()
00503                         fields = []
00504                         for field in record.modifiedFields():
00505                                 attrs = record.fields()[ field ].attrs
00506                                 if 'string' in attrs:
00507                                         name = attrs['string']
00508                                 else:
00509                                         name = field
00510                                 fields.append( '<li>%s</li>' % name )
00511                         fields.sort()
00512                         fields = '<ul>%s</ul>' % ''.join( fields )
00513                         value = QMessageBox.question( self, _('Question'), 
00514                                 _('<p>You have modified the following fields in current record:</p>%s<p>Do you want to save the changes?</p>') % fields, 
00515                                 _('Save'), _('Discard'), _('Cancel'), 2, 2 )
00516                         if value == 0:
00517                                 return self.save()
00518                         elif value == 1:
00519                                 self.cancel()
00520                                 return True
00521                         else:
00522                                 return False
00523                 else:
00524                         
00525                         
00526                         
00527                         
00528                         self.screen.cancel()
00529                 return True
00530 
00531         def massiveUpdate(self):
00532                 if not self.screen.selectedIds():
00533                         QMessageBox.information( self, _('No records selected'), _('No records selected') )
00534                         return
00535                 dialog = MassiveUpdateDialog( self )
00536                 dialog.setIds( self.screen.selectedIds() )
00537                 dialog.setModel( self.model )
00538                 dialog.setContext( self.context )
00539                 dialog.setup( self.viewTypes, self.viewIds )
00540                 dialog.exec_()
00541                 self.reload()
00542 
00543         def storeViewSettings(self):
00544                 self.screen.storeViewSettings()
00545 
00546         def closeWidget(self):
00547                 self.screen.storeViewSettings()
00548                 self.reloadTimer.stop()
00549                 self.subscriber.unsubscribe()
00550                 self.emit( SIGNAL('closed()') )
00551 
00552         def canClose(self, urgent=False):
00553                 
00554                 self.screen.storeViewSettings()
00555                 if self.modifiedSave():
00556                         
00557                         
00558                         
00559                         self.reloadTimer.stop()
00560                         self.subscriber.unsubscribe()
00561                         return True
00562                 else:
00563                         return False
00564 
00565         def actions(self):
00566                 return self.screen.actions
00567 
00568         def switchViewMenu(self):
00569                 return self._switchViewMenu
00570 
00571         def help(self, button):
00572                 QApplication.setOverrideCursor( Qt.WaitCursor )
00573                 helpWidget = Help.HelpWidget( button )
00574                 helpWidget.setLabel( self.name )
00575                 helpWidget.setType( helpWidget.ViewType )
00576                 helpWidget.setFilter( (self.model, self.screen.currentView().viewType()) )
00577                 helpWidget.show()
00578                 QApplication.restoreOverrideCursor()
00579 
00580         def __del__(self):
00581                 self.group.__del__()
00582                 del self.group
00583