root/05/bugfix/v.py

Revision 105, 109.6 kB (checked in by ug, 5 years ago)

Fix board.currentColor bug

  • Property svn:executable set to
Line 
1 #!/usr/bin/python
2 # File: v.py
3
4 ##   Copyright (C) 2001-4 Ulrich Goertz (u@g0ertz.de)
5
6 ##   This is a simple SGF viewer; it comes with the go database program
7 ##   Kombilo.
8
9 ##   This program is free software; you can redistribute it and/or modify
10 ##   it under the terms of the GNU General Public License as published by
11 ##   the Free Software Foundation; either version 2 of the License, or
12 ##   (at your option) any later version.
13
14 ##   This program is distributed in the hope that it will be useful,
15 ##   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ##   GNU General Public License for more details.
18
19 ##   You should have received a copy of the GNU General Public License
20 ##   along with this program (gpl.txt); if not, write to the Free Software
21 ##   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 ##   The GNU GPL is also currently available at
23 ##   http://www.gnu.org/copyleft/gpl.html
24
25
26 from Tkinter import *
27 from tkMessageBox import *
28 from ScrolledText import ScrolledText
29 import tkFileDialog
30 import Pmw
31
32 import cPickle
33 import os
34 import sys
35
36 import encodings.utf_8
37 from string import split, find, replace, join, strip
38 from copy import copy, deepcopy
39 from math import sqrt
40 from whrandom import randint
41
42 try:
43     from sgfpars import Node, Cursor, SGFError, SGFescape
44 except:
45     from sgfparser import Node, Cursor, SGFError, SGFescape
46 from board1 import *
47
48 # ---------------------------------------------------------------------------------------
49
50 class BunchTkVar:
51     """ This class is used to collect the Tk variables where the options
52         are stored. """
53    
54     def saveToDisk(self, filename, onFailure = lambda:None):
55         d = {}
56         for x in self.__dict__.keys():
57             d[x] = self.__dict__[x].get()
58         try:
59             f = open(filename, 'w')
60             cPickle.dump(d,f)
61             f.close()
62         except IOError:
63             onFailure()
64            
65
66     def loadFromDisk(self, filename, onFailure = lambda:None):
67         try:
68             f = open(filename)
69             d = cPickle.load(f)
70             f.close()
71         except IOError:
72             onFailure()
73         else:
74             for x in self.__dict__.keys():
75                 if d.has_key(x): self.__dict__[x].set(d[x])
76
77
78 # ---------------------------------------------------------------------------------------
79
80
81 class ScrolledText_HSB(ScrolledText):
82     """ A ScrolledText which hides the scroll bar when it is not needed. """
83
84     def __init__(self, parent, **args):
85         if not args:
86             args = {}
87         apply(ScrolledText.__init__, (self, parent,) , args)
88         self.bind('<Configure>', self.checkScrollbars)
89         self.bind('<Key>', self.checkScrollbars)
90         self.vbarPackinfo = self.vbar.pack_info()
91         self.vbar.pack_forget()
92
93     def insert(self, pos, text, tags = None):
94         ScrolledText.insert(self, pos, text, tags)
95         if self.yview() != (0.0, 1.0):
96             apply(self.vbar.pack, (), self.vbarPackinfo)
97
98     def delete(self, pos1, pos2):
99         ScrolledText.delete(self, pos1, pos2)
100         if self.yview() == (0.0, 1.0):
101             self.vbar.pack_forget()
102
103     def checkScrollbars(self, event):
104         if self.yview() == (0.0, 1.0):
105             self.vbar.pack_forget()
106         else:
107             apply(self.vbar.pack, (), self.vbarPackinfo)
108
109
110 # ---------------------------------------------------------------------------------------
111
112 class ScrolledList(Frame):
113     """ A listbox with dynamic vertical and horizontal scrollbars. """
114    
115     def __init__(self, parent, **kw):
116         Frame.__init__(self, parent)
117
118         self.sbar = Scrollbar(self)
119         self.sbar1 = Scrollbar(self)
120         self.checking = 0
121        
122         if not kw: kw = {}
123
124         for var, value in [('height', 12), ('width', 40), ('relief', SUNKEN),
125                            ('selectmode', SINGLE), ('takefocus', 1), ('exportselection', 0)]:
126             if not kw.has_key(var): kw[var] = value
127        
128         self.list = Listbox(self, kw)
129         # self.rowconfigure(0, weight=1)
130         # self.columnconfigure(0, weight=1)
131         self.sbar.config(command = self.list.yview)
132         self.sbar1.config(command = self.list.xview, orient='horizontal')
133         self.list.config(xscrollcommand = self.sbar1.set, yscrollcommand = self.sbar.set)
134         self.list.grid(row=0, column=0, sticky=NSEW)
135         self.sbar.grid(row=0, column=1, sticky=NSEW)
136         self.sbar1.grid(row=1, column=0, sticky=NSEW)
137
138         self.rowconfigure(0, weight=1)
139         self.columnconfigure(0, weight=1)
140
141         self.focus_force()
142
143         self.onSelectionChange = None
144
145         self.bind('<Up>', self.up)
146         self.bind('<Down>', self.down)
147         self.bind('<Prior>', self.pgup)
148         self.bind('<Next>', self.pgdown)
149
150         # self.unbind('<Configure>')
151         self.bind('<Configure>', self.checkScrollbars)
152        
153         self.sbar.grid_forget()
154         self.sbar1.grid_forget()
155
156     def insert(self, index, data):
157         if type(data) == type([]):
158             apply(self.list.insert, [index] + data)
159         else:
160             self.list.insert(index, data)
161        
162         if self.list.yview() != (0.0, 1.0):
163             self.sbar.grid(row=0, column=1, sticky=NSEW)
164         if self.list.xview() != (0.0, 1.0):
165             self.sbar1.grid(row=1, column=0, sticky=NSEW)
166
167     def delete(self, index, data=None):
168         if data:
169             self.list.delete(index, data)
170         else:
171             self.list.delete(index)
172
173         if self.list.yview() == (0.0, 1.0):
174             self.sbar.grid_forget()
175
176         if self.list.xview() == (0.0, 1.0):
177             self.sbar1.grid_forget()
178
179
180     def checkScrollbars(self, event=None):
181         if self.checking:
182             if self.list.yview() != (0.0, 1.0):
183                 self.sbar.grid(row=0, column=1, sticky=NSEW)
184             else:
185                 self.sbar.grid_forget()
186             if self.list.xview() != (0.0, 1.0):
187                 self.sbar1.grid(row=1, column=0, sticky=NSEW)
188             else:
189                 self.sbar1.grid_forget()
190         else:
191             self.after(100, self.checkScrollbars)
192         self.checking = 1-self.checking
193
194            
195     def up(self, event):
196         if not self.list.curselection() or len(self.list.curselection())>1: return
197         index = int(self.list.curselection()[0])
198         if index != 0:
199             self.list.select_clear(index)
200             self.list.select_set(index-1)
201             self.list.see(index-1)
202             if self.onSelectionChange:
203                 self.onSelectionChange(None, index-1)
204
205     def down(self, event):
206         if not self.list.curselection() or len(self.list.curselection())>1: return
207         index = int(self.list.curselection()[0])
208         if index != self.list.size()-1:
209             self.list.select_clear(index)
210             self.list.select_set(index+1)
211             self.list.see(index+1)
212             if self.onSelectionChange:
213                 self.onSelectionChange(None, index+1)
214
215     def pgup(self, event):
216         if not self.list.curselection() or len(self.list.curselection())>1: return
217         index = int(self.list.curselection()[0])
218         if index >= 10:
219             self.list.select_clear(index)
220             self.list.select_set(index-10)
221             self.list.see(index-10)
222             if self.onSelectionChange:
223                 self.onSelectionChange(None, index-10)
224         elif self.list.size():
225             self.list.select_clear(index)
226             self.list.select_set(0)
227             self.list.see(0)
228             if self.onSelectionChange:
229                 self.onSelectionChange(None, 0)
230
231     def pgdown(self, event):
232         if not self.list.curselection() or len(self.list.curselection())>1: return
233         index = int(self.list.curselection()[0])
234         if index <= self.list.size()-10:
235             self.list.select_clear(index)
236             self.list.select_set(index+10)
237             self.list.see(index+10)
238             if self.onSelectionChange:
239                 self.onSelectionChange(None, index+10)
240         elif self.list.size():
241             self.list.select_clear(index)
242             self.list.select_set(self.list.size()-1)
243             self.list.see(END)
244             if self.onSelectionChange:
245                 self.onSelectionChange(None, self.list.size()-1)
246
247
248
249 # ---------------------------------------------------------------------------------------
250
251 class SGFtreeCanvas(Frame):
252     """ The canvas (in the data window) displaying the tree structure of the current game."""
253
254     def __init__(self, parent, **args):
255
256         Frame.__init__(self, parent)
257        
258         if not args: args = {}
259
260         for var, value in [('height', 100), ('width', 150), ('relief', SUNKEN)]:
261             if not args.has_key(var): args[var] = value
262        
263         self.canvas = Canvas(self, background='#FFBA59')
264         apply(self.canvas.config, (), args)
265         self.canvas.config(scrollregion=(0,0,1000,30))
266
267         self.sbar_vert = Scrollbar(self)
268         self.sbar_hor = Scrollbar(self)
269
270         self.sbar_vert.config(command = self.yview)
271         self.sbar_hor.config(command = self.xview, orient='horizontal')
272         self.canvas.config(xscrollcommand = self.sbar_hor.set, yscrollcommand = self.sbar_vert.set)
273        
274         self.movenoCanvas = Canvas(self, width=150, height=15, background='white')
275         self.movenoCanvas.config(scrollregion=(0,0,1000,15))
276         # self.movenoCanvas.config(xscrollcommand = self.sbar_hor.set)
277
278         self.updateMovenoCanvas()
279      
280         self.movenoCanvas.grid(row=0, column=0, sticky=NSEW)
281         self.sbar_vert.grid(row=1, column=1, sticky=NSEW)
282         self.sbar_hor.grid(row=2, column=0, sticky=NSEW)
283         self.canvas.grid(row=1, column=0, sticky=NSEW)
284
285         self.rowconfigure(1, weight=1)
286         self.columnconfigure(0, weight=1)
287
288         # self.config(height=100, width=200)
289         # self.canvSize = 200,100
290         self.drawn = []
291
292         self.lastbind = None
293
294     def updateMovenoCanvas(self):
295         """ Put numbers on the small white canvas above the SGF tree canvas, to indicate move numbers."""
296        
297         self.movenoCanvas.delete(ALL)
298         for i in range(90):
299             self.movenoCanvas.create_text(int(SGFtreeCanvas.UNIT*.75)+i*5*SGFtreeCanvas.UNIT,7,text=`5*i`,
300                                           font=('Helvetica', 8))
301    
302
303     def xview(self, a1, a2=None, a3=None):
304         if a1 == MOVETO:
305             self.movenoCanvas.xview(a1, a2)
306             self.canvas.xview(a1, a2)
307         elif a1 == SCROLL:
308             self.movenoCanvas.xview(a1, a2, a3)
309             self.canvas.xview(a1, a2, a3)
310         else:
311             return
312             # raise Exception()
313
314
315     def yview(self, a1, a2=None, a3=None):
316
317         if a1 == MOVETO: self.canvas.yview(a1, a2)
318         elif a1 == SCROLL: self.canvas.yview(a1, a2, a3)
319         elif a1 == 'refresh': pass
320         else: return # raise Exception()
321
322         self.canvas.update_idletasks()
323
324         vert = self.sbar_vert.get()
325         y0 = int(vert[0] * self.canvSize[1])
326         y1 = int(vert[1] * self.canvSize[1])
327
328         u = max(0, (y0-300)/SGFtreeCanvas.UNIT)
329         l = (y1+500)/SGFtreeCanvas.UNIT
330
331         # print 'yview', u,l
332
333         nodelist = [(self.rootnode, 0,0)]
334
335         while nodelist:
336             c, posx, posy = nodelist[-1]
337             del nodelist[-1]
338
339             # print posx, posy
340
341             if u <= posy <= l and not posy in self.drawn:
342                 self.mark(c, posx, posy)
343                
344                 if c.previous:
345                     if c.up and c.up.up:
346                         self.link(posx, posy, c.posyD+1)
347                     else:
348                         self.link(posx, posy, c.posyD)
349
350                     if c.down and posy+c.down.posyD>l:
351                         if c.up:
352                             self.link(posx, posy+c.down.posyD, c.down.posyD+1)
353                         else:
354                             self.link(posx, posy+c.down.posyD, c.down.posyD)
355
356             if c != self.rootnode and c.down and posy + c.down.posyD <= l:
357                 d = c.down
358                 py = posy + d.posyD
359                 while d.down and py + d.down.posyD < u:
360                     d = d.down
361                     py += d.posyD
362                 nodelist.append((d, posx, py))
363
364             if c.down and posy + c.down.posyD < u: continue
365
366             while c.next:
367                 c = c.next
368                 posx += 1
369                 if u <= posy <= l and not posy in self.drawn:
370
371                     self.mark(c, posx, posy)
372
373                     if c.previous:
374                         if c.up and c.up.up:
375                             self.link(posx, posy, c.posyD+1)
376                         else:
377                             self.link(posx, posy, c.posyD)
378
379                         if c.down and posy+c.down.posyD>l:
380                             if c.up: delta2 = 1
381                             else: delta2 = 0
382                             self.link(posx, posy + c.down.posyD, c.down.posyD + delta2)
383
384                 if c.down and posy + c.down.posyD <= l:
385                     d = c.down
386                     py = posy + d.posyD
387                     while d.down and py + d.down.posyD < u:
388                         d = d.down
389                         py += d.posyD
390                     nodelist.append((d, posx, py))
391
392         self.canvas.lower('lines')
393            
394         for i in range(u, l):
395              if not i in self.drawn: self.drawn.append(i)
396
397        
398     def mark(self, c, posx, posy):
399         try:
400             n = c.getData()
401             if n.has_key('W'): color = 'white'
402             elif n.has_key('B'): color = 'black'
403             else:
404                 color = 'red'
405         except: color = 'yellow'
406
407         self.canvas.create_oval(SGFtreeCanvas.UNIT*posx+SGFtreeCanvas.UNIT/2,
408                                 SGFtreeCanvas.UNIT*posy+SGFtreeCanvas.UNIT/2,
409                                 SGFtreeCanvas.UNIT*posx+SGFtreeCanvas.UNIT,
410                                 SGFtreeCanvas.UNIT*posy+SGFtreeCanvas.UNIT, fill=color)
411
412         try:
413             if n.has_key('C') or n.has_key('TR') or n.has_key('SQ') or n.has_key('CR') or n.has_key('LB') or n.has_key('MA'):
414                 self.canvas.create_oval(SGFtreeCanvas.UNIT*posx+SGFtreeCanvas.UNIT*2/3,
415                                         SGFtreeCanvas.UNIT*posy+SGFtreeCanvas.UNIT*2/3,
416                                         SGFtreeCanvas.UNIT*posx+SGFtreeCanvas.UNIT*5/6,
417                                         SGFtreeCanvas.UNIT*posy+SGFtreeCanvas.UNIT*5/6, fill='blue')
418         except: pass
419        
420
421     def link(self, posx, posy, delta):
422
423         s4 = SGFtreeCanvas.UNIT/4
424         s34 = 3*SGFtreeCanvas.UNIT/4
425        
426         if delta == 0:
427             self.canvas.create_line(SGFtreeCanvas.UNIT*posx-s4,
428                                     SGFtreeCanvas.UNIT*posy+s34,
429                                     SGFtreeCanvas.UNIT*posx+s34,
430                                     SGFtreeCanvas.UNIT*posy+s34,
431                                     fill='blue', tags='lines', width=2)       
432         else:
433             self.canvas.create_line(SGFtreeCanvas.UNIT*posx-s4,
434                                     SGFtreeCanvas.UNIT*(posy-1)+s34,
435                                     SGFtreeCanvas.UNIT*posx+s34,
436                                     SGFtreeCanvas.UNIT*posy+s34,
437                                     fill='blue', tags='lines', width=2)       
438             if delta > 1:
439                 self.canvas.create_line(SGFtreeCanvas.UNIT*posx-s4,
440                                         SGFtreeCanvas.UNIT*(posy-delta)+s34,
441                                         SGFtreeCanvas.UNIT*posx-s4,
442                                         SGFtreeCanvas.UNIT*(posy-1)+s34,
443                                         fill='blue', tags='lines', width=2)       
444
445
446 SGFtreeCanvas.UNIT = 25
447
448
449
450 # ---------------------------------------------------------------------------------------
451
452 class DataWindow:
453
454     def __init__(self, master):
455         self.mster = master
456        
457         window = Toplevel()
458         window.title('Data window')
459         window.protocol('WM_DELETE_WINDOW', self.quit)
460         # window.withdraw()
461         
462         win = Pmw.PanedWidget(window, orient='vertical')
463
464         self.win = win
465         win.pack(expand=YES, fill=BOTH)
466        
467         self.initPanes()
468        
469         self.SGFtreeC = SGFtreeCanvas(self.gametreeF)
470        
471         self.SGFtreeC.pack(side=LEFT, expand=YES, fill=BOTH)
472
473         self.guessModeCanvas = Canvas(self.gametreeF, width=150, height=80, background='#FFBA59')
474
475        
476        
477         self.filelistV = IntVar()
478         self.filelistV.set(1)
479         b1 = Checkbutton(self.toolbarF, text = 'Files', variable = self.filelistV,
480                          command = self.toggleFilelist, indicatoron=0)
481         b1.grid(row=0, column=0)
482        
483         self.gamelistV = IntVar()
484         self.gamelistV.set(1)
485         b1 = Checkbutton(self.toolbarF, text = 'Games', variable = self.gamelistV,
486                          command = self.toggleGamelist, indicatoron=0)
487         b1.grid(row=0, column=1)
488
489         self.gameinfoV = IntVar()
490         self.gameinfoV.set(1)
491         b1 = Checkbutton(self.toolbarF, text = 'Info', variable = self.gameinfoV,
492                          command = self.toggleGameinfo, indicatoron=0)
493         b1.grid(row=0, column=2)
494
495         self.editToolsV = IntVar()
496         self.editToolsV.set(1)
497         b1 = Checkbutton(self.toolbarF, text = 'Edit tools', variable = self.editToolsV,
498                          command = self.toggleEditTools, indicatoron=0)
499         b1.grid(row=0, column=3)
500
501         self.gametreeV = IntVar()
502         self.gametreeV.set(1)
503         b1 = Checkbutton(self.toolbarF, text = 'Game tree', variable = self.gametreeV,
504                          command = self.toggleGametree, indicatoron=0)
505         b1.grid(row=0, column=4)
506
507         self.commentsV = IntVar()
508         self.commentsV.set(1)
509         b1 = Checkbutton(self.toolbarF, text = 'Comments', variable = self.commentsV,
510                          command = self.toggleComments, indicatoron=0)
511         b1.grid(row=0, column=5)
512
513         self.filelistF.rowconfigure(0, weight=1)
514         self.filelistF.columnconfigure(0, weight=1)
515
516         self.filelist = ScrolledList(self.filelistF, height = 4)
517         self.filelist.grid(row=0, column=0, rowspan=4, sticky=NSEW)
518
519         self.filelistB1 = Button(self.filelistF, text = 'NEW', command = self.mster.newFile)
520         self.filelistB1.grid(row=0, column=1, sticky=S)
521         self.filelistB2 = Button(self.filelistF, text= 'OPEN', command = self.mster.openFile)
522         self.filelistB2.grid(row=1, column=1, sticky=S)
523         self.filelistB3 = Button(self.filelistF, text = 'DEL', command = self.mster.delFile)
524         self.filelistB3.grid(row=0, column=2, sticky=S)
525         self.filelistB4 = Button(self.filelistF, text = 'split', command=self.mster.splitCollection)
526         self.filelistB4.grid(row=1, column=2, sticky=S)
527
528         self.tkImages = []
529         for button, filename in [(self.filelistB1, 'new'), (self.filelistB2, 'open'), (self.filelistB3, 'trash'),
530                                  (self.filelistB4, 'split')]:
531             try:
532                 im = PhotoImage(file=os.path.join(self.mster.basepath, 'gifs/', filename+'.gif'))
533                 button.config(image=im)
534                 self.tkImages.append(im)
535             except:
536                 pass
537
538         self.filelist.onSelectionChange = self.mster.changeCurrentFile
539         self.filelist.list.bind('<1>', self.mster.changeCurrentFile)
540
541         self.gamelistF.rowconfigure(0, weight=1)
542         self.gamelistF.columnconfigure(0, weight=1)
543
544         self.gamelist = ScrolledList(self.gamelistF, height=4)
545         self.gamelist.grid(row=0, column=0, rowspan=4, sticky=NSEW)
546
547         self.gamelistB1 = Button(self.gamelistF, text = 'NEW', command = self.mster.newGame)
548         self.gamelistB1.grid(row=0, column=1, sticky=S)
549         self.gamelistB2 = Button(self.gamelistF, text = 'DEL', command = self.mster.delGame)
550         self.gamelistB2.grid(row=1, column=1, sticky=S)
551
552         for button, filename in [(self.gamelistB1, 'new'), (self.gamelistB2, 'trash')]:
553             try:
554                 im = PhotoImage(file=os.path.join(self.mster.basepath, 'gifs/', filename+'.gif'))
555                 button.config(image=im)
556                 self.tkImages.append(im)
557             except:
558                 pass
559        
560         self.gamelist.onSelectionChange = self.mster.changeCurrentGame
561         self.gamelist.list.bind('<1>', self.gamelistClick)
562         self.gamelist.list.bind('<B1-Motion>', self.gamelistDrag)
563         self.gamelist.list.bind('<ButtonRelease-1>', self.gamelistRelease)
564
565         self.gamelist.clickedLast = -1
566         self.gamelist.dragLast = -1
567
568         self.gameinfo = ScrolledText_HSB(self.gameinfoF, height = 4, width = 20, wrap=WORD,
569                                          relief=SUNKEN)
570         self.gameinfo.config(state=DISABLED)
571         self.gameinfo.pack(fill=BOTH, expand=NO)
572
573         lab = Label(self.editToolsF, text='Ctrl-Click:')
574        
575         self.labelType = StringVar()
576         self.labelType.set('DEL ST')
577
578         self.removeStoneButton = Radiobutton(self.editToolsF, text='DEL ST', indicatoron=0,
579                                              variable=self.labelType, value='DEL ST')
580         self.triangleButton = Radiobutton(self.editToolsF, text='TR', indicatoron=0,
581                                           variable=self.labelType, value='TR')
582         self.squareButton = Radiobutton(self.editToolsF, text='SQ', indicatoron=0,
583                                         variable=self.labelType, value='SQ')
584         self.letterUButton = Radiobutton(self.editToolsF, text='ABC', indicatoron=0,
585                                          variable=self.labelType, value='ABC')
586         self.letterLButton = Radiobutton(self.editToolsF, text='abc', indicatoron=0,
587                                          variable=self.labelType, value='abc')
588         self.numberButton = Radiobutton(self.editToolsF, text='123', indicatoron=0,
589                                         variable=self.labelType, value='123')
590
591         ca1 = Canvas(self.editToolsF, height=30, width=4, highlightthickness=0)
592         ca1.create_line(3,0,3,30, fill='black', width=2)
593         ca2 = Canvas(self.editToolsF, height=30, width=4, highlightthickness=0)
594         ca2.create_line(3,0,3,30, fill='black', width=2)
595         ca3 = Canvas(self.editToolsF, height=30, width=4, highlightthickness=0)
596         ca3.create_line(3,0,3,30, fill='black', width=2)
597
598         self.delButton = Button(self.editToolsF, text='DEL', command = lambda self=self: self.mster.delVar())
599
600         self.guessMode = IntVar()
601         self.SNM = 0
602         self.guessRightWrong = [0,0]
603         self.guessModeButton = Checkbutton(self.editToolsF, text = 'Guess mode', indicatoron=0,
604                                            variable = self.guessMode, command = self.toggleGuessMode)
605         self.mirrorButton1 = Button(self.editToolsF, text = 'M |',
606                                     command=lambda self=self, flip=flip_mirror1: self.mster.mirrorSGF(flip))
607         self.mirrorButton2 = Button(self.editToolsF, text = 'M /',
608                                     command=lambda self=self, flip=flip_mirror2: self.mster.mirrorSGF(flip))
609         self.mirrorButton3 = Button(self.editToolsF, text = 'R',
610                                     command=lambda self=self, flip=flip_rotate: self.mster.mirrorSGF(flip))
611
612
613         self.images = []
614         for button, image in [(self.removeStoneButton, 'delst.gif'), (self.triangleButton, 'tr.gif'),
615                               (self.squareButton, 'sq.gif'), (self.letterUButton, 'abc-u.gif'),
616                               (self.letterLButton, 'abc-l.gif'),
617                               (self.numberButton, '123.gif'), (self.delButton, 'delvar.gif'),
618                               (self.guessModeButton, 'guess.gif'),
619                               (self.mirrorButton1, 'mirrorv.gif'), (self.mirrorButton2, 'mirrord.gif'),
620                               (self.mirrorButton3, 'rotate.gif') ]:
621             try:
622                 im = PhotoImage(file=os.path.join(self.mster.basepath, 'gifs/', image))
623                 button.config(image=im)
624                 self.images.append(im)
625             except:
626                 pass
627
628         lab.pack(side=LEFT)
629         self.removeStoneButton.pack(side=LEFT, fill=Y)
630         self.triangleButton.pack(side=LEFT, fill=Y)
631         self.squareButton.pack(side=LEFT, fill=Y)
632         self.letterUButton.pack(side=LEFT, fill=Y)
633         self.letterLButton.pack(side=LEFT, fill=Y)
634         self.numberButton.pack(side=LEFT, fill=Y)
635         ca1.pack(side=LEFT, fill=Y)
636         self.delButton.pack(side=LEFT, fill=Y)
637         ca2.pack(side=LEFT, fill=Y)
638         self.mirrorButton1.pack(side=LEFT, fill=Y)
639         self.mirrorButton2.pack(side=LEFT, fill=Y)
640         self.mirrorButton3.pack(side=LEFT, fill=Y)
641         ca3.pack(side=LEFT, fill=Y)
642         self.guessModeButton.pack(side=RIGHT, fill=Y)
643         self.comments = ScrolledText_HSB(self.commentsF, height = 5, width = 20, wrap=WORD,
644                                          relief=SUNKEN)
645         self.comments.pack(expand=YES, fill=BOTH)
646         self.window = window
647
648
649     def initPanes(self):
650         self.toolbarF = self.win.add(name='toolb', min=28, max=28)
651         self.filelistF = self.win.add(name='filel', min=1, max=500, size=65)
652         self.gamelistF = self.win.add(name='gamel', min=1, max=500, size=65)
653         self.gameinfoF = self.win.add(name='gamei', min=1, max=500, size=75)
654         self.editToolsF = self.win.add(name='editt', min=1, max=30, size=30)
655         self.gametreeF = self.win.add(name='gamet', min=1, max=500, size=90)
656         self.commentsF = self.win.add(name='comm', min=1, max=500, size=80)
657
658     def quit(self):
659         self.mster.options.datawindowVisible.set(0)
660         self.window.withdraw()
661
662     def toggleGuessMode(self):
663         if self.guessMode.get():
664             self.guessRightWrong = [0,0]
665             self.guessModeCanvas.pack(side=RIGHT, expand=NO, fill=BOTH)
666             self.guessModeCanvas.delete(ALL)
667             self.guessModeCanvas.create_rectangle(1,1,79,79, fill='', outline='black')
668
669             if self.mster.options.showNextMoveVar.get():
670                 self.SNM = 1
671                 self.mster.options.showNextMoveVar.set(0)
672                 self.mster.showNextMove()
673             else:
674                 self.SNM = 0
675         else:
676             self.guessModeCanvas.pack_forget()
677             if self.SNM: self.mster.options.showNextMoveVar.set(1)
678             self.mster.showNextMove()
679
680     def gamelistClick(self, event):
681         self.gamelist.clickedLast = self.gamelist.list.nearest(event.y)
682         self.gamelist.dragLast = -1
683         self.mster.changeCurrentGame(event)
684
685     def gamelistDrag(self, event):
686
687         i = self.gamelist.list.nearest(event.y)
688         if self.gamelist.dragLast == -1:
689             if self.gamelist.clickedLast == i: return
690             else: self.gamelist.dragLast = self.gamelist.clickedLast
691        
692         if self.gamelist.dragLast != i:
693             s = self.gamelist.list.get(self.gamelist.dragLast)
694             self.gamelist.delete(self.gamelist.dragLast)
695             self.gamelist.insert(i, s)
696             self.gamelist.list.select_set(i)
697             self.gamelist.dragLast = i
698         return 'break'
699
700     def gamelistRelease(self, event):
701
702         if self.gamelist.dragLast == -1:
703             return None, None
704        
705         i = self.gamelist.list.nearest(event.y)
706
707         if self.gamelist.dragLast != i:
708             s = self.gamelist.list.get(self.gamelist.dragLast)
709             self.gamelist.delete(self.gamelist.dragLast)
710             self.gamelist.insert(i, s)
711             self.gamelist.list.select_set(i)
712             self.gamelist.dragLast = i
713
714         if self.gamelist.clickedLast != i:
715
716             try:
717                 n = self.mster.cursor.root.next
718                 for j in range(self.gamelist.clickedLast): n = n.down
719
720                 m = self.mster.cursor.root.next
721                 for j in range(i): m = m.down
722
723                 if n.up: n.up.down = n.down
724                 else: self.mster.cursor.root.next = n.down
725                 if n.down: n.down.up = n.up
726
727                 if i < self.gamelist.clickedLast: # insert n above m
728                     n.up = m.up
729                     if m.up: m.up.down = n
730                     else: self.mster.cursor.root.next = n
731                     m.up = n
732                     n.down = m
733                 else:                             # insert n below m
734                     n.down = m.down
735                     if m.down: m.down.up = n
736                     m.down = n
737                     n.up = m
738
739                 self.mster.cursor.updateGamelist(0)
740                 self.mster.cursor.currentGame = i
741                 self.gamelist.list.select_set(i)
742                 self.gamelist.list.see(i)
743                 self.updateGameInfo(self.mster.cursor)
744
745                 filelistIndex = self.mster.currentFileNum
746                 d = self.mster.filelist[filelistIndex][4]
747
748                 if i < self.gamelist.clickedLast:
749
750                     if d.has_key(self.gamelist.clickedLast):
751                         p_last = d[self.gamelist.clickedLast]
752                     else: p_last = ()
753
754                     jj = self.gamelist.clickedLast
755                     while jj > i:
756                         if d.has_key(jj-1):
757                             d[jj] = d[jj-1]
758                         else:
759                             if d.has_key(jj): del d[jj]
760                         jj -= 1
761
762                     d[i] = p_last
763
764                 else:
765                     if d.has_key(i):
766                         p_i = d[i]
767                     else: p_i = ()
768
769                     jj = self.gamelist.clickedLast
770                     while jj < i:
771                         if d.has_key(jj+1):
772                             d[jj] = d[jj+1]
773                         else:
774                             if d.has_key(jj):
775                                 del d[jj]
776                         jj += 1
777
778                     d[self.gamelist.clickedLast] = p_i
779
780                 return self.gamelist.clickedLast, i
781
782             except SGFError: showwarning('Error', 'SGF error')
783             except: showwarning('Error', 'An error occured, please send a bug report.')
784            
785         return None, None
786
787     def get_geometry(self):
788         l = [ self.filelistV.get(), self.win._size['filel'],
789               self.gamelistV.get(), self.win._size['gamel'],
790               self.gameinfoV.get(), self.win._size['gamei'],
791               self.editToolsV.get(), self.win._size['editt'],
792               self.gametreeV.get(), self.win._size['gamet'],
793               self.commentsV.get(), self.win._size['comm'] ]
794         l1 = [ `x` for x in l ]
795         l1.append(self.window.geometry())
796
797         return join(l1, '|%')
798        
799     def set_geometry(self, s):
800         l = split(s, '|%')
801
802         l1 = [ self.filelistV,
803                self.gamelistV,
804                self.gameinfoV,
805                self.editToolsV,
806                self.gametreeV,
807                self.commentsV ]
808                
809         for i in range(len(l)/2):
810             l1[i].set(int(l[2*i]))
811             if int(l[2*i]):
812                 self.win.configurepane(i+1, min=1, max=500, size = int(l[2*i+1]))
813             else:
814                 self.win.configurepane(i+1, min=1, max=1, size = 1)
815         self.win.updatelayout()
816         self.window.geometry(l[-1])
817
818     def toggleFilelist(self):
819         if self.filelistV.get():
820             s = self.window.geometry()
821             x1, x2, x3 = split(s, '+')
822             x, y = split(x1, 'x')
823             y = `int(y)+65`
824             self.window.geometry('%sx%s+%s+%s' % (x, y, x2, x3))
825             self.win.configurepane(1, min=10, max=1.0, size=65)
826         else:
827             s = self.window.geometry()
828             x1, x2, x3 = split(s, '+')
829             x, y = split(x1, 'x')
830             y = `int(y)-self.win._size['filel']`
831             self.window.geometry('%sx%s+%s+%s' % (x, y, x2, x3))
832             self.win.configurepane(1, min=0.0, max=0.0, size=0.0)
833         self.win.updatelayout()
834        
835     def toggleGamelist(self):
836         if self.gamelistV.get():
837             s = self.window.geometry()
838             x1, x2, x3 = split(s, '+')
839             x, y = split(x1, 'x')
840             y = `int(y)+65`
841             self.window.geometry('%sx%s+%s+%s' % (x, y, x2, x3))
842             self.win.configurepane(2, min=10, max=1.0, size=65)
843         else:
844             s = self.window.geometry()
845             x1, x2, x3 = split(s, '+')
846             x, y = split(x1, 'x')
847