
Property changes on: src/lib/gtkui
___________________________________________________________________
Name: svn:ignore
   - Makefile
Makefile.in

   + Makefile
Makefile.in
*.pyc



Index: src/lib/gtkui/__init__.py
===================================================================
--- src/lib/gtkui/__init__.py	(revision 5937)
+++ src/lib/gtkui/__init__.py	(working copy)
@@ -1,3 +1,4 @@
 #!/usr/bin/env python
 
-from gtkui import GtkView, GtkUI
+from gtkui import GtkUI
+from chessview import GtkView
Index: src/lib/gtkui/chessview.py
===================================================================
--- src/lib/gtkui/chessview.py	(revision 0)
+++ src/lib/gtkui/chessview.py	(revision 0)
@@ -0,0 +1,373 @@
+import gobject
+import gtk
+import gettext
+
+import gtkui
+import glchess.ui
+import glchess.chess
+
+# Optionally use OpenGL support
+# gtk.gtkgl throws RuntimeError when there is no OpenGL support
+# It should really just throw ImportError
+try:
+    import gtk.gtkgl
+    import OpenGL
+except (ImportError, RuntimeError):
+    haveGLSupport = False
+else:
+    haveGLSupport = True
+
+__all__ = ['GtkView']
+
+pieceNames = {glchess.chess.board.PAWN:   gettext.gettext('pawn'),
+              glchess.chess.board.ROOK:   gettext.gettext('rook'),
+              glchess.chess.board.KNIGHT: gettext.gettext('knight'),
+              glchess.chess.board.BISHOP: gettext.gettext('bishop'),
+              glchess.chess.board.QUEEN:  gettext.gettext('queen'),
+              glchess.chess.board.KING:   gettext.gettext('king')}
+
+class GtkViewArea(gtk.DrawingArea):
+    """Custom widget to render an OpenGL scene"""
+    # The view this widget is rendering
+    view = None
+
+    # Pixmaps to use for double buffering
+    pixmap = None
+    dynamicPixmap = None
+    
+    # Flag to show if this scene is to be rendered using OpenGL
+    renderGL = False
+    
+    # TODO...
+    __glDrawable = None
+    
+    def __init__(self, view):
+        """
+        """
+        gtk.DrawingArea.__init__(self)
+        
+        self.view = view
+
+        # Allow notification of button presses
+        self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.BUTTON_MOTION_MASK)
+        
+        # Make openGL drawable
+        if hasattr(gtk, 'gtkgl'):
+            gtk.gtkgl.widget_set_gl_capability(self, self.view.ui.notebook.glConfig)# FIXME:, share_list=glContext)
+
+        # Connect signals
+        self.connect('realize', self.__init)
+        self.connect('configure_event', self.__configure)
+        self.connect('expose_event', self.__expose)
+        self.connect('button_press_event', self.__button_press)
+        self.connect('button_release_event', self.__button_release)
+        
+    # Public methods
+        
+    def redraw(self):
+        """Request this widget is redrawn"""
+        # If the window is visible prepare it for redrawing
+        area = gtk.gdk.Rectangle(0, 0, self.allocation.width, self.allocation.height)
+        if self.window is not None:
+            self.window.invalidate_rect(area, False)
+
+    def setRenderGL(self, renderGL):
+        """Enable OpenGL rendering"""
+        if not haveGLSupport:
+            renderGL = False
+        
+        if self.renderGL == renderGL:
+            return
+        self.renderGL = renderGL
+        self.redraw()
+    
+    # Private methods
+
+    def __startGL(self):
+        """Get the OpenGL context"""
+        if not self.renderGL:
+            return
+
+        assert(self.__glDrawable is None)
+        
+        # Obtain a reference to the OpenGL drawable
+        # and rendering context.
+        glDrawable = gtk.gtkgl.widget_get_gl_drawable(self)
+        glContext = gtk.gtkgl.widget_get_gl_context(self)
+
+        # OpenGL begin.
+        if not glDrawable.gl_begin(glContext):
+            return
+        
+        self.__glDrawable = glDrawable
+
+        if not self.view.ui.openGLInfoPrinted:
+            print 'Using OpenGL:'
+            print 'VENDOR=' + OpenGL.GL.glGetString(OpenGL.GL.GL_VENDOR)
+            print 'RENDERER=' + OpenGL.GL.glGetString(OpenGL.GL.GL_RENDERER)
+            print 'VERSION=' + OpenGL.GL.glGetString(OpenGL.GL.GL_VERSION)
+            print 'EXTENSIONS=' + OpenGL.GL.glGetString(OpenGL.GL.GL_EXTENSIONS)
+            self.view.ui.openGLInfoPrinted = True
+        
+    def __endGL(self):
+        """Free the OpenGL context"""
+        if not self.renderGL:
+            return
+        
+        assert(self.__glDrawable is not None)
+        self.__glDrawable.gl_end()
+        self.__glDrawable = None
+        
+    def __init(self, widget):
+        """Gtk+ signal"""
+        if self.view.feedback is not None:
+            self.view.feedback.reshape(widget.allocation.width, widget.allocation.height)
+        
+    def __configure(self, widget, event):
+        """Gtk+ signal"""
+        self.pixmap = gtk.gdk.Pixmap(widget.window, event.width, event.height)
+        self.dynamicPixmap = gtk.gdk.Pixmap(widget.window, event.width, event.height)
+        self.__startGL()
+        if self.view.feedback is not None:
+            self.view.feedback.reshape(event.width, event.height)
+        self.__endGL()
+
+    def __expose(self, widget, event):
+        """Gtk+ signal"""
+        if self.renderGL:
+            self.__startGL()
+        
+            # Get the scene rendered
+            try:
+                if self.view.feedback is not None:
+                    self.view.feedback.renderGL()
+            except OpenGL.GLerror, e:
+                print 'Rendering Error: ' + str(e)
+                traceback.print_exc(file = sys.stdout)
+
+            # Paint this
+            if self.__glDrawable.is_double_buffered():
+                self.__glDrawable.swap_buffers()
+            else:
+                glFlush()
+
+            self.__endGL()
+            
+        else:
+            context = self.pixmap.cairo_create()
+            if self.view.feedback is not None:
+                self.view.feedback.renderCairoStatic(context)
+            
+            # Copy the background to render the dynamic elements on top
+            self.dynamicPixmap.draw_drawable(widget.get_style().white_gc, self.pixmap, 0, 0, 0, 0, -1, -1)
+            context = self.dynamicPixmap.cairo_create()
+        
+            # Set a clip region for the expose event
+            context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
+            context.clip()
+           
+            # Render the dynamic elements
+            if self.view.feedback is not None:
+                self.view.feedback.renderCairoDynamic(context)
+                
+            # Draw the window
+            widget.window.draw_drawable(widget.get_style().white_gc, self.dynamicPixmap,
+                                        event.area.x, event.area.y,
+                                        event.area.x, event.area.y, event.area.width, event.area.height)
+
+    def __button_press(self, widget, event):
+        """Gtk+ signal"""
+        self.__startGL()
+        if self.view.feedback is not None:
+            self.view.feedback.select(event.x, event.y)
+        self.__endGL()
+        
+    def __button_release(self, widget, event):
+        """Gtk+ signal"""
+        self.__startGL()
+        if self.view.feedback is not None:
+            self.view.feedback.deselect(event.x, event.y)
+        self.__endGL()
+
+class GtkView(glchess.ui.ViewController):
+    """
+    """
+    # The UI this view belongs to
+    ui               = None
+    
+    # The title of this view
+    title            = None
+    
+    # The widget to render the scene to
+    widget           = None
+    
+    # A Gtk+ tree model to store the move history
+    moveModel        = None
+    selectedMove     = -1
+    
+    # Flag to show if requesting attention
+    requireAttention = False
+
+    # The format to report moves in
+    moveFormat     = 'human'
+    
+    def __init__(self, ui, title, feedback, isActive = True, moveFormat = 'human'):
+        """Constructor for a view.
+        
+        'feedback' is the feedback object for this view (extends ui.ViewFeedback).
+        'isActive' is a flag showing if this view can be controlled by the user (True) or not (False).
+        'moveFormat' is the format name to display moves in (string).
+        """
+        self.ui = ui
+        self.title = title
+        self.feedback = feedback
+        self.isActive = isActive
+        self.moveFormat = moveFormat
+        
+        # The GTK+ elements
+        self.gui = gtkui.loadGladeFile('chess_view.glade', 'chess_view')
+        self.gui.signal_autoconnect(self)
+        self.widget = self.gui.get_widget('chess_view')
+        self.viewWidget = GtkViewArea(self)
+        self.gui.get_widget('view_container').add(self.viewWidget)
+        
+        # Set the message panel to the tooltip style
+        # (copied from Gedit)
+        tooltip = gtk.Tooltips()
+        tooltip.force_window()
+        tooltip.tip_window.ensure_style()
+        self.gui.get_widget('info_panel').set_style(tooltip.tip_window.get_style())
+        
+        # Make a model for navigation
+        model = gtk.ListStore(gobject.TYPE_PYOBJECT, int, str)
+        iter = model.append()
+        model.set(iter, 0, None, 1, 0, 2, gettext.gettext('Game Start'))
+        self.moveModel = model
+
+        self.viewWidget.show_all()
+        
+    def _on_info_panel_expose_event(self, widget, event):
+        """Gtk+ callback"""
+        allocation = widget.allocation
+        widget.style.paint_flat_box(widget.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT, None, widget, "tooltip",
+                                    allocation.x, allocation.y, allocation.width, allocation.height)
+
+    def setMoveFormat(self, format):
+        """Set the format to display the moves in.
+        
+        'format' is the format name to use (e.g. 'human', 'san'. Defaults to 'human')
+        """
+        self.moveFormat = format
+        
+        # Update the move list
+        iter = self.moveModel.get_iter_first()
+        while iter is not None:
+            move = self.moveModel.get_value(iter, 0)
+            if move is not None:
+                string = self.generateMoveString(move)
+                self.moveModel.set(iter, 2, string)
+            iter = self.moveModel.iter_next(iter)
+    
+    # Extended methods
+    
+    def render(self):
+        """Extends glchess.ui.ViewController"""
+        self.viewWidget.redraw()
+
+    def setAttention(self, requiresAttention):
+        """Extends glchess.ui.ViewController"""
+        if self.requireAttention == requiresAttention:
+            return
+        self.requireAttention = requiresAttention
+        if requiresAttention:
+            self.ui._incAttentionCounter(1)
+        else:
+            self.ui._incAttentionCounter(-1)
+    
+    def generateMoveString(self, move):
+        """
+        """
+        if self.moveFormat == 'san':
+            string = '%2i. ' % ((move.number - 1) / 2 + 1)
+            if move.number % 2 == 0:
+                string += '... '
+            string += move.sanMove
+            return string
+        
+        if self.moveFormat == 'lan':
+            string = '%2i. ' % ((move.number - 1) / 2 + 1)
+            if move.number % 2 == 0:
+                string += '... '
+            string += move.canMove
+            return string
+        
+        colour = gettext.gettext('White')
+        victimColour = gettext.gettext('Black')
+        if move.number % 2 == 1:
+            shortColour = 'a'
+        else:
+            shortColour = 'b'
+            t = colour
+            colour = victimColour
+            victimColour = t
+        string = '%2i%s.' % ((move.number - 1) / 2 + 1, shortColour)
+
+        if move.sanMove.startswith('O-O-O'):
+            string += ' %s castles long' % colour
+        elif move.sanMove.startswith('O-O'):
+            string += ' %s castles short' % colour
+        elif move.victim is not None:
+            string += ' %s %s at %s takes %s %s at %s' % \
+                      (colour, pieceNames[move.piece.getType()],
+                       move.start, victimColour, pieceNames[move.victim.getType()], move.end)
+        else:
+            string += ' %s %s moves from %s to %s' % (colour, pieceNames[move.piece.getType()], move.start, move.end)
+
+        # FIXME: Promotion
+
+        if move.result is glchess.chess.board.MOVE_RESULT_OPPONENT_CHECK:
+            string += ' - ' + gettext.gettext('Check')
+        if move.result is glchess.chess.board.MOVE_RESULT_OPPONENT_CHECKMATE:
+            string += ' - ' + gettext.gettext('Checkmate')
+        return string
+
+    def addMove(self, move):
+        """Extends glchess.ui.ViewController"""
+        # FIXME: Make a '@ui' player who watches for these itself?
+        iter = self.moveModel.append()
+        string = self.generateMoveString(move)
+        self.moveModel.set(iter, 0, move, 1, move.number, 2, string)
+        
+        # If is the current view and tracking the game select this
+        if self.selectedMove == -1:
+            self.ui._updateViewButtons()
+            
+        # If game completed show this in the GUI
+        if move.result is glchess.chess.board.MOVE_RESULT_OPPONENT_CHECKMATE:
+            title = gettext.gettext('%s wins') % move.player.getName()
+            description = gettext.gettext('Opponent is in checkmate')
+            self.gui.get_widget('panel_title_label').set_markup('<big><b>%s</b></big>' % title)
+            self.gui.get_widget('panel_description_label').set_markup('<i>%s</i>' % description)
+            self.gui.get_widget('info_panel').show()
+    
+    def close(self):
+        """Extends glchess.ui.ViewController"""
+        if self.requireAttention:
+            self.ui._incAttentionCounter(-1)
+        self.ui._removeView(self)
+    
+    # Public methods
+
+    def _getModel(self):
+        """
+        """
+        return (self.moveModel, self.selectedMove)
+        
+    def _setMoveNumber(self, moveNumber):
+        """Set the move number this view requests.
+        
+        'moveNumber' is the move number to use (integer).
+        """
+        self.selectedMove = moveNumber
+        if self.feedback is not None:
+            self.feedback.setMoveNumber(moveNumber)
Index: src/lib/gtkui/Makefile.am
===================================================================
--- src/lib/gtkui/Makefile.am	(revision 5937)
+++ src/lib/gtkui/Makefile.am	(working copy)
@@ -1,5 +1,6 @@
 glchessdir = $(pythondir)/glchess/gtkui
 glchess_PYTHON = \
 	dialogs.py	\
