bugfixed for menu disable
[openerp-client.git] / bin / widget / view / form_gtk / date_widget.py
bloba5a955b4bde585965052589c055a784a2bd2ea1f
2 # Copyright (C) Tiny 2008
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 # USA
19 # Author(s): Johan Dahlin (Async Open Source)
20 # Tiny sprl
25 import gobject
26 import pango
27 import gtk
28 import re
30 import tools
31 from tools import date_mapping
32 import time
33 from mx.DateTime import DateTime
35 class DateEntry(gtk.Entry):
36 def __init__(self, format, callback=None, callback_process=None):
37 super(DateEntry, self).__init__()
38 self.modify_font(pango.FontDescription("monospace"))
40 self.format = format
41 self.regex = self.initial_value = format
42 for key,val in date_mapping.items():
43 self.regex = self.regex.replace(key, val[1])
44 self.initial_value = self.initial_value.replace(key, val[0])
46 self.set_text(self.initial_value)
47 self.regex = re.compile(self.regex)
49 assert self.regex.match(self.initial_value), 'Error, the initial value should be validated by regex'
50 self.set_width_chars(len(self.initial_value))
51 self.set_max_length(len(self.initial_value))
53 self.connect('key-press-event', self._on_key_press)
54 self.connect('insert-text', self._on_insert_text)
55 self.connect('delete-text', self._on_delete_text)
57 self.connect('focus-out-event', self._focus_out)
58 self.callback = callback
59 self.callback_process = callback_process
61 self._interactive_input = True
62 self.mode_cmd = False
63 gobject.idle_add(self.set_position, 0)
64 tooltips = gtk.Tooltips()
65 tooltips.set_tip(self, _('''You can use special operation by pressing +, - or =. Plus/minus adds/decrease the variable to the current selected date. Equals set part of selected date. Available variables: 12h = 12 hours, 8d = 8 days, 4w = 4 weeks, 1m = 1 month, 2y = 2 years. Some examples:
66 * +21d : adds 21 days to selected year
67 * =23w : set date to the 23th week of the year
68 * -4m : decrease 4 months to the current date
69 You can also use "=" to set the date to the current date/time and '-' to clear the field.'''))
70 tooltips.enable()
72 def _on_insert_text(self, editable, value, length, position):
73 if not self._interactive_input:
74 return
76 if self.mode_cmd:
77 if self.callback: self.callback(value)
78 self.stop_emission('insert-text')
79 return
81 pos = self.get_position()
82 if length != 1:
83 # TODO: Implement paste
84 self.stop_emission('insert-text')
85 return
87 text = self.get_text()
89 text = text[:pos] + value + text[pos + 1:]
90 if self.regex.match(text):
91 pos += 1
92 while (pos<len(self.initial_value)) and (self.initial_value[pos] not in ['_','0','X']):
93 pos += 1
94 self.set_text(text)
95 gobject.idle_add(self.set_position, pos)
96 self.stop_emission('insert-text')
97 self.show()
98 return
100 def _on_delete_text(self, editable, start, end):
101 if not self._interactive_input:
102 return
104 #if end - start != 1:
105 # #TODO: cut/delete several
106 # self.stop_emission('delete-text')
107 # return
109 while (start>0) and (self.initial_value[start] not in ['_','0','X']):
110 start -= 1
111 text = self.get_text()
112 text = text[:start] + self.initial_value[start:end] + text[end:]
113 self.set_text(text)
114 gobject.idle_add(self.set_position, start)
115 self.stop_emission('delete-text')
116 return
118 def _focus_out(self, args, args2):
119 self.date_get()
120 if self.mode_cmd:
121 self.mode_cmd = False
122 if self.callback_process: self.callback_process(False, self, False)
124 def set_text(self, text):
125 self._interactive_input = False
126 try:
127 gtk.Entry.set_text(self, text)
128 finally:
129 self._interactive_input = True
131 def date_set(self, dt):
132 if dt:
133 self.set_text( dt.strftime(self.format) )
134 else:
135 self.set_text(self.initial_value)
137 def date_get(self):
138 tt = time.strftime(self.format, time.localtime())
139 tc = self.get_text()
140 if tc==self.initial_value:
141 return False
142 for a in range(len(self.initial_value)):
143 if self.initial_value[a] == tc[a]:
144 tc = tc[:a] + tt[a] + tc[a+1:]
145 try:
146 self.set_text(tc)
147 return time.strptime(tc, self.format)
148 except:
149 tc = tt
150 self.set_text(tc)
151 return time.strptime(tc, self.format)
153 def delete_text(self, start, end):
154 print 'DELETE TEXT'
155 self._interactive_input = False
156 try:
157 gtk.Entry.delete_text(self, start, end)
158 finally:
159 self._interactive_input = True
161 def insert_text(self, text, position=0):
162 self._interactive_input = False
163 try:
164 gtk.Entry.insert_text(self, text, position)
165 finally:
166 self._interactive_input = True
168 def clear(self):
169 self.set_text(self.initial_value)
171 def _on_key_press(self, editable, event):
172 if event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Escape, gtk.keysyms.Return):
173 if self.mode_cmd:
174 self.mode_cmd = False
175 if self.callback_process: self.callback_process(False, self, event)
176 self.stop_emission("key-press-event")
177 return True
178 elif event.keyval in (ord('+'),ord('-'),ord('=')):
179 self.mode_cmd = True
180 self.date_get()
181 if self.callback_process: self.callback_process(True, self, event)
182 self.stop_emission("key-press-event")
183 return True
184 elif self.mode_cmd:
185 if self.callback: self.callback(event)
186 return True
187 return False
189 class CmdEntry(gtk.Label):
190 pass
192 class ComplexEntry(gtk.HBox):
193 def __init__(self, format, *args, **argv):
194 super(ComplexEntry, self).__init__(*args, **argv)
195 self.widget = DateEntry(
196 format,
197 self._date_cb,
198 self._process_cb
200 self.widget.set_position(0)
201 self.widget.select_region(0, 0)
202 self.widget_cmd = CmdEntry()
203 self.widget_cmd.hide()
204 self.pack_start(self.widget, expand=True, fill=True)
205 self.pack_start(self.widget_cmd, expand=False, fill=True)
207 def _date_cb(self, event):
208 if event.keyval in (gtk.keysyms.BackSpace,):
209 text = self.widget_cmd.get_text()[:-1]
210 self.widget_cmd.set_text(text)
211 return True
212 if event.keyval<250:
213 value = chr(event.keyval)
214 text = self.widget_cmd.get_text()
215 self.widget_cmd.set_text(text+value)
216 return True
218 def _process_cb(self, ok, widget, event=None):
219 if ok:
220 self.widget_cmd.show()
221 self._date_cb(event)
222 else:
223 data = self.widget.get_text()
224 if (not event.keyval == gtk.keysyms.Escape) or not event:
225 cmd = self.widget_cmd.get_text()
226 for r,f in tools.date_operation.items():
227 groups = re.match(r, cmd)
228 if groups:
229 dt = self.widget.date_get()
230 if not dt:
231 dt = time.strftime(self.widget.format, time.localtime())
232 dt = time.strptime(dt, self.widget.format)
233 self.widget.date_set(f(dt,groups))
234 break
236 # Compute HERE using DATA and setting WIDGET
237 pass
238 self.widget_cmd.set_text('')
239 self.widget_cmd.hide()
241 if __name__ == '__main__':
242 import sys
243 def main(args):
244 win = gtk.Window()
245 win.set_title('gtk.Entry subclass')
246 def cb(window, event):
247 gtk.main_quit()
248 win.connect('delete-event', cb)
250 widget = ComplexEntry('%d/%m/%Y %H:%M:%S')
251 win.add(widget)
253 win.show_all()
254 gtk.main()
256 sys.exit(main(sys.argv))