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 from xml.parsers import expat
00030 
00031 import sys
00032 import gettext
00033 
00034 from SearchWidgetFactory import *
00035 from AbstractSearchWidget import *
00036 from Koo.Common import Common
00037 from Koo.Common import Shortcuts
00038 from Koo.Common import Calendar
00039 from Koo.Common import Numeric
00040 from Koo import Rpc
00041 
00042 from PyQt4.QtGui import *
00043 from PyQt4.QtCore import *
00044 from PyQt4.uic import *
00045 
00046 class SearchFormContainer( QWidget ):
00047         def __init__(self, parent):
00048                 QWidget.__init__( self, parent )
00049                 layout = QGridLayout( self )
00050                 layout.setSpacing( 0 )
00051                 layout.setContentsMargins( 0, 0, 0, 0 )
00052                 
00053                 self.col = 4
00054                 self.x = 0
00055                 self.y = 0
00056 
00057         def addWidget(self, widget, name=None):
00058                 if self.x + 1 > self.col:
00059                         self.x = 0
00060                         self.y = self.y + 1
00061 
00062                 
00063                 
00064                 widget.gridLine = self.y
00065                 if name:
00066                         label = QLabel( name )
00067                         label.gridLine = self.y
00068                         vbox = QVBoxLayout()
00069                         vbox.setSpacing( 0 )
00070                         vbox.setContentsMargins( 0, 0, 0, 0 )
00071                         vbox.addWidget( label, 0 )
00072                         vbox.addWidget( widget )
00073                         self.layout().addLayout( vbox, self.y, self.x )
00074                 else:
00075                         self.layout().addWidget( widget, self.y, self.x )
00076                 self.x = self.x + 1
00077 
00078 (CustomSearchItemWidgetUi, CustomSearchItemWidgetBase) = loadUiType( Common.uiPath('customsearchitem.ui') )
00079 
00080 class CustomSearchItemWidget(AbstractSearchWidget, CustomSearchItemWidgetUi):
00081 
00082         operators = (
00083                 ('is empty', _('is empty'), ('char', 'text', 'many2one', 'date', 'time', 'datetime', 'float_time'), False),
00084                 ('is not empty', _('is not empty'), ('char', 'text', 'many2one', 'date', 'time', 'datetime', 'float_time'), False),
00085                 ('ilike', _('contains'), ('char','text','many2one','many2many','one2many'), True), 
00086                 ('not ilike', _('does not contain'), ('char','text','many2one'), True), 
00087                 ('=', _('is equal to'), ('boolean','char','text','integer','float','date','time','datetime','float_time'), True),
00088                 ('<>', _('is not equal to'), ('boolean','char','text','integer','float','date','time','datetime','float_time'), True), 
00089                 ('>', _('greater than'), ('char','text','integer','float','date','time','datetime','float_time'), True), 
00090                 ('<', _('less than'), ('char','text','integer','float','date','time','datetime','float_time'), True), 
00091                 ('in', _('in'), ('selection','char','text','integer','float','date','time','datetime','float_time'), True),
00092                 ('not in', _('not in'), ('selection','char','text','integer','float','date','time','datetime','float_time'), True),
00093         )
00094 
00095         typeOperators = {
00096                 'char': ('ilike', 'not ilike', '=', '<', '>', '<>'),
00097                 'integer': ('=', '<>', '>', '<')
00098         }
00099 
00100         typeRelated = ('many2one','many2many','one2many')
00101 
00102         def __init__(self, parent=None):
00103                 AbstractSearchWidget.__init__(self, '', parent)
00104                 CustomSearchItemWidgetUi.__init__(self)
00105                 self.setupUi(self)
00106 
00107                 self.uiRelatedField.setVisible( False )
00108                 self.connect(self.uiField, SIGNAL('currentIndexChanged(int)'), self.updateRelatedAndOperators)
00109                 self.connect(self.uiRelatedField, SIGNAL('currentIndexChanged(int)'), self.updateOperators)
00110                 self.connect(self.uiOperator, SIGNAL('currentIndexChanged(int)'), self.updateValue)
00111 
00112                 self.fields = None
00113 
00114         def setup(self, fields):
00115                 self.uiField.addItem( '' )
00116 
00117                 self.fields = fields
00118 
00119                 fields = [(x, fields[x].get('string', x)) for x in fields]
00120                 fields.sort( key=lambda x: x[1] )
00121                 for field in fields:
00122                         self.uiField.addItem( field[1], QVariant( field[0] ) )
00123 
00124 
00125                 self.uiOperator.addItem( '' )
00126                 for operator in self.operators:
00127                         self.uiOperator.addItem( operator[1], QVariant( operator[0] ) )
00128 
00129                 self.scNew = QShortcut( self )
00130                 self.scNew.setKey( Shortcuts.CreateInField )
00131                 self.scNew.setContext( Qt.WidgetWithChildrenShortcut )
00132 
00133                 self.setAndSelected()
00134 
00135                 self.connect( self.scNew, SIGNAL('activated()'), self, SIGNAL('newItem()') )
00136                 self.connect( self.pushNew, SIGNAL('clicked()'), self, SIGNAL('newItem()') )
00137                 self.connect( self.pushAnd, SIGNAL('clicked()'), self.andSelected )
00138                 self.connect( self.pushOr, SIGNAL('clicked()'), self.orSelected )
00139                 self.connect( self.pushRemove, SIGNAL('clicked()'), self, SIGNAL('removeItem()') )
00140 
00141         def setValid(self, valid):
00142                 if valid:
00143                         color = 'white'
00144                 else:
00145                         color = 'red'
00146                 color = QColor( color )
00147                 palette = QPalette()
00148                 palette.setColor(QPalette.Active, QPalette.Base, color)
00149                 self.setPalette(palette);
00150 
00151         def setAndSelected(self):
00152                 self.pushOr.setChecked( False )
00153                 self.pushOr.setEnabled( True )
00154                 self.pushAnd.setEnabled( False )
00155                 self.pushAnd.setChecked( True )
00156 
00157         def setOrSelected(self):
00158                 self.pushAnd.setEnabled( True )
00159                 self.pushAnd.setChecked( False )
00160                 self.pushOr.setChecked( True )
00161                 self.pushOr.setEnabled( False )
00162 
00163         def isAndSelected(self):
00164                 return self.pushAnd.isChecked()
00165 
00166         def isOrSelected(self):
00167                 return self.pushOr.isChecked()
00168 
00169         def copyState(self, widget):
00170                 if widget.isAndSelected():
00171                         self.setAndSelected()
00172                 else:
00173                         self.setOrSelected()
00174 
00175         def andSelected(self):
00176                 self.setAndSelected()
00177 
00178         def orSelected(self):
00179                 self.setOrSelected()
00180 
00181         def updateRelatedAndOperators(self, index):
00182                 fieldName = unicode( self.uiField.itemData( self.uiField.currentIndex() ).toString() )
00183                 if not fieldName:
00184                         return
00185 
00186                 fieldType = self.fields[fieldName].get('type')
00187 
00188                 if fieldType in self.typeRelated:
00189                         self.uiRelatedField.setVisible( True )
00190                         self.uiRelatedField.setCurrentIndex( 0 )
00191                         model = self.fields[fieldName].get('relation')
00192                         if model:
00193                                 view = Rpc.session.execute('/object', 'execute', model, 'fields_view_get', False, 'form', Rpc.session.context)
00194                                 self.relatedFields = view['fields']
00195                         else:
00196                                 self.relatedFields = {}
00197 
00198                         self.uiRelatedField.clear()
00199                         self.uiRelatedField.addItem( '', QVariant('') )
00200                         fields = [(x, self.relatedFields[x].get('string', x)) for x in self.relatedFields]
00201                         fields.sort( key=lambda x: x[1] )
00202                         for field in fields:
00203                                 self.uiRelatedField.addItem( field[1], QVariant( field[0] ) )
00204                 else:
00205                         self.uiRelatedField.clear()
00206                         self.uiRelatedField.setVisible( False )
00207 
00208                 self.updateOperators()
00209 
00210         def updateOperators(self, index=None):
00211                 self.uiOperator.clear()
00212                 fieldName = unicode( self.uiField.itemData( self.uiField.currentIndex() ).toString() )
00213                 relatedFieldName = unicode( self.uiRelatedField.itemData( self.uiRelatedField.currentIndex() ).toString() )
00214                 if not fieldName:
00215                         return
00216 
00217                 if relatedFieldName:
00218                         fieldType = self.relatedFields[relatedFieldName].get('type')
00219                 else:
00220                         fieldType = self.fields[fieldName].get('type')
00221 
00222                 self.uiOperator.addItem( '' )
00223                 for operator in self.operators:
00224                         if fieldType in operator[2]:
00225                                 self.uiOperator.addItem( operator[1], QVariant( operator[0] ) )
00226 
00227 
00228         def updateValue(self, index):
00229                 operator = unicode( self.uiOperator.itemData( self.uiOperator.currentIndex() ).toString() )
00230                 for op in self.operators:
00231                         if operator == op[0]:
00232                                 self.uiValue.setVisible( op[3] )
00233                                 break
00234                 self.setValid( True )
00235 
00236         def clear(self):
00237                 self.uiField.setCurrentIndex( 0 )
00238                 self.uiRelatedField.clear()
00239                 self.uiOperator.setCurrentIndex( 0 )
00240                 self.uiValue.clear()
00241 
00242         def correctValue(self, value, fieldName, relatedFieldName):
00243                 text = value
00244 
00245                 if relatedFieldName:
00246                         fieldType = self.relatedFields[relatedFieldName].get('type')
00247                 else:
00248                         fieldType = self.fields[fieldName].get('type')
00249                 if fieldType == 'date':
00250                         value = Calendar.textToDate( value )
00251                         text = Calendar.dateToText( value )
00252                         value = Calendar.dateToStorage( value )
00253                 elif fieldType == 'datetime':
00254                         value = Calendar.textToDateTime( value )
00255                         text = Calendar.dateTimeToText( value )
00256                         value = Calendar.dateTimeToStorage( value )
00257                 elif fieldType == 'float_time':
00258                         value = Calendar.textToFloatTime( value )
00259                         text = Calendar.floatTimeToText( value )
00260                         value = Calendar.floatTimeToStorage( value )
00261                 elif fieldType == 'time':
00262                         value = Calendar.textToTime( value )
00263                         text = Calendar.timeToText( value )
00264                         value = Calendar.timeToStorage( value )
00265                 elif fieldType == 'float':
00266                         value = Numeric.textToFloat( value ) or 0.0
00267                         text = Numeric.floatToText( value )
00268                 elif fieldType == 'integer':
00269                         value = Numeric.textToInteger( value ) or 0.0
00270                         text = Numeric.integerToText( value )
00271                 elif fieldType == 'selection':
00272 
00273                         if relatedFieldName:
00274                                 options = self.relatedFields[relatedFieldName]['selection']
00275                         else:
00276                                 options = self.fields[fieldName]['selection']
00277 
00278                         text = []
00279                         keys = []
00280                         for selection in options:
00281                                 
00282                                 
00283                                 
00284                                 if value.lower().strip() == selection[1].lower().strip():
00285                                         keys = [ selection[0] ]
00286                                         text = [ selection[1] ]
00287                                         break
00288                                 if value.lower() in selection[1].lower():
00289                                         keys.append( selection[0] )
00290                                         text.append( selection[1] )
00291                         value = keys
00292                         text = ', '.join( text )
00293                 elif fieldType == 'boolean':
00294                         options = [
00295                                 (True, _('True')),
00296                                 (False, _('False')),
00297                         ]
00298                         text = ''
00299                         for selection in options:
00300                                 if value.lower() in selection[1].lower():
00301                                         value = selection[0]
00302                                         text = selection[1]
00303                                         break
00304                         if not text:
00305                                 value = options[1][0]
00306                                 text = options[1][1]
00307                 return (value, text)
00308 
00309         def value(self):
00310                 if not self.uiField.currentIndex():
00311                         return []
00312                 if not self.uiOperator.currentIndex():
00313                         self.setValid( False )
00314                         return []
00315                 self.setValid( True )
00316                 fieldName = unicode( self.uiField.itemData( self.uiField.currentIndex() ).toString() )
00317                 relatedFieldName = unicode( self.uiRelatedField.itemData( self.uiRelatedField.currentIndex() ).toString() )
00318                 operator = unicode( self.uiOperator.itemData( self.uiOperator.currentIndex() ).toString() )
00319                 value = unicode( self.uiValue.text() )
00320                 if operator in ('in', 'not in'):
00321                         text = []
00322                         newValue = []
00323                         for item in value.split(','):
00324                                 data = self.correctValue( item.strip(), fieldName, relatedFieldName )
00325                                 newValue.append( data[0] )
00326                                 text.append( data[1] )
00327                         value = newValue
00328                         text = ', '.join( text )
00329 
00330                         newValue = []
00331                         for item in value:
00332                                 if isinstance(item, list):
00333                                         newValue += [x for x in item]
00334                                 else:
00335                                         newValue.append( item )
00336                         value = newValue
00337                 elif operator == 'is empty':
00338                         operator = '='
00339                         value = False
00340                         text = ''
00341                 elif operator == 'is not empty':
00342                         operator = '!='
00343                         value = False
00344                         text = ''
00345                 else:
00346                         (value, text) = self.correctValue( value, fieldName, relatedFieldName )
00347 
00348                 self.uiValue.setText( text )
00349 
00350                 if self.pushOr.isChecked():
00351                         condition = '|'
00352                 else:
00353                         condition = '&'
00354 
00355                 if relatedFieldName:
00356                         queryFieldName = '%s.%s' % (fieldName, relatedFieldName)
00357                 else:
00358                         queryFieldName = fieldName
00359                 return [condition,(queryFieldName, operator, value)]
00360 
00361 class CustomSearchFormWidget(AbstractSearchWidget):
00362         def __init__(self, parent=None):
00363                 AbstractSearchWidget.__init__(self, '', parent)
00364                 
00365                 self.layout = QVBoxLayout( self )
00366 
00367                 self.model = None
00368                 self.name = ''
00369                 self.focusable = True
00370                 self.expanded = True
00371                 self._loaded = False
00372                 self.fields = None
00373                 self.widgets = []
00374 
00375                 self.widgets = []
00376 
00377         
00378         def isLoaded(self):
00379                 return self._loaded
00380 
00381         
00382         def isEmpty(self):
00383                 if len(self.widgets):
00384                         return False
00385                 else:
00386                         return True
00387 
00388         
00389         
00390         
00391         
00392         
00393         def setItemValid(self, number, valid):
00394                 self.widgets[number].setValid( valid )
00395 
00396         def setAllItemsValid(self, valid):
00397                 for number in xrange(self.itemCount()):
00398                         self.setItemValid(number, valid)
00399 
00400         def insertItem(self, previousItem=None):
00401                 filterItem = CustomSearchItemWidget( self )
00402                 filterItem.setup( self.fields )
00403                 if previousItem:
00404                         index = self.layout.indexOf(previousItem)+1
00405                         filterItem.copyState( previousItem )
00406                         self.layout.insertWidget( index, filterItem )
00407                         self.widgets.insert(index, filterItem)
00408                 else:
00409                         self.layout.addWidget( filterItem )
00410                         self.widgets.append( filterItem )
00411                 self.connect( filterItem, SIGNAL('newItem()'), self.newItem )
00412                 self.connect( filterItem, SIGNAL('removeItem()'), self.removeItem )
00413 
00414         def dropItem(self, item):
00415                 if len(self.widgets) > 1:
00416                         self.layout.removeWidget( item )
00417                         item.hide()
00418                         self.widgets.remove( item )
00419                 else:
00420                         item.clear()
00421 
00422         def newItem(self):
00423                 self.insertItem( self.sender() )
00424 
00425         def removeItem(self):
00426                 self.dropItem( self.sender() )
00427 
00428         def itemCount(self):
00429                 return len(self.widgets)
00430 
00431         
00432         
00433         
00434         
00435         def setup(self, fields, domain):
00436                 
00437                 if self._loaded:
00438                         return 
00439                 self._loaded = True
00440 
00441                 self.fields = fields
00442                 self.insertItem()
00443 
00444                 
00445                 
00446                 
00447                 
00448                         
00449                 
00450                 
00451                 
00452 
00453 
00454         def setFocus(self):
00455                 pass
00456                 
00457                         
00458                 
00459                         
00460 
00461         
00462         
00463         
00464         def clear(self):
00465                 for item in self.widgets[:]:
00466                         self.dropItem( item )
00467                 self.setAllItemsValid(True)
00468 
00469         
00470         
00471         
00472         
00473         def value(self, domain=[]):
00474                 res = []
00475                 for x in self.widgets:
00476                         res += x.value()
00477 
00478                 if res:
00479                         
00480                         res = res[:-2] + res[-1:]
00481 
00482                 v_keys = [x[0] for x in res]
00483                 for f in domain:
00484                         if f[0] not in v_keys:
00485                                 res.append(f)
00486                 return res
00487 
00488         
00489         
00490         
00491         
00492         
00493         
00494         
00495         
00496         
00497         def setValue(self, val):
00498                 pass
00499                 
00500                         
00501                                 
00502