+	chessview.py	\
 	gtkui.py	\
 	__init__.py
Index: src/lib/gtkui/dialogs.py
===================================================================
--- src/lib/gtkui/dialogs.py	(revision 5937)
+++ src/lib/gtkui/dialogs.py	(working copy)
@@ -89,6 +89,7 @@
         'aiModel' is the AI models to use.
         """
         self.__mainUI = mainUI
+        self.__checking = True
         
         # Load the UI
         self.__gui = gtkui.loadGladeFile('new_game.glade', 'new_game_dialog')
@@ -212,13 +213,16 @@
             self.__setCombo('black_difficulty_combo', blackLevel)
 
         # Show the dialog
-        self.__getWidget('new_game_dialog').show()
+        self.__gui.get_widget('new_game_dialog').show()
+        self.__checking = False
         self.__testReady()
         
     # Private methods
     
     def __setCombo(self, comboName, key):
-        widget = self.__getWidget(comboName)
+        """
+        """
+        widget = self.__gui.get_widget(comboName)
         iter = self.__getIter(widget.get_model(), key)
         if iter is not None:
             widget.set_active_iter(iter)
@@ -233,12 +237,7 @@
 
             iter = model.iter_next(iter)
         return default
-                
-    def __getWidget(self, name):
-        """
-        """
-        return self.__gui.get_widget(name)
-    
+
     def __getComboData(self, comboBox, index):
         """
         """
@@ -258,49 +257,49 @@
 
         # Must have a name for the game
         if self.__customName:
-            name = self.__getWidget('game_name_entry').get_text()
+            name = self.__gui.get_widget('game_name_entry').get_text()
             if len(name) == 0:
                 ready = False
 
         # Name the game based on the players
         else:
-            whiteName = self.__getComboData(self.__getWidget('white_type_combo'), 2)
-            blackName = self.__getComboData(self.__getWidget('black_type_combo'), 2)
-            self.__getWidget('game_name_entry').set_text(gettext.gettext('%s versus %s') % (whiteName, blackName))
+            whiteName = self.__getComboData(self.__gui.get_widget('white_type_combo'), 2)
+            blackName = self.__getComboData(self.__gui.get_widget('black_type_combo'), 2)
+            self.__gui.get_widget('game_name_entry').set_text(gettext.gettext('%s versus %s') % (whiteName, blackName))
             
         # Disable difficulty for human players
-        whiteType = self.__getComboData(self.__getWidget('white_type_combo'), 0)
-        blackType = self.__getComboData(self.__getWidget('black_type_combo'), 0)
+        whiteType = self.__getComboData(self.__gui.get_widget('white_type_combo'), 0)
+        blackType = self.__getComboData(self.__gui.get_widget('black_type_combo'), 0)
         self.__gui.get_widget('white_difficulty_combo').set_sensitive(whiteType != None)
         self.__gui.get_widget('black_difficulty_combo').set_sensitive(blackType != None)
 
         # Can only click OK if have enough information
-        self.__getWidget('start_button').set_sensitive(ready)
+        self.__gui.get_widget('start_button').set_sensitive(ready)
         self.__checking = False
         
     def __startGame(self):
         # FIXME: Game properties
-        gameName = self.__getWidget('game_name_entry').get_text()
+        gameName = self.__gui.get_widget('game_name_entry').get_text()
         allowSpectators = True
         
         white = glchess.ui.Player()
         black = glchess.ui.Player()
 
         # Get the players
-        white.type  = self.__getComboData(self.__getWidget('white_type_combo'), 0)
+        white.type  = self.__getComboData(self.__gui.get_widget('white_type_combo'), 0)
         if white.type is None:
             white.type = glchess.ui.HUMAN
             white.name = gettext.gettext('White')
         else:
-            white.name = self.__getComboData(self.__getWidget('white_type_combo'), 2)
-        white.level = self.__getComboData(self.__getWidget('white_difficulty_combo'), 0)
-        black.type  = self.__getComboData(self.__getWidget('black_type_combo'), 0)
+            white.name = self.__getComboData(self.__gui.get_widget('white_type_combo'), 2)
+        white.level = self.__getComboData(self.__gui.get_widget('white_difficulty_combo'), 0)
+        black.type  = self.__getComboData(self.__gui.get_widget('black_type_combo'), 0)
         if black.type is None:
             black.type = glchess.ui.HUMAN
             black.name = gettext.gettext('Black')
         else:
-            black.name = self.__getComboData(self.__getWidget('black_type_combo'), 2)
-        black.level = self.__getComboData(self.__getWidget('black_difficulty_combo'), 0)
+            black.name = self.__getComboData(self.__gui.get_widget('black_type_combo'), 2)
+        black.level = self.__getComboData(self.__gui.get_widget('black_difficulty_combo'), 0)
 
         duration = self.__getComboData(self.__gui.get_widget('time_combo'), 1)
         if duration < 0:
@@ -318,13 +317,13 @@
         
     # Gtk+ signal handlers
     
-    def _on_game_name_edited(self, widget, data = None):
+    def _on_game_name_edited(self, widget):
         """Gtk+ callback"""
         if self.__checking:
             return
         self.__customName = len(widget.get_text()) != 0
 
-    def _on_time_changed(self, widget, data = None):
+    def _on_time_changed(self, widget):
         """Gtk+ callback"""
         time = self.__getComboData(widget, 1)
         w = self.__gui.get_widget('custom_time_box')
@@ -337,11 +336,11 @@
         """Gtk+ callback"""
         self.__testReady()
 
-    def _on_response(self, widget, response_id, data = None):
+    def _on_response(self, widget, response_id):
         """Gtk+ callback"""
         if response_id == gtk.RESPONSE_OK:
             self.__startGame()
-        self.__getWidget('new_game_dialog').destroy()
+        self.__gui.get_widget('new_game_dialog').destroy()
 
 class GtkLoadGameDialog:
     """
