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
19 # Author(s): Johan Dahlin (Async Open Source)
31 from mx
.DateTime
import RelativeDateTime
32 from mx
.DateTime
import DateTime
33 from mx
.DateTime
import now
34 from mx
.DateTime
import strptime
37 '%y': ('__', '[_0-9][_0-9]'),
38 '%Y': ('____', '[_1-9][_0-9][_0-9][_0-9]'),
39 '%m': ('__', '[_0-1][_0-9]'),
40 '%d': ('__', '[_0-3][_0-9]'),
41 '%H': ('__', '[_0-2][_0-9]'),
42 '%M': ('__', '[_0-6][_0-9]'),
43 '%S': ('__', '[_0-6][_0-9]'),
46 class DateEntry(gtk
.Entry
):
47 def __init__(self
, format
, callback
=None, callback_process
=None):
48 super(DateEntry
, self
).__init
__()
49 self
.modify_font(pango
.FontDescription("monospace"))
52 self
.regex
= self
.initial_value
= format
53 for key
,val
in mapping
.items():
54 self
.regex
= self
.regex
.replace(key
, val
[1])
55 self
.initial_value
= self
.initial_value
.replace(key
, val
[0])
57 self
.set_text(self
.initial_value
)
58 self
.regex
= re
.compile(self
.regex
)
60 assert self
.regex
.match(self
.initial_value
), 'Error, the initial value should be validated by regex'
61 self
.set_width_chars(len(self
.initial_value
))
62 self
.set_max_length(len(self
.initial_value
))
64 self
.connect('key-press-event', self
._on
_key
_press
)
65 self
.connect('insert-text', self
._on
_insert
_text
)
66 self
.connect('delete-text', self
._on
_delete
_text
)
68 self
.connect('focus-out-event', self
._focus
_out
)
69 self
.callback
= callback
70 self
.callback_process
= callback_process
72 self
._interactive
_input
= True
74 gobject
.idle_add(self
.set_position
, 0)
75 tooltips
= gtk
.Tooltips()
76 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:
77 * +21d : adds 21 days to selected year
78 * =23w : set date to the 23th week of the year
79 * -4m : decrease 4 months to the current date
80 You can also use "=" to set the date to the current date/time and '-' to clear the field.'''))
83 def _on_insert_text(self
, editable
, value
, length
, position
):
84 if not self
._interactive
_input
:
88 if self
.callback
: self
.callback(value
)
89 self
.stop_emission('insert-text')
92 pos
= self
.get_position()
94 # TODO: Implement paste
95 self
.stop_emission('insert-text')
98 text
= self
.get_text()
100 text
= text
[:pos
] + value
+ text
[pos
+ 1:]
101 if self
.regex
.match(text
):
103 while (pos
<len(self
.initial_value
)) and (self
.initial_value
[pos
] not in ['_','0','X']):
106 gobject
.idle_add(self
.set_position
, pos
)
107 self
.stop_emission('insert-text')
111 def _on_delete_text(self
, editable
, start
, end
):
112 if not self
._interactive
_input
:
115 #if end - start != 1:
116 # #TODO: cut/delete several
117 # self.stop_emission('delete-text')
120 while (start
>0) and (self
.initial_value
[start
] not in ['_','0','X']):
122 text
= self
.get_text()
123 text
= text
[:start
] + self
.initial_value
[start
:end
] + text
[end
:]
125 gobject
.idle_add(self
.set_position
, start
)
126 self
.stop_emission('delete-text')
129 def _focus_out(self
, args
, args2
):
132 self
.mode_cmd
= False
133 if self
.callback_process
: self
.callback_process(False, self
, False)
135 def set_text(self
, text
):
136 self
._interactive
_input
= False
138 gtk
.Entry
.set_text(self
, text
)
140 self
._interactive
_input
= True
142 def date_set(self
, dt
):
144 self
.set_text( dt
.strftime(self
.format
) )
146 self
.set_text(self
.initial_value
)
149 tt
= time
.strftime(self
.format
, time
.localtime())
151 if tc
==self
.initial_value
:
153 for a
in range(len(self
.initial_value
)):
154 if self
.initial_value
[a
] == tc
[a
]:
155 tc
= tc
[:a
] + tt
[a
] + tc
[a
+1:]
158 return strptime(tc
, self
.format
)
162 return strptime(tc
, self
.format
)
164 def delete_text(self
, start
, end
):
166 self
._interactive
_input
= False
168 gtk
.Entry
.delete_text(self
, start
, end
)
170 self
._interactive
_input
= True
172 def insert_text(self
, text
, position
=0):
173 self
._interactive
_input
= False
175 gtk
.Entry
.insert_text(self
, text
, position
)
177 self
._interactive
_input
= True
180 self
.set_text(self
.initial_value
)
182 def _on_key_press(self
, editable
, event
):
183 if event
.keyval
in (gtk
.keysyms
.Tab
, gtk
.keysyms
.Escape
, gtk
.keysyms
.Return
):
185 self
.mode_cmd
= False
186 if self
.callback_process
: self
.callback_process(False, self
, event
)
187 self
.stop_emission("key-press-event")
189 elif event
.keyval
in (ord('+'),ord('-'),ord('=')):
192 if self
.callback_process
: self
.callback_process(True, self
, event
)
193 self
.stop_emission("key-press-event")
196 if self
.callback
: self
.callback(event
)
200 class CmdEntry(gtk
.Label
):
203 class ComplexEntry(gtk
.HBox
):
204 def __init__(self
, format
, *args
, **argv
):
205 super(ComplexEntry
, self
).__init
__(*args
, **argv
)
206 self
.widget
= DateEntry(
211 self
.widget
.set_position(0)
212 self
.widget
.select_region(0, 0)
213 self
.widget_cmd
= CmdEntry()
214 self
.widget_cmd
.hide()
215 self
.pack_start(self
.widget
, expand
=True, fill
=True)
216 self
.pack_start(self
.widget_cmd
, expand
=False, fill
=True)
218 def _date_cb(self
, event
):
219 if event
.keyval
in (gtk
.keysyms
.BackSpace
,):
220 text
= self
.widget_cmd
.get_text()[:-1]
221 self
.widget_cmd
.set_text(text
)
224 value
= chr(event
.keyval
)
225 text
= self
.widget_cmd
.get_text()
226 self
.widget_cmd
.set_text(text
+value
)
229 def _process_cb(self
, ok
, widget
, event
=None):
231 self
.widget_cmd
.show()
234 data
= self
.widget
.get_text()
235 if (not event
.keyval
== gtk
.keysyms
.Escape
) or not event
:
237 '^=(\d+)w$': lambda dt
,r
: dt
+RelativeDateTime(day
=0, month
=0, weeks
= int(r
.group(1))),
238 '^=(\d+)d$': lambda dt
,r
: dt
+RelativeDateTime(day
=int(r
.group(1))),
239 '^=(\d+)m$': lambda dt
,r
: dt
+RelativeDateTime(day
=0, month
= int(r
.group(1))),
240 '^=(2\d\d\d)y$': lambda dt
,r
: dt
+RelativeDateTime(year
= int(r
.group(1))),
241 '^=(\d+)h$': lambda dt
,r
: dt
+RelativeDateTime(hour
= int(r
.group(1))),
242 '^([\\+-]\d+)h$': lambda dt
,r
: dt
+RelativeDateTime(hours
= int(r
.group(1))),
243 '^([\\+-]\d+)w$': lambda dt
,r
: dt
+RelativeDateTime(days
= 7*int(r
.group(1))),
244 '^([\\+-]\d+)d$': lambda dt
,r
: dt
+RelativeDateTime(days
= int(r
.group(1))),
245 '^([\\+-]\d+)m$': lambda dt
,r
: dt
+RelativeDateTime(months
= int(r
.group(1))),
246 '^([\\+-]\d+)y$': lambda dt
,r
: dt
+RelativeDateTime(years
= int(r
.group(1))),
247 '^=$': lambda dt
,r
: now(),
248 '^-$': lambda dt
,r
: False
250 cmd
= self
.widget_cmd
.get_text()
251 for r
,f
in lst
.items():
252 groups
= re
.match(r
, cmd
)
254 dt
= self
.widget
.date_get()
256 dt
= time
.strftime(self
.widget
.format
, time
.localtime())
257 dt
= strptime(dt
, self
.widget
.format
)
258 self
.widget
.date_set(f(dt
,groups
))
261 # Compute HERE using DATA and setting WIDGET
263 self
.widget_cmd
.set_text('')
264 self
.widget_cmd
.hide()
266 if __name__
== '__main__':
270 win
.set_title('gtk.Entry subclass')
271 def cb(window
, event
):
273 win
.connect('delete-event', cb
)
275 widget
= ComplexEntry('%d/%m/%Y %H:%M:%S')
281 sys
.exit(main(sys
.argv
))