9 from notebook
import Notebook
10 from shell_buffer
import ShellBuffer
11 from shell_view
import ShellView
13 from format_escaped
import format_escaped
14 from optparse
import OptionParser
18 usage
= "usage: %prog [options]"
19 op
= OptionParser(usage
=usage
)
20 op
.add_option("-u", "--ui", type="choice", choices
=("standard", "hildon", "rox"),
21 default
="standard", help=("which user interface to use (standard, rox or "
22 "hildon), default=%default"))
23 op
.add_option("-d", "--debug", action
="store_true",
24 help=("enable internal debug messages"))
26 options
, args
= op
.parse_args()
31 logging
.basicConfig(level
=logging
.DEBUG
)
33 if options
.ui
== "hildon":
37 except ImportError, e
:
38 print >>sys
.stderr
, "Error importing hildon. Falling back to standard ui."
39 elif options
.ui
== "rox":
53 buf
= ShellBuffer(notebook
)
55 view
.modify_font(pango
.FontDescription("monospace"))
56 buf
= view
.get_buffer()
58 ui_manager
= gtk
.UIManager()
59 w
.add_accel_group(ui_manager
.get_accel_group())
62 if not confirm_discard('Save the unchanged changes to worksheet "%s" before quitting?', '_Quit without saving'):
70 view
.emit('cut-clipboard')
73 view
.emit('copy-clipboard')
75 def on_copy_as_doctests(action
):
76 view
.get_buffer().copy_as_doctests(view
.get_clipboard(gtk
.gdk
.SELECTION_CLIPBOARD
))
79 view
.emit('paste-clipboard')
81 def on_delete(action
):
82 buf
.delete_selection(True, view
.get_editable())
84 def confirm_discard(message_format
, continue_button_text
):
85 if not buf
.code_modified
:
88 if buf
.filename
== None:
89 save_button_text
= gtk
.STOCK_SAVE_AS
91 save_button_text
= gtk
.STOCK_SAVE
93 if buf
.filename
== None:
94 name
= "Unsaved Worksheet"
98 message
= format_escaped("<big><b>" + message_format
+ "</b></big>", name
)
100 dialog
= gtk
.MessageDialog(parent
=w
, buttons
=gtk
.BUTTONS_NONE
,
101 type=gtk
.MESSAGE_WARNING
)
102 dialog
.set_markup(message
)
104 dialog
.add_buttons(continue_button_text
, gtk
.RESPONSE_OK
,
105 gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
107 dialog
.set_default_response(1)
108 response
= dialog
.run()
111 if response
== gtk
.RESPONSE_OK
:
114 if buf
.filename
== None:
119 if buf
.code_modified
:
127 if not confirm_discard('Discard unsaved changes to worksheet "%s"?', '_Discard'):
133 notebook
.set_path([os
.path
.dirname(os
.path
.abspath(filename
))])
134 if not os
.path
.exists(filename
):
135 buf
.filename
= filename
142 if not confirm_discard('Discard unsaved changes to worksheet "%s"?', '_Discard'):
146 chooser
= hildon
.FileChooserDialog(w
, gtk
.FILE_CHOOSER_ACTION_OPEN
)
148 chooser
= gtk
.FileChooserDialog("Open Worksheet...", w
, gtk
.FILE_CHOOSER_ACTION_OPEN
,
149 (gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
150 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
))
151 chooser
.set_default_response(gtk
.RESPONSE_OK
)
152 response
= chooser
.run()
154 if response
== gtk
.RESPONSE_OK
:
155 filename
= chooser
.get_filename()
163 if buf
.filename
== None:
170 rox_ui
.save_with_rox_savebox(buf
, notebook
)
174 chooser
= hildon
.FileChooserDialog(w
, gtk
.FILE_CHOOSER_ACTION_SAVE
)
176 chooser
= gtk
.FileChooserDialog("Save As...", w
, gtk
.FILE_CHOOSER_ACTION_SAVE
,
177 (gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
178 gtk
.STOCK_SAVE
, gtk
.RESPONSE_OK
))
179 chooser
.set_default_response(gtk
.RESPONSE_OK
)
180 response
= chooser
.run()
182 if response
== gtk
.RESPONSE_OK
:
183 filename
= chooser
.get_filename()
187 notebook
.set_path([os
.path
.dirname(os
.path
.abspath(filename
))])
191 def on_save_as(action
):
194 def find_program_in_path(progname
):
196 path
= os
.environ
['PATH']
200 for dir in path
.split(os
.pathsep
):
201 p
= os
.path
.join(dir, progname
)
202 if os
.path
.exists(p
):
207 def find_url_open_program():
208 for progname
in ['xdg-open', 'htmlview', 'gnome-open']:
209 path
= find_program_in_path(progname
)
214 def open_url(dialog
, url
):
215 prog
= find_url_open_program()
216 os
.spawnl(os
.P_NOWAIT
, prog
, prog
, url
)
218 def on_about(action
):
219 if find_url_open_program() != None:
220 gtk
.about_dialog_set_url_hook(open_url
)
222 dialog
= gtk
.AboutDialog()
223 dialog
.set_transient_for(w
)
224 dialog
.set_name("Reinteract")
225 dialog
.set_copyright("Copyright \302\251 2007 Owen Taylor, Red Hat, Inc., and others")
226 dialog
.set_website("http://www.reinteract.org")
227 dialog
.connect("response", lambda d
, r
: d
.destroy())
233 # This is a hack to work around the fact that scroll_mark_onscreen()
234 # doesn't wait for a size-allocate cycle, so doesn't properly handle
235 # embedded request widgets
237 view
.size_allocate((view
.allocation
.x
, view
.allocation
.y
,
238 view
.allocation
.width
, view
.allocation
.height
))
240 view
.scroll_mark_onscreen(buf
.get_insert())
242 def on_calculate(action
):
245 action_group
= gtk
.ActionGroup("main")
246 action_group
.add_actions([
247 ('file', None, "_File"),
248 ('edit', None, "_Edit"),
249 ('help', None, "_Help"),
250 ('new', gtk
.STOCK_NEW
, None, None, None, on_new
),
251 ('open', gtk
.STOCK_OPEN
, None, None, None, on_open
),
252 ('save', gtk
.STOCK_SAVE
, None, None, None, on_save
),
253 ('save-as', gtk
.STOCK_SAVE_AS
, None, None, None, on_save_as
),
254 ('quit', gtk
.STOCK_QUIT
, None, None, None, on_quit
),
255 ('cut', gtk
.STOCK_CUT
, None, None, None, on_cut
),
256 ('copy', gtk
.STOCK_COPY
, None, None, None, on_copy
),
262 on_copy_as_doctests
),
263 ('paste', gtk
.STOCK_PASTE
, None, None, None, on_paste
),
264 ('delete', gtk
.STOCK_DELETE
, None, None, None, on_delete
),
265 ('about', gtk
.STOCK_ABOUT
, None, None, None, on_about
),
266 ('calculate', gtk
.STOCK_REFRESH
, "_Calculate", '<control>Return', None, on_calculate
),
269 ui_manager
.insert_action_group(action_group
, 0)
272 menu_element
= 'popup'
274 menu_element
= 'menubar'
278 <%(menu_element)s name="TopMenu">
280 <menuitem action="new"/>
281 <menuitem action="open"/>
283 <menuitem action="save"/>
284 <menuitem action="save-as"/>
286 <menuitem action="quit"/>
289 <menuitem action="cut"/>
290 <menuitem action="copy"/>
291 <menuitem action="copy-as-doctests"/>
292 <menuitem action="paste"/>
293 <menuitem action="delete"/>
295 <menuitem action="calculate"/>
298 <menuitem action="about"/>
301 <toolbar name="ToolBar">
302 <toolitem action="calculate"/>
305 """ % { 'menu_element': menu_element
}
307 ui_manager
.add_ui_from_string(ui_string
)
308 ui_manager
.ensure_update()
310 menu
= ui_manager
.get_widget("/TopMenu")
311 toolbar
= ui_manager
.get_widget("/ToolBar")
315 w
.add_toolbar(toolbar
)
317 v
.pack_start(menu
, expand
=False, fill
=False)
318 v
.pack_start(toolbar
, expand
=False, fill
=False)
320 sw
= gtk
.ScrolledWindow()
321 sw
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
322 v
.pack_start(sw
, expand
=True, fill
=True)
326 w
.set_default_size(700, 800)
331 def update_title(*args
):
332 if buf
.code_modified
:
337 if buf
.filename
== None:
338 title
+= "Unsaved Worksheet"
340 title
+= os
.path
.basename(buf
.filename
)
342 title
+= " - Reinteract"
346 buf
.connect('filename-changed', update_title
)
347 buf
.connect('code-modified-changed', update_title
)
351 # We have a <Control>Return accelerator, but this hooks up <Control>KP_Enter as well;
352 # maybe someone wants that
353 def on_key_press_event(window
, event
):
354 if (event
.keyval
== 0xff0d or event
.keyval
== 0xff8d) and (event
.state
& gtk
.gdk
.CONTROL_MASK
!= 0):
359 w
.connect('key-press-event', on_key_press_event
)
364 # If you run reinteract from the command line, you'd expect to be able to
365 # create a worksheet, test it, then save it in the current directory, and
366 # have that act the same as loading the worksheet to start with. This is
367 # less obviously right when run from a menu item.
368 notebook
.set_path([os
.getcwd()])
371 settings
= w
.get_settings()
372 settings
.set_property("gtk-button-images", False)
373 settings
.set_property("gtk-menu-images", False)
377 def on_delete_event(window
, event
):
381 w
.connect('delete-event', on_delete_event
)