def __init__(self, thegame): self.timer_active = False self.timer_id = -1 self.thegame = thegame self.me = MidEnd(self, thegame) self.me.new_game() # 2174 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_title(thegame.name) vbox = gtk.VBox() self.window.add(vbox) vbox.show() self.accelgroup = gtk.AccelGroup() self.window.add_accel_group(self.accelgroup) hbox = gtk.HBox() vbox.pack_start(hbox, False, False) self.menubar = self.add_menubar(hbox) self.changed_preset() self.snaffle_colours() colmap = gtk.gdk.colormap_get_system() bg = [x * 65535 for x in self.colours[0] ] self.background = gtk.gdk.Color(bg[0], bg[1], bg[2]) colmap.alloc_color(self.background, False, False) if self.me.wants_statusbar(): viewport = gtk.Viewport() viewport.set_shadow_type(gtk.SHADOW_NONE) self.statusbar = gtk.Statusbar() viewport.add(self.statusbar) viewport.show() vbox.pack_end(viewport, False, False) self.statusbar.show() self.statusctx = self.statusbar.get_context_id('game') self.statusbar.push(self.statusctx, 'test') x, y = self.statusbar.size_request() viewport.set_size_request(x, y) else: self.statusbar = None self.statusctx = None self.area = gtk.DrawingArea() self.area.set_double_buffered(False) x, y = self.get_size() self.drawing_area_shrink_pending = False self.area.set_size_request(x, y) self.w = x self.h = y vbox.pack_end(self.area, True, True, 0) self.clear_backing_store() self.fonts = None self.nfonts = 0 self.fontsize = 0 self.paste_data = None self.window.connect('destroy', self.destroy); self.window.connect('key-press-event', self.key_event); self.area.connect('button-press-event', self.button_event); self.area.connect('button-release-event', self.button_event); self.area.connect('motion-notify-event', self.motion_event); self.area.connect('selection-get', self.selection_get); self.area.connect('selection-clear-event', self.selection_clear); self.area.connect('expose-event', self.expose_area); self.window.connect('map-event', self.map_window); self.area.connect('configure-event', self.configure_area); self.window.connect('configure-event', self.configure_window); self.window.connect('delete-event', self.destroy) self.window.show_all() self.set_window_background(0)
class FrontEnd(object): def __init__(self, thegame): self.timer_active = False self.timer_id = -1 self.thegame = thegame self.me = MidEnd(self, thegame) self.me.new_game() # 2174 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_title(thegame.name) vbox = gtk.VBox() self.window.add(vbox) vbox.show() self.accelgroup = gtk.AccelGroup() self.window.add_accel_group(self.accelgroup) hbox = gtk.HBox() vbox.pack_start(hbox, False, False) self.menubar = self.add_menubar(hbox) self.changed_preset() self.snaffle_colours() colmap = gtk.gdk.colormap_get_system() bg = [x * 65535 for x in self.colours[0] ] self.background = gtk.gdk.Color(bg[0], bg[1], bg[2]) colmap.alloc_color(self.background, False, False) if self.me.wants_statusbar(): viewport = gtk.Viewport() viewport.set_shadow_type(gtk.SHADOW_NONE) self.statusbar = gtk.Statusbar() viewport.add(self.statusbar) viewport.show() vbox.pack_end(viewport, False, False) self.statusbar.show() self.statusctx = self.statusbar.get_context_id('game') self.statusbar.push(self.statusctx, 'test') x, y = self.statusbar.size_request() viewport.set_size_request(x, y) else: self.statusbar = None self.statusctx = None self.area = gtk.DrawingArea() self.area.set_double_buffered(False) x, y = self.get_size() self.drawing_area_shrink_pending = False self.area.set_size_request(x, y) self.w = x self.h = y vbox.pack_end(self.area, True, True, 0) self.clear_backing_store() self.fonts = None self.nfonts = 0 self.fontsize = 0 self.paste_data = None self.window.connect('destroy', self.destroy); self.window.connect('key-press-event', self.key_event); self.area.connect('button-press-event', self.button_event); self.area.connect('button-release-event', self.button_event); self.area.connect('motion-notify-event', self.motion_event); self.area.connect('selection-get', self.selection_get); self.area.connect('selection-clear-event', self.selection_clear); self.area.connect('expose-event', self.expose_area); self.window.connect('map-event', self.map_window); self.area.connect('configure-event', self.configure_area); self.window.connect('configure-event', self.configure_window); self.window.connect('delete-event', self.destroy) self.window.show_all() self.set_window_background(0) def destroy(self, widget): self.me.free() gtk.main_quit() def key_event(self, widget, event): shift = MOD_SHIFT if event.state & gtk.gdk.SHIFT_MASK else 0 ctrl = MOD_CTRL if event.state & gtk.gdk.CONTROL_MASK else 0 if not self.backing_store_ok(): return True if self.window.activate_key(event): return True keyval = -1 if event.keyval == gtk.keysyms.Up: keyval = shift | ctrl | CURSOR_UP elif event.keyval == gtk.keysyms.KP_Up or event.keyval == gtk.keysyms.KP_8: keyval = MOD_NUM_KEYPAD | '8' elif event.keyval == gtk.keysyms.Down: keyval = shift | ctrl | CURSOR_DOWN elif event.keyval == gtk.keysyms.KP_Down or event.keyval == gtk.keysyms.KP_2: keyval = MOD_NUM_KEYPAD | '2' elif event.keyval == gtk.keysyms.Left: keyval = shift | ctrl | CURSOR_LEFT elif event.keyval == gtk.keysyms.KP_Left or event.keyval == gtk.keysyms.KP_4: keyval = MOD_NUM_KEYPAD | '4' elif event.keyval == gtk.keysyms.Right: keyval = shift | ctrl | CURSOR_RIGHT elif event.keyval == gtk.keysyms.KP_Right or event.keyval == gtk.keysyms.KP_6: keyval = MOD_NUM_KEYPAD | '6' elif event.keyval == gtk.keysyms.KP_Home or event.keyval == gtk.keysyms.KP_7: keyval = MOD_NUM_KEYPAD | '7' elif event.keyval == gtk.keysyms.KP_End or event.keyval == gtk.keysyms.KP_1: keyval = MOD_NUM_KEYPAD | '1' elif event.keyval == gtk.keysyms.KP_Page_Up or event.keyval == gtk.keysyms.KP_9: keyval = MOD_NUM_KEYPAD | '9' elif event.keyval == gtk.keysyms.KP_Page_Down or event.keyval == gtk.keysyms.KP_3: keyval = MOD_NUM_KEYPAD | '3' elif event.keyval == gtk.keysyms.KP_Insert or event.keyval == gtk.keysyms.KP_0: keyval = MOD_NUM_KEYPAD | '0' elif event.keyval == gtk.keysyms.KP_Begin or event.keyval == gtk.keysyms.KP_5: keyval = MOD_NUM_KEYPAD | '5' elif event.keyval == gtk.keysyms.BackSpace or \ event.keyval == gtk.keysyms.Delete or \ event.keyval == gtk.keysyms.KP_Delete: keyval = 0177 elif len(event.string) == 1: keyval = ord(event.string[0]) else: keyval = -1 if keyval >= 0 and not self.me.process_key(0, 0, keyval): self.window.destroy() return True def button_event(self, widget, event): if not self.backing_store_ok(): return True if event.type != gtk.gdk.BUTTON_PRESS and \ event.type != gtk.gdk.BUTTON_RELEASE: return True button = 0 if event.button == 2 or (event.state & gtk.gdk.SHIFT_MASK): button = MIDDLE_BUTTON elif event.button == 3 or (event.state & gtk.gdk.MOD1_MASK): button = RIGHT_BUTTON elif event.button == 1: button = LEFT_BUTTON else: return False # Could not determine what button! if event.type == gtk.gdk.BUTTON_RELEASE: button += LEFT_RELEAE - LEFT_BUTTON if not self.me.process_key(event.x - self.ox, event.y - self.oy, button): self.window.destroy() return True def clear_backing_store(self): self.image = None def motion_event(self, widget, event): if not self.backing_store_ok(): return True button = 0 if event.state & (gtk.gdk.BUTTON2_MASK | gtk.gdk.SHIFT_MASK): button = MIDDLE_DRAG elif event.state & gtk.gdk.BUTTON1_MASK: button = LEFT_DRAG elif event.state & gtk.gdk.BUTTON3_MASK: button = RIGHT_DRAG else: return False if not self.me.process_key(event.x - self.ox, event.y - self.oy, button): self.window.destroy() gtk.gdk.event_request_motions(event) return True def expose_area(self, widget, event): if self.backing_store_ok(): self.repaint_rectangle(widget, event.area.x, event.area.y, event.area.width, event.area.height) return True def map_window(self, widget, event): self.window.queue_draw() return True def configure_area(self, widget, event): if self.backing_store_ok(): self.teardown_backing_store() x = self.w = event.width y = self.h = event.height x, y = self.me.size(x, y, True) self.pw = x self.ph = y self.ox = (self.w - self.pw) / 2 self.oy = (self.h - self.ph) / 2 self.setup_backing_store() self.me.force_redraw() return True def configure_window(self, widget, event): return True def selection_get(self, widget, seldata, info, time_stamp): seldata.set(seldata.target, 8, self.paste_data) def selection_clear(self, widget, event): self.paste_data = None return True def backing_store_ok(self): return self.image != None def teardown_backing_store(self): self.image = None self.pixmap = None def setup_backing_store(self): self.pixmap = gtk.gdk.Pixmap(self.area.window, self.pw, self.ph) self.image = cairo.ImageSurface(cairo.FORMAT_RGB24, self.pw, self.ph) def color_paint(cr): cr.set_source_rgb(self.colours[0][0], self.colours[0][1], self.colours[0][2]) cr.paint() cr = cairo.Context(self.image) ; color_paint(cr) cr = self.pixmap.cairo_create() ; color_paint(cr) cr = self.area.window.cairo_create() ; color_paint(cr) def repaint_rectangle(self, widget, x, y, w, h): gc = gtk.gdk.GC(widget.window) gc.set_foreground(self.background) if x < self.ox: widget.window.draw_rectangle(gc, True, x, y, self.ox - x, h) w -= (self.ox - x) x = self.ox if y < self.oy: widget.window.draw_rectangle(gc, True, x, y, w, self.oy - y) h -= (self.oy - y) y = self.oy if w > self.pw: widget.window.draw_rectangle(gc, True, x + self.pw, y, w - self.pw, h) w = self.pw if h > self.ph: widget.window.draw_rectangle(gc, True, x, y + self.ph, w, h - self.ph) widget.window.draw_drawable(gc, self.pixmap, x - self.ox, y - self.oy, x, y, w, h) def status_bar(self, text): assert(self.statusbar) self.statusbar.pop(self.statusctx) self.statusbar.push(self.statusctx, text) def setup_drawing(self): self.cr = cairo.Context(self.image) self.cr.set_antialias(cairo.ANTIALIAS_GRAY) self.cr.set_line_width(1.0) self.cr.set_line_cap(cairo.LINE_CAP_SQUARE) self.cr.set_line_join(cairo.LINE_JOIN_ROUND) def teardown_drawing(self): self.cr = None cr = self.pixmap.cairo_create() cr.set_source_surface(self.image, 0, 0) cr.rectangle(self.bbox_l - 1, self.bbox_u - 1, self.bbox_r - self.bbox_l + 2, self.bbox_d - self.bbox_u + 2) cr.fill() def start_draw(self): self.bbox_l = self.w self.bbox_r = 0 self.bbox_u = self.h self.bbox_d = 0 self.setup_drawing() def clip(self, x, y, w, h): self.cr.new_path() self.cr.rectangle(x, y, w, h) self.cr.clip() def unclip(self): self.cr.reset_clip() def draw_rect(self, x, y, w, h, colour): self.set_colour(colour) self.cr.save() self.cr.new_path() self.cr.set_antialias(cairo.ANTIALIAS_NONE) self.cr.rectangle(x, y, w, h) self.cr.fill() self.cr.restore() def draw_line(self, x1, y1, x2, y2, colour): self.set_colour(colour) self.dr.new_path() self.cr.move_to(x1 + 0.5, y1 + 0.5) self.cr.line_to(x2 + 0.5, y2 + 0.5) self.cr.stroke() def draw_polygon(self, coords, fillcolour, outlinecolour): self.cr.new_path() for i in range(0, len(coords)/2, 2): self.cr.line_to(coords[i] + 0.5, coords[i+1] + 0.5) self.cr.close_path() if fillcolour >= 0: self.set_colour(fillcolour) self.cr.fill_preserve() assert(outlinecolour >= 0) self.set_colour(outlinecolour) self.cr.stroke() def draw_update(self, x, y, w, h): if self.bbox_l > x: self.bbox_l = x if self.bbox_r < x+w: self.bbox_r = x+w if self.bbox_u > y: self.bbox_u = y if self.bbox_d < y+h: self.bbox_d = y+h def draw_update(self, x, y, w, h): if (self.bbox_l > x ): self.bbox_l = x if (self.bbox_r < x+w): self.bbox_r = x+w if (self.bbox_u > y ): self.bbox_u = y if (self.bbox_d < y+h): self.bbox_d = y+h def end_draw(self): self.teardown_drawing() if self.bbox_l < self.bbox_r and self.bbox_u < self.bbox_d: self.repaint_rectangle(self.area, self.bbox_l - 1 + self.ox, self.bbox_u - 1 + self.oy, self.bbox_r - self.bbox_l + 2, self.bbox_d - self.bbox_u + 2) def get_size(self): x, y = self.me.size(sys.maxint, sys.maxint, False) return (x, y) def snaffle_colours(self): self.colours, self.ncolours = self.me.colours() def set_colour(self, colour): self.cr.set_source_rgb( self.colours[colour][0], self.colours[colour][1], self.colours[colour][2]) def default_colour(self): col = self.window.style.bg[gtk.STATE_NORMAL] return (col.red / 65535.0, col.green / 65565.0, col.blue / 65535.0) def set_window_background(self, colour): colmap = gtk.gdk.colormap_get_system() bg = [x * 65535 for x in self.colours[colour] ] self.background = gtk.gdk.Color(bg[0], bg[1], bg[2]) colmap.alloc_color(self.background, False, False) self.area.window.set_background(self.background) self.window.window.set_background(self.background) #self.window.modify_bg(gtk.STATE_NORMAL, self.background) def add_menubar(self, container): menubar = gtk.MenuBar() container.pack_start(menubar) menuitem = gtk.MenuItem('_Game') menubar.add(menuitem) menu = gtk.Menu() menuitem.set_submenu(menu) self.add_menu_item_with_key(menu, '_New', 'n') menuitem = gtk.MenuItem('Restart') menu.add(menuitem) menuitem.connect('activate', self.menu_restart_event) menuitem = gtk.MenuItem('Specific...') menuitem.set_data('user-data', CFG_DESC) menu.add(menuitem) menuitem.connect('activate', self.menu_config_event) menuitem = gtk.MenuItem('Random Seed...') menuitem.set_data('user-data', CFG_SEED) menu.add(menuitem) menuitem.connect('activate', self.menu_config_event) self.preset_radio = None self.preset_custom = None self.n_preset_menu_items = 0 self.preset_threaded = False n = self.me.num_presets() if n > 0 or self.thegame.can_configure: print "FE: got {} presets from me".format(n) menuitem = gtk.MenuItem('_Type') menubar.add(menuitem) submenu = gtk.Menu() menuitem.set_submenu(submenu) for i in range(n): params, name = self.me.fetch_preset(i) menuitem = gtk.RadioMenuItem(self.preset_radio, name) if not self.preset_radio: self.preset_radio = menuitem self.n_preset_menu_items += 1 submenu.add(menuitem) menuitem.set_data('user-data', params) menuitem.connect('activate', self.menu_preset_event) if self.thegame.can_configure: menuitem = gtk.RadioMenuItem(self.preset_radio, "Custom...") self.preset_custom = menuitem self.preset_radio = menuitem.get_group() submenu.add(menuitem) menuitem.set_data('user-data', CFG_SETTINGS) menuitem.connect('activate', self.menu_config_event) menu.add(gtk.MenuItem()) # separator menuitem = gtk.MenuItem('Load...') menu.add(menuitem) menuitem.connect('activate', self.menu_load_event) menuitem = gtk.MenuItem('Save...') menu.add(menuitem) menuitem.connect('activate', self.menu_save_event) menu.add(gtk.MenuItem()) # separator self.add_menu_item_with_key(menu, 'Undo', 'u') self.add_menu_item_with_key(menu, 'Redo', 'r') if self.thegame.can_solve: menu.add(gtk.MenuItem()) # separator menuitem = gtk.MenuItem('Solve') menu.add(menuitem) menuitem.connect('activate', self.menu_solve_event) menu.add(gtk.MenuItem()) # separator self.add_menu_item_with_key(menu, 'Exit', 'q') menuitem = gtk.MenuItem('_Help') menubar.add(menuitem) menu = gtk.Menu() menuitem.set_submenu(menu) menuitem = gtk.MenuItem('About') menu.add(menuitem) menuitem.connect('activate', self.menu_about_event) container.show_all() return menubar def add_menu_item_with_key(self, cont, text, key): menuitem = gtk.MenuItem(text) cont.add(menuitem) menuitem.set_data('user-data', key) menuitem.connect('activate', self.menu_key_event) accel_mod = 0 mask = ord(key) & ~0x1F if mask == 0x00: key += 0x60 + ord(key) accel_mod = gtk.gdk.CONTROL_MASK elif mask == 0x40: key += 0x20 accel_mod = gtk.gdk.SHIFT_MASK menuitem.add_accelerator('activate', self.accelgroup, ord(key), accel_mod, gtk.ACCEL_VISIBLE) def menu_key_event(self, menuitem): print 'FrontEnd.menu_key_event' key = menuitem.get_data('user-data') print 'key accelerator: {}'.format(key) # TODO: call self.me.process_key # TODO: conditionally call self.window.destroy() (on q) def changed_preset(self): n = self.me.which_preset() self.preset_threaded = True if n > 0 and self.preset_custom: self.preset_custom.set_active() else: gslist = self.preset_radio i = self.n_preset_menu_items - 1 - n if self.preset_custom: gslist = gslist[1:] if i < len(gslist): gslist[i].set_active(True) else: for item in self.preset_radio: item.set_active(False) self.preset_threaded = False def menu_solve_event(self, menuitem): print 'FrontEnd.menu_solve_event' def menu_restart_event(self, menuitem): print 'FrontEnd.menu_restart_event:' self.me.restart_game() def menu_config_event(self, menuitem): print 'FrontEnd.menu_config_event:' which = menuitem.get_data('user-data') if self.preset_threaded: # TODO: || (check-menu-item !active) return # TODO: self.changed_preset # TODO: self.get_config self.me.new_game() # TODO: self.resize def menu_load_event(self, menuitem): print 'FrontEnd.menu_load_event:' # TODO: implement def menu_save_event(self, menuitem): print 'FrontEnd.menu_save_event:' # TODO: implement def menu_about_event(self, menuitem): title = 'About {}'.format(self.thegame.name) textbuf = '''{} from Simon Tatham's Portable Puzzle Collection {}'''.format(self.thegame.name, VER) self.message_box(self.window, title, textbuf) def menu_preset_event(self, menuitem): params = menuitem.get_data('user-data') if self.preset_threaded or \ (type(menuitem) == type(gtk.CheckMenuItem()) and \ not menuitem.get_active()): return self.me.set_params(params) self.me.new_game() # self.resize_fe() def message_box(self, parent, title, msg, centre=True, type=MB_OK): window = gtk.Dialog(title, parent, gtk.DIALOG_MODAL) text = gtk.Label(msg) text.set_alignment(0.0, 0.0) hbox = gtk.HBox() hbox.pack_start(text, False, False, 20) window.vbox.pack_start(hbox, False, False, 20) window.set_title(title) text.set_line_wrap(True) if type == MB_OK: titles = { 'stock':[ gtk.STOCK_OK ], 'def':0, 'cancel':0 } else: titles = { 'stock':[ gtk.STOCK_NO, gtk.STOCK_YES ], 'def':1, 'cancel':0 } i = 0 for item in titles['stock']: button = gtk.Button(None, item, False) window.action_area.pack_end(button, False, False, 0) if i == titles['def']: button.set_flags(gtk.CAN_DEFAULT) window.set_default(button) if i == titles['cancel']: window.connect('key-press-event', self.win_key_press, button) button.connect('clicked', self.msgbox_button_clicked, window) button.set_data('user-data', i) i += 1 window.connect('destroy', gtk.main_quit) window.show_all() self.i = -1 gtk.main() if type == MB_OK: rc = True else: rc = self.i == 1 return rc def win_key_press(self, window, event, button): if event.keyval == gtk.keysyms.Escape: button.emit('clicked') return True return False def msgbox_button_clicked(self, button, window): self.i = button.get_data('user-data') window.destroy() def main(self): gtk.main()