merge
This commit is contained in:
commit
365b845a3e
18 files changed
+452
-64
No files matched your search
@@ -1,3 +1,5 @@
|
||||
* Added additional gtk.Entry for filename on BinaryField
|
||||
* Display deleted lines in One2Many and Many2Many
|
||||
* Handle URL
|
||||
* Add communication between boards
|
||||
* Added domain inversion feature
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="90.000000"
|
||||
inkscape:export-xdpi="90.000000"
|
||||
inkscape:export-filename="/home/jimmac/Desktop/wi-fi.png"
|
||||
width="48px"
|
||||
height="48px"
|
||||
id="svg11300"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.46"
|
||||
sodipodi:docbase="/home/tigert/cvs/freedesktop.org/tango-icon-theme/scalable/actions"
|
||||
sodipodi:docname="edit-undo.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
<defs
|
||||
id="defs3">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 24 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="48 : 24 : 1"
|
||||
inkscape:persp3d-origin="24 : 16 : 1"
|
||||
id="perspective31" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2326">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2328" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2330" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2316">
|
||||
<stop
|
||||
style="stop-color:#c4a000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2318" />
|
||||
<stop
|
||||
style="stop-color:#c4a000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2320" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2308">
|
||||
<stop
|
||||
style="stop-color:#edd400;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2310" />
|
||||
<stop
|
||||
style="stop-color:#edd400;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2312" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient8662">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop8664" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop8666" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8662"
|
||||
id="radialGradient8668"
|
||||
cx="24.837126"
|
||||
cy="36.421127"
|
||||
fx="24.837126"
|
||||
fy="36.421127"
|
||||
r="15.644737"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.536723,-6.227265e-14,16.87306)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient2187"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop2189"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop2191"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2187"
|
||||
id="linearGradient1764"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-1.813471e-16,-1.171926,-1.171926,1.813471e-16,46.17440,54.10111)"
|
||||
x1="17.060806"
|
||||
y1="11.39502"
|
||||
x2="12.624337"
|
||||
y2="12.583769" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2308"
|
||||
id="linearGradient2314"
|
||||
x1="26.5"
|
||||
y1="34.25"
|
||||
x2="26.25"
|
||||
y2="43.571831"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2316"
|
||||
id="linearGradient2322"
|
||||
x1="26.5"
|
||||
y1="34.25"
|
||||
x2="26.25"
|
||||
y2="43.571831"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2326"
|
||||
id="radialGradient2332"
|
||||
cx="15.09403"
|
||||
cy="13.282721"
|
||||
fx="15.09403"
|
||||
fy="13.282721"
|
||||
r="10.16466"
|
||||
gradientTransform="matrix(2.496031,-1.151905e-16,1.061756e-16,2.300689,-25.12402,-17.82636)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
stroke="#c4a000"
|
||||
fill="#edd400"
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="0.25490196"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="-19.855325"
|
||||
inkscape:cy="-15.183692"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="891"
|
||||
inkscape:window-height="818"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="30" />
|
||||
<metadata
|
||||
id="metadata4">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Jakub Steiner</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:source>http://jimmac.musichall.cz</dc:source>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
|
||||
<dc:title>Edit Undo</dc:title>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>edit</rdf:li>
|
||||
<rdf:li>undo</rdf:li>
|
||||
<rdf:li>revert</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/publicdomain/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
transform="matrix(-1.489736,0.000000,0.000000,-1.001252,60.60436,75.31260)"
|
||||
d="M 40.481863 36.421127 A 15.644737 8.3968935 0 1 1 9.1923885,36.421127 A 15.644737 8.3968935 0 1 1 40.481863 36.421127 z"
|
||||
sodipodi:ry="8.3968935"
|
||||
sodipodi:rx="15.644737"
|
||||
sodipodi:cy="36.421127"
|
||||
sodipodi:cx="24.837126"
|
||||
id="path8660"
|
||||
style="opacity:0.14117647;color:#000000;fill:url(#radialGradient8668);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
style="opacity:1;color:#000000;fill:url(#linearGradient2314);fill-opacity:1.0;fill-rule:nonzero;stroke:url(#linearGradient2322);stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
|
||||
d="M 9.582441,45.034369 C 49.608249,46.355509 43.282405,12.29355 22.462411,12.49765 L 22.462411,3.1222396 L 5.8139298,17.708819 L 22.462411,33.006349 C 22.462411,33.006349 22.462411,23.337969 22.462411,23.337969 C 36.525521,22.751999 40.639939,44.770549 9.582441,45.034369 z "
|
||||
id="path1432"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
id="path2177"
|
||||
d="M 31.032281,39.315519 C 42.75538,33.235892 39.220073,13.087489 21.448701,13.549959 L 21.448701,5.4508678 C 21.448701,5.4508678 7.4009628,17.714589 7.4009628,17.714589 L 21.448701,30.658617 C 21.448701,30.658617 21.448701,22.380979 21.448701,22.380979 C 36.288551,22.032709 35.608611,35.138579 31.032281,39.315519 z "
|
||||
style="opacity:0.69886361;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient1764);stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
|
||||
<path
|
||||
style="opacity:0.51136364;color:#000000;fill:url(#radialGradient2332);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
d="M 6.6291261,17.682797 L 12.28598,23.074486 C 18.561553,22.897709 15.733126,16.710525 26.958446,13.616933 L 22.008699,12.998214 L 21.92031,4.3361562 L 6.6291261,17.682797 z "
|
||||
id="path2324"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.0 KiB |
@@ -29,6 +29,8 @@ class CellRendererCombo(gtk.GenericCellRenderer):
|
||||
gobject.PARAM_READWRITE),
|
||||
'text-column': (gobject.TYPE_INT, 'Text Column',
|
||||
'Text Column', 0, 10, 0, gobject.PARAM_READWRITE),
|
||||
'strikethrough': (gobject.TYPE_BOOLEAN, 'Strikethrough',
|
||||
'Strikethrough', False, gobject.PARAM_WRITABLE),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@@ -40,6 +42,11 @@ class CellRendererCombo(gtk.GenericCellRenderer):
|
||||
self.editable = self._renderer.get_property('editable')
|
||||
self.visible = True
|
||||
|
||||
def set_sensitive(self, value):
|
||||
if hasattr(self._renderer, 'set_sensitive'):
|
||||
return self._renderer.set_sensitive(value)
|
||||
return self._renderer.set_property('sensitive', value)
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
setattr(self, pspec.name, value)
|
||||
if pspec.name == 'visible':
|
||||
|
||||
@@ -22,6 +22,8 @@ class CellRendererDate(gtk.GenericCellRenderer):
|
||||
'Editable', 0, 10, 0, gobject.PARAM_READWRITE),
|
||||
'visible': (gobject.TYPE_INT, 'Visible',
|
||||
'Visible', 0, 10, 0, gobject.PARAM_READWRITE),
|
||||
'strikethrough': (gobject.TYPE_BOOLEAN, 'Strikethrough',
|
||||
'Strikethrough', False, gobject.PARAM_WRITABLE),
|
||||
}
|
||||
|
||||
def __init__(self, format):
|
||||
@@ -36,6 +38,11 @@ class CellRendererDate(gtk.GenericCellRenderer):
|
||||
self.editable = self._renderer.get_property('editable')
|
||||
self.visible = True
|
||||
|
||||
def set_sensitive(self, value):
|
||||
if hasattr(self._renderer, 'set_sensitive'):
|
||||
return self._renderer.set_sensitive(value)
|
||||
return self._renderer.set_property('sensitive', value)
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
setattr(self, pspec.name, value)
|
||||
if pspec.name == 'visible':
|
||||
|
||||
@@ -23,6 +23,8 @@ class CellRendererText(gtk.GenericCellRenderer):
|
||||
'XAlign', 0, 1, 0, gobject.PARAM_READWRITE),
|
||||
'visible': (gobject.TYPE_INT, 'Visible',
|
||||
'Visible', 0, 10, 0, gobject.PARAM_READWRITE),
|
||||
'strikethrough': (gobject.TYPE_BOOLEAN, 'Strikethrough',
|
||||
'Strikethrough', False, gobject.PARAM_WRITABLE),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@@ -34,6 +36,11 @@ class CellRendererText(gtk.GenericCellRenderer):
|
||||
self.editable = self._renderer.get_property('editable')
|
||||
self.visible = True
|
||||
|
||||
def set_sensitive(self, value):
|
||||
if hasattr(self._renderer, 'set_sensitive'):
|
||||
return self._renderer.set_sensitive(value)
|
||||
return self._renderer.set_property('sensitive', value)
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
setattr(self, pspec.name, value)
|
||||
if pspec.name == 'visible':
|
||||
|
||||
@@ -27,6 +27,11 @@ class CellRendererToggle(gtk.GenericCellRenderer):
|
||||
self.activatable = self._renderer.get_property('activatable')
|
||||
self.visible = True
|
||||
|
||||
def set_sensitive(self, value):
|
||||
if hasattr(self._renderer, 'set_sensitive'):
|
||||
return self._renderer.set_sensitive(value)
|
||||
return self._renderer.set_property('sensitive', value)
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
setattr(self, pspec.name, value)
|
||||
if pspec.name == 'visible':
|
||||
|
||||
@@ -24,17 +24,22 @@ class Shortcuts(object):
|
||||
self.dialog.vbox.pack_start(notebook)
|
||||
|
||||
shortcuts = [
|
||||
(_('Text Entries Shortcuts'),),
|
||||
('<Ctrl> + X', _('Cut selected text')),
|
||||
('<Ctrl> + C', _('Copy selected text')),
|
||||
('<Ctrl> + V', _('Paste copied text')),
|
||||
('<Tab>', _('Next widget')),
|
||||
('<Shift> + <Tab>', _('Previous widget')),
|
||||
(_('Relation Entries Shortcuts'),),
|
||||
('<F3>', _('Create new relation')),
|
||||
('<F2>', _('Open/Search relation')),
|
||||
(_('Date/Datetime Entries Shortcuts'),),
|
||||
(_('''You can use special operators:
|
||||
(_('Text Entries Shortcuts'),),
|
||||
('<Ctrl> + X', _('Cut selected text')),
|
||||
('<Ctrl> + C', _('Copy selected text')),
|
||||
('<Ctrl> + V', _('Paste copied text')),
|
||||
('<Tab>', _('Next widget')),
|
||||
('<Shift> + <Tab>', _('Previous widget')),
|
||||
(_('Relation Entries Shortcuts'),),
|
||||
('<F3>', _('Create new relation')),
|
||||
('<F2>', _('Open/Search relation')),
|
||||
(_('List Entries Shortcuts'),),
|
||||
('<F3>', _('Create new line')),
|
||||
('<F2>', _('Open relation')),
|
||||
('<Del>', _('Mark line for deletion')),
|
||||
('<Ins>', _('Unmark line for deletion')),
|
||||
(_('Date/Datetime Entries Shortcuts'),),
|
||||
(_('''You can use special operators:
|
||||
* + to increase the date
|
||||
* - to decrease the date or clear
|
||||
* = to set the date or the current date
|
||||
@@ -49,8 +54,8 @@ y for years
|
||||
Examples:
|
||||
"+21d" increase of 21 days the date
|
||||
"=11m" set the date to the 11th month of the year
|
||||
"-2w" decrease of 2 weeks the date'''),)
|
||||
]
|
||||
"-2w" decrease of 2 weeks the date'''),),
|
||||
]
|
||||
notebook.append_page(self._fill_table(shortcuts),
|
||||
gtk.Label(_('Edition Widgets')))
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ class CharField(object):
|
||||
def get_state_attrs(self, record):
|
||||
if self.name not in record.state_attrs:
|
||||
record.state_attrs[self.name] = self.attrs.copy()
|
||||
if record.group.readonly:
|
||||
if record.group.readonly or record.readonly:
|
||||
record.state_attrs[self.name]['readonly'] = True
|
||||
return record.state_attrs[self.name]
|
||||
|
||||
@@ -449,10 +449,10 @@ class O2MField(CharField):
|
||||
record2.get(check_load=check_load, get_readonly=readonly)))
|
||||
if record.value[self.name].record_removed:
|
||||
result.append(('unlink', [x.id for x in \
|
||||
record.value[self.name].record_removed]))
|
||||
record.value[self.name].record_removed]))
|
||||
if record.value[self.name].record_deleted:
|
||||
result.append(('delete', [x.id for x in \
|
||||
record.value[self.name].record_deleted]))
|
||||
record.value[self.name].record_deleted]))
|
||||
return result
|
||||
|
||||
def get_timestamp(self, record):
|
||||
@@ -473,7 +473,8 @@ class O2MField(CharField):
|
||||
if record.value.get(self.name) is None:
|
||||
return []
|
||||
for record2 in record.value[self.name]:
|
||||
result.append(record2.get_eval(check_load=check_load))
|
||||
if not (record2.deleted or record2.removed):
|
||||
result.append(record2.get_eval(check_load=check_load))
|
||||
return result
|
||||
|
||||
def set(self, record, value, modified=False):
|
||||
@@ -531,7 +532,7 @@ class O2MField(CharField):
|
||||
parent_datetime_field=self.attrs.get('datetime_field'))
|
||||
if record.value.get(self.name):
|
||||
group.record_deleted.extend(x for x in record.value[self.name]
|
||||
if x.id > 0)
|
||||
if x.id > 0)
|
||||
group.record_deleted.extend(record.value[self.name].record_deleted)
|
||||
group.record_removed.extend(record.value[self.name].record_removed)
|
||||
record.value[self.name] = group
|
||||
@@ -698,6 +699,9 @@ class ReferenceField(CharField):
|
||||
if not value:
|
||||
record.value[self.name] = False
|
||||
return
|
||||
if isinstance(value, basestring):
|
||||
model, ref_id = value.split(',')
|
||||
value = model, (ref_id, record.value.get(self.name + '.rec_name'))
|
||||
ref_model, (ref_id, ref_str) = value
|
||||
if ref_model:
|
||||
ref_id = int(ref_id)
|
||||
|
||||
@@ -27,8 +27,7 @@ class Group(SignalEvent, list):
|
||||
self.load_fields(fields)
|
||||
self.current_idx = None
|
||||
self.load(ids)
|
||||
self.record_removed = []
|
||||
self.record_deleted = []
|
||||
self.record_deleted, self.record_removed = [], []
|
||||
self.on_write = set()
|
||||
self.readonly = readonly
|
||||
if self._context.get('_datetime'):
|
||||
@@ -87,8 +86,7 @@ class Group(SignalEvent, list):
|
||||
if not self.lock_signal:
|
||||
self.signal('group-list-changed', ('group-cleared',))
|
||||
self.__id2record = {}
|
||||
self.record_removed = []
|
||||
self.record_deleted = []
|
||||
self.record_removed, self.record_deleted = [], []
|
||||
|
||||
def move(self, record, pos):
|
||||
self.lock_signal = True
|
||||
@@ -159,9 +157,16 @@ class Group(SignalEvent, list):
|
||||
def load(self, ids, display=True, modified=False):
|
||||
if not ids:
|
||||
return True
|
||||
# Work with sets to compute intersection and set difference
|
||||
set_records = set(self.get(x) for x in ids)
|
||||
for record in set_records & set(self.record_removed):
|
||||
self.record_removed.remove(record)
|
||||
for record in set_records & set(self.record_deleted):
|
||||
self.record_deleted.remove(record)
|
||||
|
||||
old_ids = [x.id for x in self]
|
||||
ids = [x for x in ids if x not in old_ids]
|
||||
set_records -= set(x for x in self)
|
||||
# return on the list form in order to preserver ordering
|
||||
ids = [x for x in ids if self.get(x) in set_records]
|
||||
if not ids:
|
||||
return True
|
||||
|
||||
@@ -177,12 +182,6 @@ class Group(SignalEvent, list):
|
||||
self._record_changed)
|
||||
new_record.signal_connect(self, 'record-modified',
|
||||
self._record_modified)
|
||||
for record in self.record_removed[:]:
|
||||
if record.id in ids:
|
||||
self.record_removed.remove(record)
|
||||
for record in self.record_deleted[:]:
|
||||
if record.id in ids:
|
||||
self.record_deleted.remove(record)
|
||||
if self.lock_signal:
|
||||
self.lock_signal = False
|
||||
self.signal('group-cleared')
|
||||
@@ -253,18 +252,31 @@ class Group(SignalEvent, list):
|
||||
self.signal('group-changed', record)
|
||||
return record
|
||||
|
||||
def unremove(self, record, signal=True):
|
||||
if record in self.record_removed:
|
||||
self.record_removed.remove(record)
|
||||
if record in self.record_deleted:
|
||||
self.record_deleted.remove(record)
|
||||
if signal:
|
||||
record.signal('record-changed', record.parent)
|
||||
|
||||
def remove(self, record, remove=False, modified=True, signal=True):
|
||||
idx = self.index(record)
|
||||
if self[idx].id > 0:
|
||||
if remove:
|
||||
if self[idx] in self.record_deleted:
|
||||
self.record_deleted.remove(self[idx])
|
||||
self.record_removed.append(self[idx])
|
||||
else:
|
||||
if self[idx] in self.record_removed:
|
||||
self.record_removed.remove(self[idx])
|
||||
self.record_deleted.append(self[idx])
|
||||
if record.parent:
|
||||
record.parent.modified = True
|
||||
if modified:
|
||||
record.modified = True
|
||||
self._remove(self[idx])
|
||||
if not record.parent or self[idx].id <= 0:
|
||||
self._remove(self[idx])
|
||||
|
||||
if len(self):
|
||||
self.current_idx = min(idx, len(self) - 1)
|
||||
@@ -351,8 +363,7 @@ class Group(SignalEvent, list):
|
||||
for field in self.fields.itervalues():
|
||||
field.destroy()
|
||||
self.fields = {}
|
||||
self.record_removed = None
|
||||
self.record_deleted = None
|
||||
self.record_deleted, self.record_removed = [], []
|
||||
self.__id2record = None
|
||||
for record in self:
|
||||
record.destroy()
|
||||
|
||||
@@ -106,6 +106,25 @@ class Record(SignalEvent):
|
||||
|
||||
modified = property(get_modified, set_modified)
|
||||
|
||||
def get_removed(self):
|
||||
if self.group is not None:
|
||||
return self in self.group.record_removed
|
||||
return False
|
||||
|
||||
removed = property(get_removed)
|
||||
|
||||
def get_deleted(self):
|
||||
if self.group is not None:
|
||||
return self in self.group.record_deleted
|
||||
return False
|
||||
|
||||
deleted = property(get_deleted)
|
||||
|
||||
def get_readonly(self):
|
||||
return self.deleted or self.removed
|
||||
|
||||
readonly = property(get_readonly)
|
||||
|
||||
def is_modified(self):
|
||||
return self.modified
|
||||
|
||||
|
||||
@@ -504,6 +504,10 @@ class Screen(SignalEvent):
|
||||
self.display()
|
||||
self.request_set()
|
||||
|
||||
def unremove(self):
|
||||
if self.current_record in self.group:
|
||||
self.group.unremove(self.current_record)
|
||||
|
||||
def remove(self, delete=False, remove=False):
|
||||
res = False
|
||||
reload_ids = []
|
||||
|
||||
@@ -9,6 +9,12 @@ from interface import WidgetInterface
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
def humanize(size):
|
||||
for x in ('bytes', 'KB', 'MB', 'GB', 'TB', 'PB'):
|
||||
if size < 1000:
|
||||
return '%3.1f%s' % (size, x)
|
||||
size /= 1000.0
|
||||
|
||||
|
||||
class Binary(WidgetInterface):
|
||||
"Binary"
|
||||
@@ -17,12 +23,26 @@ class Binary(WidgetInterface):
|
||||
super(Binary, self).__init__(field_name, model_name, window,
|
||||
attrs=attrs)
|
||||
|
||||
self.filename = attrs.get('filename')
|
||||
self.filename_visible = attrs.get('filename_visible')
|
||||
|
||||
self.tooltips = Tooltips()
|
||||
|
||||
self.widget = gtk.HBox(spacing=0)
|
||||
self.wid_text = gtk.Entry()
|
||||
self.wid_text.set_property('activates_default', True)
|
||||
self.widget.pack_start(self.wid_text, expand=True, fill=True)
|
||||
if self.filename and self.filename_visible:
|
||||
self.wid_text = gtk.Entry()
|
||||
self.wid_text.set_property('activates_default', True)
|
||||
self.wid_text.connect('focus-in-event', lambda x, y: self._focus_in())
|
||||
self.wid_text.connect('focus-out-event', lambda x, y: self._focus_out())
|
||||
self.wid_text.connect_after('key_press_event', self.sig_key_press)
|
||||
self.wid_size = gtk.Entry()
|
||||
self.wid_size.set_width_chars(11)
|
||||
self.wid_size.props.sensitive = False
|
||||
self.widget.pack_start(self.wid_text, expand=True, fill=True)
|
||||
else:
|
||||
self.wid_text = None
|
||||
self.widget.pack_start(self.wid_size, expand=not self.filename,
|
||||
fill=True)
|
||||
|
||||
self.but_new = gtk.Button()
|
||||
img_new = gtk.Image()
|
||||
@@ -35,7 +55,8 @@ class Binary(WidgetInterface):
|
||||
|
||||
self.but_save_as = gtk.Button()
|
||||
img_save_as = gtk.Image()
|
||||
img_save_as.set_from_stock('tryton-save-as', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_save_as.set_from_stock('tryton-save-as',
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
self.but_save_as.set_image(img_save_as)
|
||||
self.but_save_as.set_relief(gtk.RELIEF_NONE)
|
||||
self.but_save_as.connect('clicked', self.sig_save_as)
|
||||
@@ -61,11 +82,17 @@ class Binary(WidgetInterface):
|
||||
else:
|
||||
self.but_new.show()
|
||||
self.but_remove.show()
|
||||
self.widget.set_focus_chain([self.but_new, self.but_save_as,
|
||||
self.but_remove])
|
||||
if self.wid_text:
|
||||
focus_chain = [self.wid_text]
|
||||
else:
|
||||
focus_chain = [self.but_new, self.but_save_as, self.but_remove]
|
||||
self.widget.set_focus_chain(focus_chain)
|
||||
|
||||
def grab_focus(self):
|
||||
return self.wid_text.grab_focus()
|
||||
if self.wid_text:
|
||||
return self.wid_text.grab_focus()
|
||||
else:
|
||||
return self.wid_size.grab_focus()
|
||||
|
||||
def sig_new(self, widget=None):
|
||||
try:
|
||||
@@ -74,9 +101,9 @@ class Binary(WidgetInterface):
|
||||
if filename and self.field:
|
||||
self.field.set_client(self.record,
|
||||
base64.encodestring(open(filename, 'rb').read()))
|
||||
fname = self.attrs.get('fname_widget', False)
|
||||
if fname:
|
||||
self.parent.value = {fname:os.path.basename(filename)}
|
||||
if self.filename and self.filename_visible and self.record:
|
||||
name_wid = self.record.group.fields[self.filename]
|
||||
name_wid.set_client(self.record, os.path.basename(filename))
|
||||
self.display(self.record, self.field)
|
||||
except Exception, exception:
|
||||
warning(_('Error reading the file.\nError message:\n%s') \
|
||||
@@ -98,29 +125,47 @@ class Binary(WidgetInterface):
|
||||
def sig_remove(self, widget=None):
|
||||
if self.field:
|
||||
self.field.set_client(self.record, False)
|
||||
fname = self.attrs.get('fname_widget', False)
|
||||
if fname:
|
||||
self.parent.value = {fname:False}
|
||||
self.display(self.record, self.field)
|
||||
|
||||
def sig_key_press(self, widget, event, *args):
|
||||
editable = self.wid_text and self.wid_text.get_editable()
|
||||
if event.keyval == gtk.keysyms.F3 and editable:
|
||||
self.sig_new(widget)
|
||||
return True
|
||||
elif event.keyval == gtk.keysyms.F2:
|
||||
self.sig_save_as(widget)
|
||||
return True
|
||||
return False
|
||||
|
||||
def display(self, record, field):
|
||||
super(Binary, self).display(record, field)
|
||||
if not field:
|
||||
self.wid_text.set_text('')
|
||||
if self.wid_text:
|
||||
self.wid_text.set_text('')
|
||||
self.wid_size.set_text('')
|
||||
self.but_save_as.set_sensitive(False)
|
||||
return False
|
||||
self.wid_text.set_text(self._size_get(field.get(record)))
|
||||
if self.wid_text:
|
||||
name_wid = record.group.fields[self.filename]
|
||||
self.wid_text.set_text(name_wid.get(record) or '')
|
||||
self.wid_size.set_text(humanize(len(field.get(record) or [])))
|
||||
self.but_save_as.set_sensitive(bool(field.get(record)))
|
||||
return True
|
||||
|
||||
def display_value(self):
|
||||
return self.wid_text.get_text()
|
||||
|
||||
def _size_get(self, value):
|
||||
return value and ('%d ' + _('bytes')) % len(value) or ''
|
||||
if self.wid_text:
|
||||
return self.wid_text.get_text()
|
||||
else:
|
||||
return ''
|
||||
|
||||
def set_value(self, record, field):
|
||||
if self.wid_text:
|
||||
name_wid = self.record.group.fields[self.filename]
|
||||
name_wid.set_client(self.record, self.wid_text.get_text() or False)
|
||||
return
|
||||
|
||||
def _color_widget(self):
|
||||
return self.wid_text
|
||||
if self.wid_text:
|
||||
return self.wid_text
|
||||
else:
|
||||
return self.wid_size
|
||||
@@ -20,6 +20,7 @@ class Image(WidgetInterface):
|
||||
super(Image, self).__init__(field_name, model_name, window,
|
||||
attrs=attrs)
|
||||
|
||||
self.filename = attrs.get('filename')
|
||||
self.height = int(attrs.get('img_height', 100))
|
||||
self.width = int(attrs.get('img_width', 300))
|
||||
|
||||
@@ -102,6 +103,9 @@ class Image(WidgetInterface):
|
||||
if filename:
|
||||
self.field.set_client(self.record,
|
||||
encodestring(open(filename, 'rb').read()))
|
||||
if self.filename and self.record:
|
||||
name_wid = self.record.group.fields[self.filename]
|
||||
name_wid.set_client(self.record, os.path.basename(filename))
|
||||
self.update_img()
|
||||
|
||||
def sig_save_as(self, widget):
|
||||
|
||||
@@ -167,6 +167,7 @@ class WidgetInterface(object):
|
||||
'fg_color_normal': style.fg[gtk.STATE_NORMAL],
|
||||
'fg_color_insensitive': style.fg[gtk.STATE_INSENSITIVE],
|
||||
'text_color_normal': style.text[gtk.STATE_NORMAL],
|
||||
'text_color_insensitive': style.text[gtk.STATE_INSENSITIVE],
|
||||
}
|
||||
|
||||
if COLORS.get(name):
|
||||
|
||||
@@ -96,6 +96,15 @@ class Many2Many(WidgetInterface):
|
||||
self.widget.destroy()
|
||||
del self.widget
|
||||
|
||||
def color_set(self, name):
|
||||
super(Many2Many, self).color_set(name)
|
||||
widget = self._color_widget()
|
||||
# if the style to apply is different from readonly then insensitive
|
||||
# cellrenderers should use the default insensitive color
|
||||
if name != 'readonly':
|
||||
widget.modify_text(gtk.STATE_INSENSITIVE,
|
||||
self.colors['text_color_insensitive'])
|
||||
|
||||
def _sig_add(self, *args):
|
||||
domain = self.field.domain_get(self.record)
|
||||
context = self.field.context_get(self.record)
|
||||
|
||||
@@ -104,7 +104,7 @@ class One2Many(WidgetInterface):
|
||||
|
||||
self.but_del = gtk.Button()
|
||||
tooltips.set_tip(self.but_del, _('Delete selected record'))
|
||||
self.but_del.connect('clicked', self._sig_remove)
|
||||
self.but_del.connect('clicked', self._sig_remove, False)
|
||||
img_del = gtk.Image()
|
||||
img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_del.set_alignment(0.5, 0.5)
|
||||
@@ -112,6 +112,16 @@ class One2Many(WidgetInterface):
|
||||
self.but_del.set_relief(gtk.RELIEF_NONE)
|
||||
hbox.pack_start(self.but_del, expand=False, fill=False)
|
||||
|
||||
self.but_undel = gtk.Button()
|
||||
tooltips.set_tip(self.but_undel, _('Undelete selected record'))
|
||||
self.but_undel.connect('clicked', self._sig_undelete)
|
||||
img_undel = gtk.Image()
|
||||
img_undel.set_from_stock('tryton-undo', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_undel.set_alignment(0.5, 0.5)
|
||||
self.but_undel.add(img_undel)
|
||||
self.but_undel.set_relief(gtk.RELIEF_NONE)
|
||||
hbox.pack_start(self.but_undel, expand=False, fill=False)
|
||||
|
||||
hbox.pack_start(gtk.VSeparator(), expand=False, fill=True)
|
||||
|
||||
self.but_pre = gtk.Button()
|
||||
@@ -195,6 +205,9 @@ class One2Many(WidgetInterface):
|
||||
and widget == self.screen.widget:
|
||||
self._sig_remove(widget)
|
||||
return False
|
||||
if event.keyval == gtk.keysyms.Insert and widget == self.screen.widget:
|
||||
self._sig_undelete(widget)
|
||||
return False
|
||||
|
||||
def destroy(self):
|
||||
self.screen.destroy()
|
||||
@@ -206,6 +219,15 @@ class One2Many(WidgetInterface):
|
||||
self.screen.switch_view()
|
||||
self.color_set(self.color_name)
|
||||
|
||||
def color_set(self, name):
|
||||
super(One2Many, self).color_set(name)
|
||||
widget = self._color_widget()
|
||||
# if the style to apply is different from readonly then insensitive
|
||||
# cellrenderers should use the default insensitive color
|
||||
if name != 'readonly':
|
||||
widget.modify_text(gtk.STATE_INSENSITIVE,
|
||||
self.colors['text_color_insensitive'])
|
||||
|
||||
def _readonly_set(self, value):
|
||||
self._readonly = value
|
||||
self.but_new.set_sensitive(not value)
|
||||
@@ -231,12 +253,6 @@ class One2Many(WidgetInterface):
|
||||
self.screen.new(context=ctx)
|
||||
self.screen.current_view.widget.set_sensitive(True)
|
||||
else:
|
||||
readonly = False
|
||||
domain = []
|
||||
if self.field and self.record:
|
||||
readonly = self.field.get_state_attrs(self.record
|
||||
).get('readonly', False)
|
||||
domain = self.field.domain_get(self.record)
|
||||
win = WinForm(self.screen, self.window, new=True,
|
||||
context=ctx)
|
||||
while True:
|
||||
@@ -250,8 +266,9 @@ class One2Many(WidgetInterface):
|
||||
|
||||
def _sig_edit(self, widget=None):
|
||||
self.view.set_value()
|
||||
if self.screen.current_record:
|
||||
if not self.screen.current_record.validate():
|
||||
record = self.screen.current_record
|
||||
if record:
|
||||
if not record.validate():
|
||||
self.screen.display()
|
||||
return
|
||||
win = WinForm(self.screen, self.window)
|
||||
@@ -277,6 +294,9 @@ class One2Many(WidgetInterface):
|
||||
def _sig_remove(self, widget, remove=False):
|
||||
self.screen.remove(remove=remove)
|
||||
|
||||
def _sig_undelete(self, button):
|
||||
self.screen.unremove()
|
||||
|
||||
def _sig_activate(self, *args):
|
||||
self._sig_add()
|
||||
self.wid_text.grab_focus()
|
||||
@@ -333,9 +353,12 @@ class One2Many(WidgetInterface):
|
||||
self.but_pre.set_sensitive(True)
|
||||
else:
|
||||
self.but_pre.set_sensitive(False)
|
||||
self.but_del.set_sensitive(True)
|
||||
self.but_undel.set_sensitive(True)
|
||||
else:
|
||||
self.but_open.set_sensitive(False)
|
||||
self.but_del.set_sensitive(False)
|
||||
self.but_undel.set_sensitive(False)
|
||||
self.but_next.set_sensitive(False)
|
||||
self.but_pre.set_sensitive(False)
|
||||
if self.attrs.get('add_remove'):
|
||||
|
||||
@@ -518,7 +518,7 @@ class ParserForm(ParserInterface):
|
||||
continue
|
||||
for attr_name in ('relation', 'domain', 'selection',
|
||||
'relation_field', 'string', 'views', 'invisible',
|
||||
'add_remove', 'sort', 'context', 'size'):
|
||||
'add_remove', 'sort', 'context', 'size', 'filename'):
|
||||
if attr_name in fields[name].attrs and \
|
||||
not attr_name in attrs:
|
||||
attrs[attr_name] = fields[name].attrs[attr_name]
|
||||
|
||||
@@ -250,6 +250,10 @@ class Char(object):
|
||||
if isinstance(cell, CellRendererToggle):
|
||||
cell.set_active(bool(text))
|
||||
else:
|
||||
cell.set_sensitive(not (record.deleted or record.removed))
|
||||
if isinstance(cell,
|
||||
(CellRendererText, CellRendererDate, CellRendererCombo)):
|
||||
cell.set_property('strikethrough', record.deleted)
|
||||
cell.set_property('text', text)
|
||||
fg_color = self.get_color(record)
|
||||
cell.set_property('foreground', fg_color)
|
||||
@@ -293,7 +297,8 @@ class Char(object):
|
||||
cell.set_property('background-set', False)
|
||||
else:
|
||||
cell.set_property('background-set', True)
|
||||
cell.set_property('foreground-set', True)
|
||||
cell.set_property('foreground-set',
|
||||
not (record.deleted or record.removed))
|
||||
|
||||
if isinstance(cell, CellRendererToggle):
|
||||
cell.set_property('activatable', not readonly)
|
||||
|
||||
Reference in new issue
Block a user