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