1#!/usr/bin/env python 2 3# This is a sample implementation of an editor. 4 5import os 6 7import pygtk 8pygtk.require('2.0') 9import gtk 10import dialogs 11 12BLOCK_SIZE = 2048 13RESPONSE_FORWARD = 1 14 15class EditWindow(gtk.Window): 16 def __init__(self, quit_cb=None): 17 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) 18 self.set_size_request(470, 300) 19 self.connect("delete_event", self.file_exit) 20 self.quit_cb = quit_cb 21 self.vbox = gtk.VBox() 22 self.add(self.vbox) 23 self.vbox.show() 24 hdlbox = gtk.HandleBox() 25 self.vbox.pack_start(hdlbox, expand=False) 26 hdlbox.show() 27 self.menubar, self.toolbar = self.create_menu() 28 hdlbox.add(self.menubar) 29 self.menubar.show() 30 self.vbox.pack_start(self.toolbar, expand=False) 31 self.scrolledwin = gtk.ScrolledWindow() 32 self.scrolledwin.show() 33 self.vbox.pack_start(self.scrolledwin) 34 self.text = gtk.TextView() 35 self.text.set_editable(True) 36 self.scrolledwin.add(self.text) 37 self.text.show() 38 self.buffer = self.text.get_buffer() 39 self.dirty = 0 40 self.file_new() 41 self.text.grab_focus() 42 self.clipboard = gtk.Clipboard(selection='CLIPBOARD') 43 self.dirname = None 44 self.search_string = None 45 self.last_search_iter = None 46 return 47 def load_file(self, fname): 48 try: 49 fd = open(fname) 50 self.buffer.set_text('') 51 buf = fd.read(BLOCK_SIZE) 52 while buf != '': 53 self.buffer.insert_at_cursor(buf) 54 buf = fd.read(BLOCK_SIZE) 55 self.text.queue_draw() 56 self.set_title(os.path.basename(fname)) 57 self.fname = fname 58 self.dirname = os.path.dirname(self.fname) 59 self.buffer.set_modified(False) 60 self.new = 0 61 except: 62 dlg = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT, 63 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, 64 "Can't open " + fname) 65 resp = dlg.run() 66 dlg.hide() 67 return 68 def create_menu(self): 69 ui_string = """<ui> 70 <menubar> 71 <menu name='FileMenu' action='FileMenu'> 72 <menuitem action='FileNew'/> 73 <menuitem action='FileOpen'/> 74 <menuitem action='FileSave'/> 75 <menuitem action='FileSaveAs'/> 76 <separator/> 77 <menuitem action='FileExit'/> 78 </menu> 79 <menu name='EditMenu' action='EditMenu'> 80 <menuitem action='EditCut'/> 81 <menuitem action='EditCopy'/> 82 <menuitem action='EditPaste'/> 83 <menuitem action='EditClear'/> 84 <separator/> 85 <menuitem action='EditFind'/> 86 <menuitem action='EditFindNext'/> 87 </menu> 88 <placeholder name='OtherMenus'/> 89 <menu name='HelpMenu' action='HelpMenu'> 90 <menuitem action='HelpAbout'/> 91 </menu> 92 </menubar> 93 <toolbar> 94 <toolitem action='FileNew'/> 95 <toolitem action='FileOpen'/> 96 <toolitem action='FileSave'/> 97 <toolitem action='FileSaveAs'/> 98 <separator/> 99 <toolitem action='EditCut'/> 100 <toolitem action='EditCopy'/> 101 <toolitem action='EditPaste'/> 102 <toolitem action='EditClear'/> 103 </toolbar> 104 </ui> 105 """ 106 actions = [ 107 ('FileMenu', None, '_File'), 108 ('FileNew', gtk.STOCK_NEW, None, None, None, self.file_new), 109 ('FileOpen', gtk.STOCK_OPEN, None, None, None, self.file_open), 110 ('FileSave', gtk.STOCK_SAVE, None, None, None, self.file_save), 111 ('FileSaveAs', gtk.STOCK_SAVE_AS, None, None, None, 112 self.file_saveas), 113 ('FileExit', gtk.STOCK_QUIT, None, None, None, self.file_exit), 114 ('EditMenu', None, '_Edit'), 115 ('EditCut', gtk.STOCK_CUT, None, None, None, self.edit_cut), 116 ('EditCopy', gtk.STOCK_COPY, None, None, None, self.edit_copy), 117 ('EditPaste', gtk.STOCK_PASTE, None, None, None, self.edit_paste), 118 ('EditClear', gtk.STOCK_REMOVE, 'C_lear', None, None, 119 self.edit_clear), 120 ('EditFind', gtk.STOCK_FIND, None, None, None, self.edit_find), 121 ('EditFindNext', None, 'Find _Next', "F3", None, 122 self.edit_find_next), 123 ('HelpMenu', gtk.STOCK_HELP), 124 ('HelpAbout', None, 'A_bout', None, None, self.help_about), 125 ] 126 self.ag = gtk.ActionGroup('edit') 127 self.ag.add_actions(actions) 128 self.ui = gtk.UIManager() 129 self.ui.insert_action_group(self.ag, 0) 130 self.ui.add_ui_from_string(ui_string) 131 self.add_accel_group(self.ui.get_accel_group()) 132 return (self.ui.get_widget('/menubar'), self.ui.get_widget('/toolbar')) 133 134 def chk_save(self): 135 if self.buffer.get_modified(): 136 dlg = gtk.Dialog('Unsaved File', self, 137 gtk.DIALOG_DESTROY_WITH_PARENT, 138 (gtk.STOCK_YES, gtk.RESPONSE_YES, 139 gtk.STOCK_NO, gtk.RESPONSE_NO, 140 gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) 141 lbl = gtk.Label((self.fname or "Untitled")+ 142 " has not been saved\n" + 143 "Do you want to save it?") 144 lbl.show() 145 dlg.vbox.pack_start(lbl) 146 ret = dlg.run() 147 dlg.hide() 148 if ret == gtk.RESPONSE_NO: 149 return 0 150 if ret == gtk.RESPONSE_YES: 151 if self.file_save(): 152 return 0 153 return 1 154 return 0 155 156 def file_new(self, mi=None): 157 if self.chk_save(): return 158 self.buffer.set_text('') 159 self.buffer.set_modified(False) 160 self.fname = None 161 self.set_title("Untitled") 162 self.new = 1 163 return 164 def file_open(self, mi=None): 165 if self.chk_save(): return 166 fname = dialogs.OpenFile('Open File', self, self.dirname, self.fname) 167 if not fname: return 168 self.load_file(fname) 169 return 170 def file_save(self, mi=None): 171 if self.new: 172 return self.file_saveas() 173 ret = False 174 try: 175 start, end = self.buffer.get_bounds() 176 blockend = start.copy() 177 fd = open(self.fname, "w") 178 while blockend.forward_chars(BLOCK_SIZE): 179 buf = self.buffer.get_text(start, blockend) 180 fd.write(buf) 181 start = blockend.copy() 182 buf = self.buffer.get_text(start, blockend) 183 fd.write(buf) 184 fd.close() 185 self.buffer.set_modified(False) 186 ret = True 187 except: 188 dlg = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT, 189 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, 190 "Error saving file " + self.fname) 191 resp = dlg.run() 192 dlg.hide() 193 return ret 194 def file_saveas(self, mi=None): 195 fname = dialogs.SaveFile('Save File As', self, self.dirname, 196 self.fname) 197 if not fname: return False 198 self.fname = fname 199 self.dirname = os.path.dirname(self.fname) 200 self.set_title(os.path.basename(fname)) 201 self.new = 0 202 return self.file_save() 203 def file_exit(self, mi=None, event=None): 204 if self.chk_save(): return True 205 self.hide() 206 self.destroy() 207 if self.quit_cb: self.quit_cb(self) 208 return False 209 def edit_cut(self, mi): 210 self.buffer.cut_clipboard(self.clipboard, True) 211 return 212 def edit_copy(self, mi): 213 self.buffer.copy_clipboard(self.clipboard) 214 return 215 def edit_paste(self, mi): 216 self.buffer.paste_clipboard(self.clipboard, None, True) 217 return 218 def edit_clear(self, mi): 219 self.buffer.delete_selection(True, True) 220 return 221 def _search(self, search_string, iter = None): 222 if iter is None: 223 start = self.buffer.get_start_iter() 224 else: 225 start = iter 226 i = 0 227 if search_string: 228 self.search_string = search_string 229 res = start.forward_search(search_string, gtk.TEXT_SEARCH_TEXT_ONLY) 230 if res: 231 match_start, match_end = res 232 self.buffer.place_cursor(match_start) 233 self.buffer.select_range(match_start, match_end) 234 self.text.scroll_to_iter(match_start, 0.0) 235 self.last_search_iter = match_end 236 237 else: 238 self.search_string = None 239 self.last_search_iter = None 240 241 def edit_find(self, mi): 242 def dialog_response_callback(dialog, response_id): 243 if response_id == gtk.RESPONSE_CLOSE: 244 dialog.destroy() 245 return 246 self._search(search_text.get_text(), self.last_search_iter) 247 search_text = gtk.Entry() 248 s = self.buffer.get_selection_bounds() 249 if len(s) > 0: 250 search_text.set_text(self.buffer.get_slice(s[0], s[1])) 251 dialog = gtk.Dialog("Search", self, 252 gtk.DIALOG_DESTROY_WITH_PARENT, 253 (gtk.STOCK_FIND, RESPONSE_FORWARD, 254 gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) 255 dialog.vbox.pack_end(search_text, True, True, 0) 256 dialog.connect("response", dialog_response_callback) 257 search_text.show() 258 search_text.grab_focus() 259 dialog.show_all() 260 response_id = dialog.run() 261 def edit_find_next(self, mi): 262 self._search(self.search_string, self.last_search_iter) 263 def help_about(self, mi): 264 dlg = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT, 265 gtk.MESSAGE_INFO, gtk.BUTTONS_OK, 266 "Copyright (C)\n" 267 "1998 James Henstridge\n" 268 "2004 John Finlay\n" 269 "This program is covered by the GPL>=2") 270 dlg.run() 271 dlg.hide() 272 return 273 274def edit(fname, mainwin=False): 275 if mainwin: quit_cb = lambda w: gtk.main_quit() 276 else: quit_cb = None 277 w = EditWindow(quit_cb=quit_cb) 278 w.load_file(fname) 279 w.show() 280 w.set_size_request(0,0) 281 if mainwin: gtk.main() 282 return 283 284if __name__ == '__main__': 285 import sys 286 edit(sys.argv[-1], mainwin=True) 287