@@ -388,7 +387,7 @@
         """
         return self.__gui.get_widget('filechooserwidget').get_filename()
     
-    def _on_file_changed(self, widget, data = None):
+    def _on_file_changed(self, widget):
         """Gtk+ callback"""
         name = self.__getFilename()
         if name is None:
@@ -402,7 +401,7 @@
         # Save the directory we used
         dialog = self.__gui.get_widget('filechooserwidget')
         glchess.config.set('load_directory', dialog.get_current_folder())
-        
+
         self.__mainUI.loadGame(self.__getFilename())
         self._on_close(widget, data)
         
@@ -454,7 +453,7 @@
         
         if path is not None:
             dialog.set_current_name(path)
-        
+
     def _on_save(self, widget, data = None):
         """Gtk+ callback"""
         dialog = self.__gui.get_widget('dialog')
Index: src/lib/gtkui/gtkui.py
===================================================================
--- src/lib/gtkui/gtkui.py	(revision 5940)
+++ src/lib/gtkui/gtkui.py	(working copy)
@@ -2,7 +2,7 @@
 __license__ = 'GNU General Public License Version 2'
 __copyright__ = 'Copyright 2005-2006  Robert Ancell'
 
-__all__ = ['GtkView', 'GtkUI']
+__all__ = ['GtkUI']
 
 # TODO: Extend base UI classes?
 
@@ -32,294 +32,17 @@
 
 from glchess.defaults import *
 
-# Optionally use OpenGL support
-# gtk.gtkgl throws RuntimeError when there is no OpenGL support
-# It should really just throw ImportError
-try:
-    import gtk.gtkgl
-    import OpenGL
-except (ImportError, RuntimeError):
-    haveGLSupport = False
-else:
-    haveGLSupport = True
-
 # Stop PyGTK from catching exceptions
 os.environ['PYGTK_FATAL_EXCEPTIONS'] = '1'
 
 import glchess.config
 import glchess.ui
 import dialogs
+import chessview
 
 def loadGladeFile(name, root = None):
     return gtk.glade.XML(os.path.join(GLADE_DIR, name), root, domain = DOMAIN)
 
-class GtkViewArea(gtk.DrawingArea):
-    """Custom widget to render an OpenGL scene"""
-    # The view this widget is rendering
-    view = None
-
-    # Pixmaps to use for double buffering
-    pixmap = None
-    dynamicPixmap = None
-    
-    # Flag to show if this scene is to be rendered using OpenGL
-    renderGL = False
-    
-    # TODO...
-    __glDrawable = None
-    
-    def __init__(self, view):
-        """
-        """
-        gtk.DrawingArea.__init__(self)
-        
-        self.view = view
-
-        if haveGnomeSupport:
-            gnome.program_init('glchess', VERSION,
-                               properties={gnome.PARAM_APP_DATADIR: APP_DATA_DIR})
-
-        # Allow notification of button presses
-        self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.BUTTON_MOTION_MASK)
-        
-        # Make openGL drawable
-        if hasattr(gtk, 'gtkgl'):
-            gtk.gtkgl.widget_set_gl_capability(self, self.view.ui.notebook.glConfig)# FIXME:, share_list=glContext)
-
-        # Connect signals
-        self.connect('realize', self.__init)
-        self.connect('configure_event', self.__configure)
-        self.connect('expose_event', self.__expose)
-        self.connect('button_press_event', self.__button_press)
-        self.connect('button_release_event', self.__button_release)
-        
-    # Public methods
-        
-    def redraw(self):
-        """Request this widget is redrawn"""
-        # If the window is visible prepare it for redrawing
-        area = gtk.gdk.Rectangle(0, 0, self.allocation.width, self.allocation.height)
-        if self.window is not None:
-            self.window.invalidate_rect(area, False)
-
-    def setRenderGL(self, renderGL):
-        """Enable OpenGL rendering"""
-        if not haveGLSupport:
-            renderGL = False
-        
-        if self.renderGL == renderGL:
-            return
-        self.renderGL = renderGL
-        self.redraw()
-    
-    # Private methods
-
-    def __startGL(self):
-        """Get the OpenGL context"""
-        if not self.renderGL:
-            return
-
-        assert(self.__glDrawable is None)
-        
-        # Obtain a reference to the OpenGL drawable
-        # and rendering context.
-        glDrawable = gtk.gtkgl.widget_get_gl_drawable(self)
-        glContext = gtk.gtkgl.widget_get_gl_context(self)
-
-        # OpenGL begin.
-        if not glDrawable.gl_begin(glContext):
-            return
-        
-        self.__glDrawable = glDrawable
-
-        if not self.view.ui.openGLInfoPrinted:
-            print 'Using OpenGL:'
-            print 'VENDOR=' + OpenGL.GL.glGetString(OpenGL.GL.GL_VENDOR)
-            print 'RENDERER=' + OpenGL.GL.glGetString(OpenGL.GL.GL_RENDERER)
-            print 'VERSION=' + OpenGL.GL.glGetString(OpenGL.GL.GL_VERSION)
-            print 'EXTENSIONS=' + OpenGL.GL.glGetString(OpenGL.GL.GL_EXTENSIONS)
-            self.view.ui.openGLInfoPrinted = True
-        
-    def __endGL(self):
-        """Free the OpenGL context"""
-        if not self.renderGL:
-            return
-        
-        assert(self.__glDrawable is not None)
-        self.__glDrawable.gl_end()
-        self.__glDrawable = None
-        
-    def __init(self, widget):
-        """Gtk+ signal"""
-        if self.view.feedback is not None:
-            self.view.feedback.reshape(widget.allocation.width, widget.allocation.height)
-        
-    def __configure(self, widget, event):
-        """Gtk+ signal"""
-        self.pixmap = gtk.gdk.Pixmap(widget.window, event.width, event.height)
-        self.dynamicPixmap = gtk.gdk.Pixmap(widget.window, event.width, event.height)
-        self.__startGL()
-        if self.view.feedback is not None:
-            self.view.feedback.reshape(event.width, event.height)
-        self.__endGL()
-
-    def __expose(self, widget, event):
-        """Gtk+ signal"""
-        if self.renderGL:
-            self.__startGL()
-        
-            # Get the scene rendered
-            try:
-                if self.view.feedback is not None:
-                    self.view.feedback.renderGL()
-            except OpenGL.GLerror, e:
-                print 'Rendering Error: ' + str(e)
-                traceback.print_exc(file = sys.stdout)
-
-            # Paint this
-            if self.__glDrawable.is_double_buffered():
-                self.__glDrawable.swap_buffers()
-            else:
-                glFlush()
-
-            self.__endGL()
-            
-        else:
-            context = self.pixmap.cairo_create()
-            if self.view.feedback is not None:
-                self.view.feedback.renderCairoStatic(context)
-            
-            # Copy the background to render the dynamic elements on top
-            self.dynamicPixmap.draw_drawable(widget.get_style().white_gc, self.pixmap, 0, 0, 0, 0, -1, -1)
-            context = self.dynamicPixmap.cairo_create()
-        
-            # Set a clip region for the expose event
-            context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
-            context.clip()
-           
-            # Render the dynamic elements
-            if self.view.feedback is not None:
-                self.view.feedback.renderCairoDynamic(context)
-                
-            # Draw the window
-            widget.window.draw_drawable(widget.get_style().white_gc, self.dynamicPixmap,
-                                        event.area.x, event.area.y,
-                                        event.area.x, event.area.y, event.area.width, event.area.height)
-
-    def __button_press(self, widget, event):
-        """Gtk+ signal"""
-        self.__startGL()
-        if self.view.feedback is not None:
-            self.view.feedback.select(event.x, event.y)
-        self.__endGL()
-        
-    def __button_release(self, widget, event):
-        """Gtk+ signal"""
-        self.__startGL()
-        if self.view.feedback is not None:
-            self.view.feedback.deselect(event.x, event.y)
-        self.__endGL()
-        
-class GtkView(glchess.ui.ViewController):
-    """
-    """
-    # The UI this view belongs to
-    ui               = None
-    
-    # The title of this view
-    title            = None
-    
-    # The widget to render the scene to
-    widget           = None
-    
-    # A Gtk+ tree model to store the move history
-    moveModel        = None
-    selectedMove     = -1
-    
-    # Flag to show if requesting attention
-    requireAttention = False
-    
-    def __init__(self, ui, title, feedback, isActive = True):
-        """Constructor for a view.
-        
-        'feedback' is the feedback object for this view (extends ui.ViewFeedback).
-        'isActive' is a flag showing if this view can be controlled by the user (True) or not (False).
-        """
-        self.ui = ui
-        self.title = title
-        self.feedback = feedback
-        self.isActive = isActive
-        self.widget = GtkViewArea(self)
-
-        # Make a model for navigation
-        model = gtk.ListStore(int, str)
-        iter = model.append()
-        model.set(iter, 0, 0, 1, gettext.gettext('Game Start'))
-        self.moveModel = model
-
-        self.widget.show_all()
-    
-    # Extended methods
-    
-    def render(self):
-        """Extends glchess.ui.ViewController"""
-        self.widget.redraw()
-
-    def setAttention(self, requiresAttention):
-        """Extends glchess.ui.ViewController"""
-        if self.requireAttention == requiresAttention:
-            return
-        self.requireAttention = requiresAttention
-        if requiresAttention:
-            self.ui._incAttentionCounter(1)
-        else:
-            self.ui._incAttentionCounter(-1)
-
-    def addMove(self, move):
-        """Extends glchess.ui.ViewController
-	Based on the current move, this method creates a string
-	describing the move, and displays it in the GUI.
-	"""
-        # FIXME: Make a '@ui' player who watches for these itself?
-        iter = self.moveModel.append()
-        string = '%2i. ' % ((move.number - 1) / 2 + 1)
-        if move.number % 2 == 0:
-            string += '... '
-        string += move.sanMove
-
-        if move.result is glchess.chess.board.MOVE_RESULT_OPPONENT_CHECK:
-            string += " - " + gettext.gettext('Check')
-        if move.result is glchess.chess.board.MOVE_RESULT_OPPONENT_CHECKMATE:
-            string += " - " + gettext.gettext('Checkmate, %s wins.' % (move.player.getName()))
-
-        self.moveModel.set(iter, 0, move.number, 1, string)
-        
-        # If is the current view and tracking the game select this
-        if self.selectedMove == -1:
-            self.ui._updateViewButtons()
-    
-    def close(self):
-        """Extends glchess.ui.ViewController"""
-        if self.requireAttention:
-            self.ui._incAttentionCounter(-1)
-        self.ui._removeView(self)
-    
-    # Public methods
-
-    def _getModel(self):
-        """
-        """
-        return (self.moveModel, self.selectedMove)
-        
-    def _setMoveNumber(self, moveNumber):
-        """Set the move number this view requests.
-        
-        'moveNumber' is the move number to use (integer).
-        """
-        self.selectedMove = moveNumber
-        if self.feedback is not None:
-            self.feedback.setMoveNumber(moveNumber)
-
 class GtkGameNotebook(gtk.Notebook):
     """
     """
@@ -362,7 +85,7 @@
         """
         """
         assert(self.defaultView is None)
-        self.defaultView = GtkView(self.ui, '', feedback)
+        self.defaultView = chessview.GtkView(self.ui, '', feedback)
         page = self.append_page(self.defaultView.widget)
         self.set_current_page(page)
         
@@ -373,7 +96,7 @@
     def addView(self, title, feedback):
         """
         """
-        view = GtkView(self.ui, title, feedback)
+        view = chessview.GtkView(self.ui, title, feedback, moveFormat = self.ui.moveFormat)
         self.views.append(view)
         self.viewsByWidget[view.widget] = view
         page = self.append_page(view.widget)
@@ -577,11 +300,25 @@
 
     __attentionCounter = 0
 
+    moveFormat         = 'human'
+
     def __init__(self):
         """Constructor for a GTK+ glChess GUI"""
+        if haveGnomeSupport:
+            gnome.program_init('glchess', VERSION,
+                               properties={gnome.PARAM_APP_DATADIR: APP_DATA_DIR})
+
         self.__networkGames = {}
         self.__saveGameDialogs = {}
         self.__joinGameDialogs = []
+        self.timers = {}
+
+        # Set the message panel to the tooltip style
+        # (copied from Gedit)
+        tooltip = gtk.Tooltips()
+        tooltip.force_window()
+        tooltip.tip_window.ensure_style()
+        self.tooltipStyle = tooltip.tip_window.get_style()
         
         self._gui = loadGladeFile('glchess.glade')
         self._gui.signal_autoconnect(self)
@@ -608,7 +345,7 @@
         combo = self.__getWidget('history_combo')
         cell = gtk.CellRendererText()
         combo.pack_start(cell, False)
-        combo.add_attribute(cell, 'text', 1)
+        combo.add_attribute(cell, 'text', 2)
 
         self.defaultViewController = self.notebook.setDefault(None)
 
@@ -616,7 +353,7 @@
         if haveGnomeSupport:
             self._gui.get_widget('menu_help').show()
         
-        self.defaultViewController.widget.setRenderGL(self.__renderGL)
+        self.defaultViewController.viewWidget.setRenderGL(self.__renderGL)
 
         self.saveDialog = SaveDialog(self)
         
@@ -627,6 +364,7 @@
         glchess.config.watch('show_3d', self.__applyConfig)
         glchess.config.watch('width', self.__applyConfig)
         glchess.config.watch('height', self.__applyConfig)
+        glchess.config.watch('move_format', self.__applyConfig)
 
     # Public methods
     
@@ -677,7 +415,7 @@
     def addView(self, title, feedback):
         """Extends ui.UI"""
         view = self.notebook.addView(title, feedback)
-        view.widget.setRenderGL(self.__renderGL)
+        view.viewWidget.setRenderGL(self.__renderGL)
         return view
 
     def addAIWindow(self, title, executable, description):
@@ -691,7 +429,7 @@
         This method will not return.
         """        
         # Load configuration
