root/06/devel-old/v.py

Revision 175, 111.5 kB (checked in by ug, 2 years ago)

Done some more work on pattern.cc; seems to work now.

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