root/05/devel/v.py

Revision 157, 113.6 kB (checked in by ug, 5 years ago)

Encoding fixes in v.gameinfoOK

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