-        for name in ['show_toolbar', 'show_history', 'show_3d']:
+        for name in ['show_toolbar', 'show_history', 'show_3d', 'move_format']:
             try:
                 value = glchess.config.get(name)
             except glchess.config.Error:
@@ -830,17 +568,23 @@
 
         # Enable/disable OpenGL rendering
         elif name == 'show_3d':            
-            if value and not haveGLSupport:
+            if value and not chessview.haveGLSupport:
                 self._gui.get_widget('3d_support_dialog').show()
                 glchess.config.set('show_3d', False)
                 value = False
             self.__renderGL = value
             menuItem = self.__getWidget('menu_view_3d')
             menuItem.set_active(self.__renderGL)
-            self.notebook.defaultView.widget.setRenderGL(self.__renderGL)
+            self.notebook.defaultView.viewWidget.setRenderGL(self.__renderGL)
             for view in self.notebook.views:
-                view.widget.setRenderGL(self.__renderGL)
+                view.viewWidget.setRenderGL(self.__renderGL)
         
+        elif name == 'move_format':
+            self._gui.get_widget('menu_movef_%s' % value).set_active(True)
+            self.moveFormat = value
+            for view in self.notebook.views:
+                view.setMoveFormat(value)
+        
     def startAnimation(self):
         """Start the animation callback"""
         if self.__animationTimer is None:
@@ -909,7 +653,7 @@
             return
         
         # Get the move number
-        moveNumber = model.get_value(iter, 0)
+        moveNumber = model.get_value(iter, 1)
         
         string = 'Show move number: ' + str(moveNumber)
         if moveNumber == len(model) - 1:
@@ -919,6 +663,18 @@
         view = self.notebook.getView()
         if view is not None:
             view._setMoveNumber(moveNumber)
+            
+    def _on_menu_movef_human_activate(self, widget):
+        """Gtk+ callback"""
+        glchess.config.set('move_format', 'human')
+
+    def _on_menu_movef_lan_activate(self, widget):
+        """Gtk+ callback"""
+        glchess.config.set('move_format', 'lan')
+
+    def _on_menu_movef_san_activate(self, widget):
+        """Gtk+ callback"""
+        glchess.config.set('move_format', 'san')            
         
     def __selectMoveNumber(self, moveNumber):
         """FIXME
Index: glade/glchess.glade
===================================================================
--- glade/glchess.glade	(revision 5937)
+++ glade/glchess.glade	(working copy)
@@ -62,7 +62,7 @@
 		      <accelerator key="J" modifiers="GDK_CONTROL_MASK" signal="activate"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image27">
+			<widget class="GtkImage" id="image36">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-connect</property>
 			  <property name="icon_size">1</property>
@@ -113,7 +113,7 @@
 		      <accelerator key="W" modifiers="GDK_CONTROL_MASK" signal="activate"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image28">
+			<widget class="GtkImage" id="image37">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-close</property>
 			  <property name="icon_size">1</property>
@@ -163,7 +163,7 @@
 		      <accelerator key="F11" modifiers="0" signal="activate"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image29">
+			<widget class="GtkImage" id="image38">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-fullscreen</property>
 			  <property name="icon_size">1</property>
@@ -184,7 +184,7 @@
 		      <accelerator key="F11" modifiers="0" signal="activate"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image30">
+			<widget class="GtkImage" id="image39">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-leave-fullscreen</property>
 			  <property name="icon_size">1</property>
@@ -228,6 +228,52 @@
 		  </child>
 
 		  <child>
+		    <widget class="GtkMenuItem" id="move_format1">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">_Move Format</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_move_format1_activate" last_modification_time="Sat, 10 Feb 2007 03:04:46 GMT"/>
+
+		      <child>
+			<widget class="GtkMenu" id="move_format1_menu">
+
+			  <child>
+			    <widget class="GtkRadioMenuItem" id="menu_movef_human">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">_Human</property>
+			      <property name="use_underline">True</property>
+			      <property name="active">False</property>
+			      <signal name="activate" handler="_on_menu_movef_human_activate" last_modification_time="Sat, 10 Feb 2007 03:06:49 GMT"/>
+			    </widget>
+			  </child>
+
+			  <child>
+			    <widget class="GtkRadioMenuItem" id="menu_movef_lan">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">_Long Algebraic</property>
+			      <property name="use_underline">True</property>
+			      <property name="active">False</property>
+			      <property name="group">menu_movef_human</property>
+			      <signal name="activate" handler="_on_menu_movef_lan_activate" last_modification_time="Sat, 10 Feb 2007 03:06:49 GMT"/>
+			    </widget>
+			  </child>
+
+			  <child>
+			    <widget class="GtkRadioMenuItem" id="menu_movef_san">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">_Standard Algebraic</property>
+			      <property name="use_underline">True</property>
+			      <property name="active">False</property>
+			      <property name="group">menu_movef_human</property>
+			      <signal name="activate" handler="_on_menu_movef_san_activate" last_modification_time="Sat, 10 Feb 2007 03:06:49 GMT"/>
+			    </widget>
+			  </child>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
+
+		  <child>
 		    <widget class="GtkSeparatorMenuItem" id="separator1">
 		      <property name="visible">True</property>
 		    </widget>
@@ -262,7 +308,7 @@
 		      <signal name="activate" handler="_on_help_clicked" last_modification_time="Fri, 11 Aug 2006 12:43:28 GMT"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image17">
+			<widget class="GtkImage" id="image40">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-help</property>
 			  <property name="icon_size">1</property>
Index: glade/chess_view.glade
===================================================================
--- glade/chess_view.glade	(revision 5937)
+++ glade/chess_view.glade	(working copy)
@@ -29,22 +29,149 @@
       <property name="position">300</property>
 
       <child>
-	<widget class="Custom" id="drawing_area">
+	<widget class="GtkVBox" id="vbox22">
 	  <property name="visible">True</property>
-	  <property name="creation_function">make_chess_view</property>
-	  <property name="int1">0</property>
-	  <property name="int2">0</property>
-	  <property name="last_modification_time">Thu, 24 Mar 2005 01:41:43 GMT</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkTable" id="info_panel">
+	      <property name="border_width">6</property>
+	      <property name="n_rows">2</property>
+	      <property name="n_columns">3</property>
+	      <property name="homogeneous">False</property>
+	      <property name="row_spacing">6</property>
+	      <property name="column_spacing">6</property>
+	      <signal name="expose_event" handler="_on_info_panel_expose_event" last_modification_time="Sat, 10 Feb 2007 05:12:26 GMT"/>
+
+	      <child>
+		<widget class="GtkImage" id="image1">
+		  <property name="visible">True</property>
+		  <property name="icon_size">6</property>
+		  <property name="icon_name">gtk-dialog-info</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">0</property>
+		  <property name="right_attach">1</property>
+		  <property name="top_attach">0</property>
+		  <property name="bottom_attach">2</property>
+		  <property name="x_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="panel_description_label">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="no">Sub description</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">True</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">1</property>
+		  <property name="right_attach">2</property>
+		  <property name="top_attach">1</property>
+		  <property name="bottom_attach">2</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="panel_title_label">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="no">&lt;b&gt;Title&lt;/b&gt;</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="left_attach">1</property>
+		  <property name="right_attach">3</property>
+		  <property name="top_attach">0</property>
+		  <property name="bottom_attach">1</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkButton" id="info_ok_button">
+		  <property name="can_focus">True</property>
+		  <property name="label">gtk-ok</property>
+		  <property name="use_stock">True</property>
+		  <property name="relief">GTK_RELIEF_NORMAL</property>
+		  <property name="focus_on_click">True</property>
+		  <signal name="clicked" handler="_on_info_ok_button_clicked" last_modification_time="Sat, 10 Feb 2007 09:35:00 GMT"/>
+		</widget>
+		<packing>
+		  <property name="left_attach">2</property>
+		  <property name="right_attach">3</property>
+		  <property name="top_attach">1</property>
+		  <property name="bottom_attach">2</property>
+		  <property name="x_options">fill</property>
+		  <property name="y_options">fill</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkAlignment" id="view_container">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">0</property>
+	      <property name="bottom_padding">0</property>
+	      <property name="left_padding">0</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<placeholder/>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
 	</widget>
 	<packing>
 	  <property name="shrink">True</property>
-	  <property name="resize">True</property>
+	  <property name="resize">False</property>
 	</packing>
       </child>
 
       <child>
 	<widget class="GtkVBox" id="vbox21">
-	  <property name="visible">True</property>
 	  <property name="homogeneous">False</property>
 	  <property name="spacing">0</property>
 
@@ -73,7 +200,7 @@
 		  <property name="left_margin">0</property>
 		  <property name="right_margin">0</property>
 		  <property name="indent">0</property>
-		  <property name="text" translatable="yes"></property>
+		  <property name="text" translatable="no"></property>
 		</widget>
 	      </child>
 	    </widget>
@@ -91,7 +218,7 @@
 	      <property name="editable">True</property>
 	      <property name="visibility">True</property>
 	      <property name="max_length">0</property>
-	      <property name="text" translatable="yes"></property>
+	      <property name="text" translatable="no"></property>
 	      <property name="has_frame">True</property>
 	      <property name="invisible_char">*</property>
 	      <property name="activates_default">False</property>
