# TkTeXCad
# (C) Copyright 2001 Hilmar Straube; dieses Programm untersteht der GPL.
# Keinerlei Garantie auf was auch immer, was mit diesem Programm zu tun hat.

from Tkinter import *
import tkSimpleDialog
import tkFileDialog
import tkMessageBox

import traceback # PrettyPrint TB
import math
import sys # Traceback, exit

import pickle # Datei speichern/laden

try:
       import Image # EPS-Unterst�tzung
       import ImageTk
       FPIL = 1
except ImportError:
       FPIL = 0

from internat import *
from config import *
from globalpars import *
from picels import *

class diameter: # Sucht einen passenden Durchmesser zu gegebenem
       def getd(self, d, as):
               index = 0
               if as:
                       pd = POSSIBLEDIAMETERSAS
               else:
                       pd = POSSIBLEDIAMETERS
               while pd[index] < d:
                       index += 1
                       if index >= len(pd) - 1:
                               return pd[index]

               if index == 0: # index - 1 gibt �rger!
                       return pd[0]
               if abs(pd[index] - d) < abs(pd[index - 1] - d):
                       return pd[index]
               else:
                       return pd[index - 1]

diameterer = diameter()

class angle: # Klasse zum Bestimmen eines besten LaTeX-Paares zu einem gegebenen Winkel
       def __init__(self, set):
               self.erg = []
               for dxdy in set:
                       if dxdy != (0, 1):
                               m = dxdy[1]/float(dxdy[0])
                               alpha = math.atan(m)
                               self.erg.append([alpha, dxdy])
                       else:
                               self.erg.append([math.pi/2, dxdy])
       def getdxdy(self, alpha): # Winkel im Bereich [0; pi/2]
               dev = -1
               index = -1
               z = 0
               for el in self.erg:
                       aktudev = abs(el[0] - alpha)
                       if aktudev < dev or index == -1:
                               index = z
                               dev = aktudev
                       z += 1
               return self.erg[index]

angler = angle(POSSIBLEDXDY) # F�r allg. Benutzung
vecangler = angle(VECDXDY) # F�r Pfeile

class mainwindow: # Benutzeroberfl�che
       def __init__(self, picc):
               self.root = Tk() # Hauptfenster

               self.selected = [] # Nix angew�hlt
               self.parts = pcs() # Noch keine Teilbilder
               self.tkclass = IntVar() # F�r die Auswahl der blockerten
               self.tkclass.set(-1)
               self.tkchangeclass = IntVar() # ... und unblockierten Teilbilder in picmenu
               self.tkchangeclass.set(0)

               self.root.title(PROGNAME)
               helperwin = Toplevel(self.root)
               helperwin.transient(self.root)
               helperwin.protocol('WM_DELETE_WINDOW', self.donothing)
               self.helperwin = helperwin
               helperwin.title(CHANGEWINTITLE[LANG])
               self.changeframe = Frame(helperwin) # Frame f�r weitere Operationen
               self.changeframe.pack(fill = BOTH, expand = 1) # Ist hier nur, damit es zerst�rt werden kann. Das eigentlich wichtige wird in .editwin getan.
               Label(self.changeframe, text = STATUSNOSELECTION[LANG]).pack()

               # Widgets basteln

               self.yscrollbar = Scrollbar(self.root, orient = VERTICAL)
               self.xscrollbar = Scrollbar(self.root, orient = HORIZONTAL)

               self.canvas = Canvas(self.root, xscrollcommand = self.xscrollbar.set, yscrollcommand = self.yscrollbar.set, borderwidth = 1, relief = RIDGE, width = CANVASWIDTH, height = CANVASHEIGHT, background = CANVASBG)

               self.yscrollbar['command'] = self.canvas.yview
               self.xscrollbar['command'] = self.canvas.xview

               self.statusframe = Frame(self.root, borderwidth = 1, relief = SUNKEN)
               self.statuslabel = Label(self.statusframe)
               self.statusframe.grid(row = 2, column = 0, columnspan = 2, sticky = E + W)
               self.statuslabel.pack(side = LEFT)

               # Anzeigen

               self.canvas.grid(row = 0, column = 0, sticky = N + E + S + W)
               self.yscrollbar.grid(row = 0, column = 1, sticky = N + S)
               self.xscrollbar.grid(row = 1, column = 0, sticky = W + E)
               self.root.rowconfigure(0, weight = 1)
               self.root.columnconfigure(0, weight = 1)

               filemenu = Menu()
               filemenu.add_command(label = MENUFILELOAD[LANG], command = self.filemenu_load)
               filemenu.add_command(label = MENUFILEINSERT[LANG], command = self.filemenu_import)
               filemenu.add_command(label = MENUFILESAVE[LANG], command = self.filemenu_save)
               filemenu.add_command(label = MENUTEXEXPORT[LANG], command = self.filemenu_export)
               filemenu.add_separator()
               filemenu.add_command(label = MENUQUIT[LANG], command = self.filemenu_quit)

               self.root.bind("<Control-l>", self.filemenu_load)
               self.root.bind("<Control-s>", self.filemenu_save)
               self.root.bind("<Control-i>", self.filemenu_import)
               self.root.bind("<Control-x>", self.filemenu_export)
               self.root.bind("<Control-q>", self.filemenu_quit)

               # Menu bauen
               newmenu = Menu()
               newmenu.add_command(label = MENUCIRCLE[LANG], command = self.newcircle)
               newmenu.add_command(label = MENUCIRCLEAS[LANG], command = self.newcircleas)
               newmenu.add_command(label = MENULINE[LANG], command = self.newline)
               newmenu.add_command(label = MENUVECTOR[LANG], command = self.newvec)
               newmenu.add_command(label = MENUOVAL[LANG], command = self.newoval)
               newmenu.add_command(label = MENUBEZIER[LANG], command = self.newbezier)
               newmenu.add_command(label = MENUBB[LANG], command = self.newbb)
               newmenu.add_command(label = MENUEPS[LANG], command = self.neweps)
               newmenu.add_separator()
               newmenu.add_command(label = MENUSIMPLETEXT[LANG], command = self.newsimpletext)
               newmenu.add_command(label = MENUSHORTSTACK[LANG], command = self.newshortstack)
               newmenu.add_command(label = MENUTEXTBOX[LANG], command = self.newtextbox)

               self.root.bind("k", self.newcircle)
               self.root.bind("K", self.newcircleas)
               self.root.bind("l", self.newline)
               self.root.bind("L", self.newvec)
               self.root.bind("o", self.newoval)
               self.root.bind("q", self.newbezier)
               self.root.bind("B", self.newbb)
               self.root.bind("e", self.neweps)

               self.root.bind("t", self.newsimpletext)
               self.root.bind("a", self.newshortstack)
               self.root.bind("b", self.newtextbox)

               faktmenu = Menu()
               self.tkfakt = IntVar()
               self.tkfakt.set(FAKT)
               for fakt in FAKTS:
                       faktmenu.add_radiobutton(label = `fakt` + MENUSCALELABEL[LANG], variable = self.tkfakt, value = fakt, command = self.faktmenu_change)
               faktmenu.add_separator()
               self.tkfGrid = IntVar()
               self.tkfGrid.set(1)
               faktmenu.add_checkbutton(label = MENUSHOWGRID[LANG], variable = self.tkfGrid, command = self.faktmenu_gridf)
               faktmenu.add_command(label = MENUCHANGEGRID[LANG], command = self.faktmenu_grid)
               self.root.bind("<Control-g>", self.faktmenu_gridfkey)
               self.root.bind("<Control-Shift-G>", self.faktmenu_grid)

               selmenu = Menu()
               selmenu.add_command(label = MENUSELECT[LANG], command = self.getuids)
               selmenu.add_command(label = MENUDESELECT[LANG], command = self.clearselect)
               selmenu.add_command(label = MENUDEL[LANG], command = self.delselected)
               self.root.bind("<space>", self.getuids)
               self.root.bind("<Escape>", self.clearselect)
               self.root.bind("<Delete>", self.delselected)

               clipmenu = Menu()
               clipmenu.add_command(label = CLIPCOPY[LANG], command = self.clip_copy)
               clipmenu.add_command(label = CLIPCUT[LANG], command = self.clip_cut)
               clipmenu.add_command(label = CLIPPASTE[LANG], command = self.clip_paste)
               self.root.bind("<Control-c>", self.clip_copy)
               self.root.bind("<Control-x>", self.clip_cut)
               self.root.bind("<Control-v>", self.clip_paste)

               # Passt nirgendwo sonst
               self.root.bind('<Control-a>', self.picmenu_adjustdxdy)

               self.picmenu = Menu(postcommand = self.updatepicmenu, tearoff = 0) # Abrei�en gibt bei diesem Men� heftig �rger
               self.viewpicture(picc)

               # Funktionsweise des Callbackwaldes:
               # die new...-Funktionen erstellen das gefragte Objekt und stellen Fest, ob
               # ein, zwei oder drei Punkte gefordert sind (mousinputdepth).
               # Daraufhin rufen sie eine Funktion auf, die einen (ebenfalls leeren) putter darum erstellt.
               # Der erste Klick f�llt in clickcb diese Putterstruktur.
               # Sind mehr Punkte n�tig, werden die Mausbewegungen in motion abgefangen und das picel �ber
               # seines putters minfo-Routine �ber die Position der Maus informiert.
               # Nach dieser Information wird die Anzeige aufgefrischt.
               # Bei Tiefe 3 wird dann noch clickcb2 ausgef�hrt, der motion so �ndert, dass minfo2 aufgerufen wird.
               # Sind alle Informationen aufgerufen, folgt done und ein self.update().

               menu = Menu(self.root)
               self.root["menu"] = menu
               menu.add_cascade(label = MENUCLASSES[LANG], menu = self.picmenu)
               menu.add_cascade(label = MENUFILE[LANG], menu = filemenu)
               menu.add_cascade(label = MENUNEW[LANG], menu = newmenu)
               menu.add_cascade(label = MENUDISPLAY[LANG], menu = faktmenu)
               menu.add_cascade(label = MENUELEM[LANG], menu = selmenu)
               menu.add_cascade(label = MENUCLIP[LANG], menu = clipmenu)

               # Ist gerade kein Men� vorhanden, soll ein linker Mausklick das Bearbeitungsfenster �ffnen
               self.canvas.bind("<Button-1>", self.editwin)
               self.frameuid = None
               self.griduids = []

       def filemenu_load(self, event = None):
               self.changeframe.destroy()
               ft = [(FTLABEL[LANG], "*.pic")]
               filename = tkFileDialog.askopenfilename(title = LOADTITLE[LANG], filetypes = ft)
               if filename == '':
                       return
               try:
                       f = open(filename)
                       self.parts = pickle.load(f)

                       for picclass in self.parts.pictures: # F�r alle Putter, die gerade geladen werden diese Information bereit stellen.
                               for putter in picclass.putters:
                                       putter.notifyunpickle(self)

                       f.close()
                       self.viewpicture(self.parts.pictures[0])
                       self.changeframe.destroy()
                       self.update()
               except:
                       tkMessageBox.showerror(title = PROGNAME, message = LOADERROR[LANG] + `sys.exc_info()[0]`)
                       traceback.print_tb(sys.exc_info()[2])
       def filemenu_save(self, event = None):
               ft = [(FTLABEL[LANG], "*.pic")]
               filename = tkFileDialog.asksaveasfilename(title = SAVETITLE[LANG], filetypes = ft, defaultextension = '.pic')
               if filename == '':
                       return
               try:
                       f = open(filename, "w")
                       for pc in self.parts.pictures: # Unpickelbare tk-Variablen vernichten
                               pc.untk()
                               pickle.dump(self.parts, f)
                       for picclass in self.parts.pictures: # F�r alle Putter, die gerade geladen werden: diese Information bereit stellen.
                               for putter in picclass.putters:
                                       putter.notifypickle(self)

                       f.close()
               except:
                       tkMessageBox.showerror(title = PROGNAME, message = SAVEERROR[LANG] + `sys.exc_info()[0]`)
                       traceback.print_tb(sys.exc_info()[2])
       def filemenu_import(self, event = None): # fast wie load
               self.changeframe.destroy()
               ft = [(FTLABEL[LANG], "*.pic")]
               filename = tkFileDialog.askopenfilename(title = INSERTTITLE[LANG], filetypes = ft)
               if filename == '':
                       return
               try:
                       f = open(filename)
                       newparts = pickle.load(f)
                       f.close()
                       for newname in newparts.names:
                               if newname in self.parts.names:
                                       self.status(STATUSNAMECOL[LANG])
                                       return
                       # Keine �berschneidungen
                       for picclass in newparts.pictures: # F�r alle Putter, die gerade geladen werden: diese Information bereit stellen.
                               for putter in picclass.putters:
                                       putter.notifypickle(self)
                       self.parts.pictures += newparts.pictures
                       self.parts.names += newparts.names
               except:
                       tkMessageBox.showerror(title = PROGNAME, message = LOADERROR[LANG] + `sys.exc_info()[0]`)
                       traceback.print_tb(sys.exc_info()[2])
       def filemenu_export(self, event = None):
               z = 0
               for z in range(len(self.parts.names)):
                       f = open(self.parts.names[z] + SUFFIX, 'w')
                       f.write(self.parts.pictures[z].dumptex())
       def filemenu_quit(self, event = None):
               self.root.destroy()
       def faktmenu_change(self, event = None):
               global fakt
               fakt = self.tkfakt.get()
               notifyfaktchange(fakt)
               self.update()
       def faktmenu_gridf(self, event = None):
               global FGRID
               FGRID = self.tkfGrid.get()
               self.update()
       def faktmenu_gridfkey(self, event = None):
               self.tkfGrid.set(not self.tkfGrid.get())
               self.faktmenu_gridf()
       def faktmenu_grid(self, event = None):
               global GRID
               g = tkSimpleDialog.askfloat(PROGNAME, LABELNEWGRID[LANG], minvalue = 0.0000001)
               if g != None:
                       GRID = g
                       self.update()
       def picmenu_addmenus(self, menu, block): # H�ngt an menu Radiobuttons an, m�glicherweise enthaltende blockiert, oder auch nicht.
               if block:
                       blocks = self.parts.pictures[0].getallblocks(self.pic.pc) + [self.pic.pc]
               for z in range(len(self.parts.pictures)):
                       sf = self.parts.names[z]
                       if block:
                               if self.parts.pictures[z] in blocks:
                                       menu.add_radiobutton(label = sf, state = DISABLED, variable = self.tkclass, value = z)
                               else:
                                       menu.add_radiobutton(label = sf, state = NORMAL, variable = self.tkclass, value = z)
                       else:
                               menu.add_radiobutton(label = sf, variable = self.tkchangeclass, value = z, command = self.picmenu_changepc)
       def updatepicmenu(self):
               self.picmenu.delete(0, 99999) # Das ist unsch�n!

               self.picmenu.add_command(label = MENUNEWPICCLASS[LANG], command = self.createpic)
               self.picmenu.add_separator()

               self.picmenu_addmenus(self.picmenu, 1)
               changemenu = Menu()
               self.picmenu_addmenus(changemenu, 0)

               self.picmenu.add_separator()

               self.picmenu.add_command(label = MENUNEWINSTANCE[LANG], command = self.picmenu_newpicture)
               self.picmenu.add_command(label = MENUDELCLASS[LANG], command = self.picmenu_delpc)
               self.picmenu.add_command(label = MENUSIZECHANGE[LANG], command = self.picmenu_changesize)
               self.picmenu.add_command(label = MENUADJUSTDXDY[LANG], command = self.picmenu_adjustdxdy)

               self.picmenu.add_cascade(label = MENUCHANGECLASS[LANG], menu = changemenu)
       def picmenu_getclass(self, nr = 0): # Liest tkclass aus und gibt Fehlermeldung und None bzw. Klasse
               blocks = rootpc.getallblocks(self.pic.pc) + [self.pic.pc]
               classnr = self.tkclass.get()
               if classnr == -1:
                       self.status(STATUSNOCLASSEL[LANG])
                       return None
               elif self.parts.pictures[classnr] in blocks:
                       self.status(STATUSBLOCKED[LANG])
                       return None
               else:
                       if nr: # Nutzer der Klasse w�hlt, ob er Nummer oder Instanz braucht
                               return classnr
                       else:
                               return self.parts.pictures[classnr]
       def picmenu_newpicture(self, event = None):
               picclass = self.picmenu_getclass()
               if picclass == None:
                       return
               self.pendingpicel = picture(picclass, 1.0, self.pic.pc)
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def picmenu_delpc(self, event = None):
               nr = self.picmenu_getclass(nr = 1)
               if nr == None:
                       return
               picclass = self.parts.pictures[nr]
               if picclass == None:
                       return

               blocks = self.parts.pictures[0].getallblocks(self.parts.pictures[nr]) # Klassen bestimmen, in deren Instanzen die Klasse enthalten ist.
               if blocks != [] or nr == 0: # L�schbar ist er nur, wenn nirgendwo Instanzen lungern und es nicht das Hauptbild ist.
                       self.status(STATUSDELCLASSREJECT[LANG])
                       return
               del self.parts[nr]
       def picmenu_changepc(self, event = None):
               pc = self.parts.pictures[self.tkchangeclass.get()]
               self.viewpicture(pc)
               self.changeframe.destroy()
               self.update()
       def picmenu_changesize(self, event = None): # Fenster zur Gr��en�nderung
               sc = sizechanger(self.root, self.pic.pc.xsize, self.pic.pc.ysize, self.pic.pc.name, self.parts.names, self.picmenu_sccb)
       def picmenu_sccb(self, xs, ys, name):
               self.pic.pc.xsize = xs
               self.pic.pc.ysize = ys

               self.parts.changename(self.pic.pc.name, name) # in parts und pc �ndern

               self.canvas.delete(self.frameuid)
               self.frameuid = self.canvas.create_rectangle(0, fakt * self.pic.pc.ysize, fakt * self.pic.pc.xsize, 0, fill = None, outline = PICFRAMECOL)
               self.update()
       def picmenu_adjustdxdy(self, event = None):
               self.pic.pc.adjustdxdy()
               self.update()
       # ENDE picmenu
       def clip_copy(self, event = None):
                       #def createpicwork(self, newpicclass, action):
               # action: 0: Instanz einf�gen; action 1: Einzelelemente lassen; action 2: Einzelemente nur verschwinden lassen
               self.clipboard = pictureclass(0, 0, CLIPBOARDNAME)
               self.createpicwork(self.clipboard, 1)
               self.update()
       def clip_cut(self, event = None):
               self.clipboard = pictureclass(0, 0, CLIPBOARDNAME)
               self.createpicwork(self.clipboard, 2)
               self.update()
       def clip_paste(self, event = None):
               self.pendingpicel = picture(self.clipboard, 1.0, self.pic.pc)
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel() # done k�mmert sich um die Aufl�sung in Einzelelemente
       # ENDE clip
       def viewpicture(self, picc):
               self.tkclass.set(-1) # Danach ist die angeklickte Klasse mgl. blockiert
               try:
                       self.pic.undraw(self.canvas)
               except AttributeError:
                       pass # Falls pic noch nicht existiert
               self.pic = picture(picc, 1.0, None)
       def donothing(self): # Um das Nebenfenster zu erhalten
               pass
       def drawgrid(self):
               self.griduids = []
               xanz = int(self.pic.pc.xsize / GRID)
               yanz = int(self.pic.pc.ysize / GRID)
               if xanz != 0:
                       xstep = self.pic.pc.xsize / float(xanz)
               if yanz != 0:
                       ystep = self.pic.pc.ysize / float(yanz)
               for z in range(xanz):
                       self.griduids.append(self.canvas.create_line(fakt * (z * xstep),
                                                                   fakt * (self.pic.pc.ysize - 0),
                                                                   fakt * (z * xstep),
                                                                   0, # fakt * (ysize - ysize)
                                                                   fill = GRIDCOL))
               for z in range(yanz):
                       self.griduids.append(self.canvas.create_line(0,
                                                                    fakt * (self.pic.pc.ysize - z * ystep),
                                                                    fakt * self.pic.pc.xsize,
                                                                    fakt * (self.pic.pc.ysize - z * ystep),
                                                                    fill = GRIDCOL))
               for uid in self.griduids:
                       self.canvas.lower(uid)
       def undrawgrid(self):
               for uid in self.griduids:
                       self.canvas.delete(uid)
       def update(self):
               # Rahmen und Netz und Bild entfernen (falls vorhanden)
               if self.frameuid != None:
                       self.canvas.delete(self.frameuid)
               self.undrawgrid()
               self.pic.undraw(self.canvas)

               if FGRID:
                       self.drawgrid()
               self.frameuid = self.canvas.create_rectangle(0, fakt * self.pic.pc.ysize, fakt * self.pic.pc.xsize, 0, fill = None, outline = PICFRAMECOL) # Rahmen

               self.pic.draw(0, 0, self.pic.pc.ysize, self.canvas) # Bild
               self.canvas['scrollregion'] = self.canvas.bbox(ALL) # Scrollleisten einstellen
               self.selected = [] # mgl. Auswahl angezeigt l�schen, Anzeige ist schon weg (Bilder aufl�sen k�nnte �rger geben beim weiter halten)
       def status(self, text):
               self.statuslabel['text'] = text
       def editwin(self, event): # Dem Mausklick n�chste Uid suchen, putter holen und aufrufen
               x = event.widget.canvasx(event.x)
               y = event.widget.canvasy(event.y)
               if FGRID: # Das Netz st�rt bei find-closest
                       self.undrawgrid()
               uid = event.widget.find_closest(x, y)[0]
               if FGRID:
                       self.drawgrid()
               putter = self.pic.getputter(uid)
               # Putter == 0: Rahmen wurde angeklickt:
               if putter == 0:
                       return
               self.editwin2(putter)
       def editwin2(self, putter): # wird von putter und done (Objekt erzeugt) aufgerufen
               self.changeframe.destroy()
               self.changeframe = Frame(self.helperwin)
               self.changeframe.pack(fill = BOTH, expand = 1)
               putter.change(self.changeframe, self)
       def bindings(self, done, cancel):
               self.canvas.bind("<Motion>", self.motion)
               self.canvas.bind("<Button-1>", done)
               self.canvas.bind("<Button-3>", cancel)
               self.movecb_uids = []
       def unbind(self):
               self.canvas.bind("<Button-1>", self.editwin) # Zur�ckbinden
               self.canvas.unbind("<Motion>")
               self.canvas.unbind("<Button-3>")
       def motion(self, event):
               self.pendingputter.undraw(self.canvas)
               x = round(self.canvas.canvasx(event.x) / float(fakt * GRID)) * GRID
               y = self.pic.pc.ysize - round(self.canvas.canvasy(event.y) / float(fakt * GRID)) * GRID
               if self.motionmode == 0:
                       self.pendingputter.x = x
                       self.pendingputter.y = y
               elif self.motionmode == 1:
                       self.pendingputter.minfo(x, y) # Information �bertragen
               elif self.motionmode == 2:
                       self.pendingputter.minfo2(x, y) # Falls drei Mausklicks n�tig sein sollten ...
               self.pendingputter.draw(0, 0, self.pic.pc.ysize, self.canvas) # neu zeichnen
       def cancel(self, event):
               self.unbind()
               self.status(STATUSCANCEL[LANG])
               self.pendingputter.undraw(self.canvas)
               del self.pendingpicel
               del self.pendingputter
       def done(self, event):
               if self.mouseinputdepth == 1:
                       self.pendingputter.x = round(self.canvas.canvasx(event.x) / float(fakt * GRID)) * GRID
                       self.pendingputter.y = self.pic.pc.ysize - round(self.canvas.canvasy(event.y) / float(fakt * GRID)) * GRID
               self.unbind()
               self.status(STATUSDONE[LANG])

               self.pic.putters.append(self.pendingputter) # Erstelltes Objekt einf�gen sowie hiesige Referenz entfernen

               if not(self.pendingputter.pe.__class__ == picture and self.pendingputter.pe.pc.name == CLIPBOARDNAME):
                       self.editwin2(self.pendingputter) # Aufl�ser!

               self.pendingputter.notifydone(self) # Benachrichtigung schicken

               del self.pendingpicel
               del self.pendingputter

               self.update() # UIDs auffrischen
       def clickcb(self, event):
               # Putter mit richtigen Anfangskoordinaten versorgen
               self.pendingputter.x = round(self.canvas.canvasx(event.x) / float(fakt * GRID)) * GRID
               self.pendingputter.y = self.pic.pc.ysize - round(self.canvas.canvasy(event.y) / float(fakt * GRID)) * GRID
               self.status(STATUSPOINT2[LANG])
               if self.mouseinputdepth == 2:
                       self.bindings(self.done, self.cancel) # done, cancel und motion aktivieren
               elif self.mouseinputdepth == 3:
                       self.bindings(self.clickcb2, self.cancel)
               elif self.mouseinputdepth == 200:
                       self.bindings(self.getuids2, self.cancel)
               self.motionmode = 1
       def clickcb2(self, event):
               self.status(STATUSPOINT3[LANG])
               if self.mouseinputdepth == 3:
                       self.bindings(self.done, self.cancel)
               self.motionmode = 2
       def newpicel(self, event = None):
               self.pendingputter = multiputter(0, 0, 0, 0, 1, self.pendingpicel)
               self.status(STATUSPOINT1[LANG])
               self.motionmode = 0
               if self.mouseinputdepth > 1:
                       self.bindings(self.clickcb, self.cancel)
               else:
                       self.bindings(self.done, self.cancel)
       def newline(self, event = None):
               self.pendingpicel = line(1, 1, 1, 0)
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def newvec(self, event = None):
               self.pendingpicel = line(1, 1, 1, 1)
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def newcircleas(self, event = None):
               self.pendingpicel = circle(1, 1)
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def newcircle(self, event = None):
               self.pendingpicel = circle(1, 0)
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def newoval(self, event = None):
               self.pendingpicel = oval(3, 2)
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def newbezier(self, event = None):
               self.pendingpicel = bezier(0, 0, 1, 1, 2, 1)
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def newbb(self, event = None):
               self.pendingpicel = bb(2, 2)
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def neweps(self, event = None):
               self.pendingpicel = eps()
               ft = [(FTLABEL[LANG], "*.eps")]
               filename = tkFileDialog.askopenfilename(title = EPSLOADTITLE[LANG], filetypes = ft)
               if FPIL:
                       if not self.pendingpicel.loadfile(filename, self):
                               del self.pendingpicel
                               return
               else:
                       self.pendingpicel.filename = filename
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def newsimpletext(self, event = None):
               self.pendingpicel = simpletext(INITIALSIMPLETEXT[LANG])
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def newshortstack(self, event = None):
               self.pendingpicel = shortstack(1, INITIALSHORTSTACK[LANG])
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def newtextbox(self, event = None):
               self.pendingpicel = textbox(0, 0, 1, 1, DEFAULTDASHLEN, '')
               self.mouseinputdepth = self.pendingpicel.clickdepth
               self.newpicel()
       def getuids(self, event = None):
               self.pendingpicel = textbox(0, 0, 1, 1, 0, INITIALTEXTBOX[LANG])
               self.mouseinputdepth = 200
               self.newpicel()
               # Unsch�n (200): Teilt den Nachfolgenden Prozeduren mit, dass hier eigentlich nur die Anzeigeroutinen der Textbox benutzt werden, aber nur deren Koordinaten interessieren. Damit werden dann in einer ver�nderten Version von done die richtigen Koordinaten gesichert.
       def getuids2(self, event): # Genau hierhin. self.pendingpicel d�rfte nun die gew�nschten Daten enthalten
               self.update() # Eine einfache undraw-Routine scheitert daran, dass fill unterschiedliche Werte anfangs hat.
               # Koordinaten ausrechnen
               self.pendingputter.undraw(self.canvas) # Die uids l�schen, die zum gezogenen Rahmen geh�ren, sp�ter wird er auch gel�scht.
               x = fakt * self.pendingputter.x
               y = fakt * (self.pic.pc.ysize - self.pendingputter.y)
               x2 = fakt * self.pendingpicel.dx + x
               y2 = fakt * (self.pic.pc.ysize - (self.pendingputter.y + self.pendingpicel.dy))
               uids = self.canvas.find_overlapping(x, y, x2, y2)
               uids = list(uids) # find liefert tupel

               putters = []
               for uid in uids:
                       for putter in self.pic.putters:
                               if uid in putter.uids and putter not in putters:
                                       putters.append(putter)
               self.selected = putters

               self.status(`len(putters)` + STATUSXELSSELECTED[LANG])

               # Hilfsrechteck nun endg�ltig l�schen

               del self.pendingpicel
               del self.pendingputter

               self.unbind()
               self.select()
       def select(self): # Auswahl anzeigen
               for putter in self.selected:
                       putter.mark(self.canvas)
       def clearselect(self, event = None): # Auswahl l�schen
               self.selected = []
               self.status(STATUSDESELECT[LANG])
               self.update()
       def delselected(self, event = None, update = 1): # AusGEW�HLTE l�schen
               for putter in self.selected:
                       putter.undraw(self.canvas)
                       index = self.pic.putters.index(putter)
                       del self.pic.putters[index]
               if update:
                       self.status(STATUSELSDELETED[LANG])
                       self.update()
       def createpic(self):
               if self.selected != []:
                       # Namen abfragen und �berpr�fen
                       name = tkSimpleDialog.askstring(PROGNAME, LABELNEWCLASSNAME[LANG])
                       if name == None or not(self.parts.newname(name)) or name == "":
                               self.status(STATUSINVALIDNAME[LANG])
                               return
                       pc = pictureclass(0, 0, name)
                       self.createpicwork(pc, 0)
                       self.update()
                       self.parts.append(pc)
                       self.status(STATUSNEWCLASS[LANG])
               else:
                       self.status(STATUSNOSELECTION[LANG])
       def createpicwork(self, newpicclass, action):
               # action: 0: Instanz einf�gen; action 1: Einzelelemente lassen; action 2: Einzelemente nur verschwinden lassen
               if self.selected != []:
                       x1, y1, x2, y2 = plboundingbox(self.selected)
                       dx = x2 - x1
                       dy = y2 - y1

                       newpicclass.xsize, newpicclass.ysize = dx, dy

                       # Alle putter bewegen
                       for putter in self.selected:
                               putter.x -= x1
                               putter.y -= y1
                       # Neue Bildklasse damit f�llen
                       if action != 1:
                               newpicclass.putters = self.selected
                       else: # F�r Kopieren brauchen wir eine hier und eine Unabh�ngige in der Zwischenablage
                               for el in self.selected:
                                       elcopy = el.copy()
                                       el.x += x1
                                       el.y += y1
                                       newpicclass.putters.append(elcopy)
                       self.pic.undraw(self.canvas) # alle Canvasitems entfernen (Referenz k�nnte verschwinden)
                       if action != 1:
                               self.delselected(update = 0) # ... jetzt
                       if action == 0:
                               self.pic.putters.append( # Anstelle dessen kommte nur ein putter f�r das neu erstellte Objekt der neuen Klasse
                                       multiputter(x1, y1, 0, 0, 1, picture(newpicclass, 1.0, self.pic.pc))
                                       )
                       self.changeframe.destroy()

class multiputter: # Steht f�r einen multiput-Befehl
       def dumptex(self):
               if self.pe.__class__ in [textbox, bb]: # Dieser macht das selbst, da LaTeX nicht mit negativen H�hen und Breiten rechnet.
                       return self.pe.dumptex(self.x, self.y, self.dx, self.dy, self.n)
               else:
                       if self.n != 1:
                               sf = '\\multiput' + `(self.x, self.y)` + `(self.dx, self.dy)` + '{' + `self.n` + '}{'
                       else:
                               sf = '\\put' + `(self.x, self.y)` + '{'

                       sf += self.pe.dumptex() + '}'
                       return sf
       def __init__(self, x, y, dx, dy, n, pe):
               "dx, dy - jew. Verschiebung beim n-ten Anzeigen"
               self.x = x
               self.y = y
               self.pe = pe
               self.dx = dx
               self.dy = dy
               self.n = n
               self.multiput = 1
               self.uids = []
       def mark(self, canvas):
               for uid in self.uids:
                       type = canvas.type(uid)
                       if type in ['arc', 'oval']:
                               canvas.itemconfigure(uid, outline = SELECTCOL)
                       elif type == 'image':
                               pass
                       else:
                               canvas.itemconfigure(uid, fill = SELECTCOL)
       def draw(self, x, y, maxy, canvas, scale = 1): # Element mehrfach zeichnen
               # Ist firstcall gesetzt, werden x- und y-Position nicht skaliert, aber scale �bergeben.
               # F�r den ersten Aufruf aus einem picture
               self.uids = []
               for z in range(self.n):
                       self.uids += self.pe.draw(
                               scale * (self.x + z * self.dx + x),
                               scale * (self.y + z * self.dy + y),
                               scale * maxy, canvas, scale)
               return self.uids
       def undraw(self, canvas):
               self.pe.undrawhook()
               for uid in self.uids:
                       canvas.delete(uid)
               self.uids = []
       def minfo(self, x, y): # Leitet nur weiter
               self.pe.minfo(self.x, self.y, x, y)
       def minfo2(self, x, y):
               self.pe.minfo2(self.x, self.y, x, y)
       def dxdy(self): # Liefert Abmessungen des multiput in LaTeX-Koordinaten
               x1, y1, x2, y2 = self.pe.dxdy(self.x, self.y)
               if self.dx > 0:
                       x2 += (self.n - 1) * self.dx
               else:
                       x1 += (self.n - 1) * self.dx
               if self.dy > 0:
                       y2 += (self.n - 1) * self.dy
               else:
                       y1 += (self.n - 1) * self.dy
               return x1, y1, x2, y2
       # moveput, click2 und click3 nutzen vom mainwindow die Routinen in motionmode 0 bis 2
       def click2(self):
               self.pe.savestate()
               p = self.tkparent
               p.motionmode = 1
               p.pendingputter = self
               p.bindings(self.done, self.c23cancel)
       def click3(self):
               self.pe.savestate()
               p = self.tkparent
               p.motionmode = 2
               p.pendingputter = self
               p.bindings(self.done, self.c23cancel)
       def c23cancel(self, event):
               self.pe.cancel()
               self.cancel()
       def moveput(self):
               p = self.tkparent
               p.motionmode = 0
               self.cancelx = self.x
               self.cancely = self.y
               p.bindings(self.done, self.mpcancel)
               p.pendingputter = self
       def mpcancel(self, event):
               self.x = self.cancelx
               self.y = self.cancely
               self.cancel()
       def cancel(self):
               self.tkparent.status(STATUSCANCEL[LANG])
               del self.tkparent.pendingputter
               self.tkparent.unbind()
               self.tkparent.update()
       def done(self, event):
               self.tkparent.status(STATUSDONE[LANG])
               del self.tkparent.pendingputter
               self.tkparent.unbind()
       def capply(self, event): # Eingegebene Felder lesen und fehlerpr�fen
               try:
                       self.dx = self.tkdx.get()
               except ValueError:
                       self.tkdx.set(self.dx)
               try:
                       self.dy = self.tkdy.get()
               except ValueError:
                       self.tkdy.set(self.dy)
               try:
                       if self.tkn.get() < 1:
                               raise ValueError
                       self.n = self.tkn.get()
                       if self.n == 1:
                               self.tkBMultiAuf['state'] = DISABLED
                       else:
                               self.tkBMultiAuf['state'] = NORMAL
               except ValueError:
                       self.tkn.set(self.n)
               self.tkcallredraw()
       def change(self, frame, parent):
               # Multifelder anzeigen
               self.tkframe = frame # Speichern f�r die Aufl�sebefehle
               self.tkparent = parent
               self.tkcallredraw = parent.update
               Label(frame, text = '\[multi]put').grid(row = 0, column = 0, columnspan = 2)
               Label(frame, text = MULTIDX[LANG]).grid(row = 1, column = 0)
               Label(frame, text = MULTIDY[LANG]).grid(row = 2, column = 0)
               Label(frame, text = MULTIN[LANG]).grid(row = 3, column = 0)

               self.tkdx = DoubleVar()
               self.tkdy = DoubleVar()
               self.tkn = IntVar()
               self.tkdx.set(self.dx)
               self.tkdy.set(self.dy)
               self.tkn.set(self.n)
               e1 = Entry(frame, textvariable = self.tkdx)
               e1.grid(sticky = W + E, row = 1, column = 1)
               e2 = Entry(frame, textvariable = self.tkdy)
               e2.grid(sticky = W + E, row = 2, column = 1)
               e3 = Entry(frame, textvariable = self.tkn)
               e3.grid(sticky = W + E, row = 3, column = 1)
               e1.bind("<Return>", self.capply)
               e2.bind("<Return>", self.capply)
               e3.bind("<Return>", self.capply)

               # Punktfelder nach picel anzeigen
               Button(frame, text = MULTISETROOT[LANG], command = self.moveput).grid(row = 4, columnspan = 2, sticky = E + W)
               if self.pe.clickdepth > 1:
                       Button(frame, text = MULTISETP2[LANG], command = self.click2).grid(row = 5, columnspan = 2, sticky = E + W)
               if self.pe.clickdepth > 2:
                       Button(frame, text = MULTISETP3[LANG], command = self.click3).grid(row = 6, columnspan = 2, sticky = E + W)

               self.tkBMultiAuf = Button(frame, text = MULTIUNMULTI[LANG], state = [NORMAL, DISABLED][self.n == 1], command = self.cbMultiAuf)
               self.tkBMultiAuf.grid(row = 7, column = 0, sticky = E + W)
               if self.pe.__class__ == picture:
                       Button(frame, text = MULTIUNPIC[LANG], command = self.cbPicAuf).grid(row = 7, column = 1, sticky = E + W)
               # picel auffordern, sein Konfigurationsfenster anzuzeigen
               peframe = Frame(frame, width = 100, height = 100, borderwidth = 2, relief = RIDGE)
               peframe.grid(sticky = N + S + W + E, columnspan = 2)

               self.pe.change(peframe, self.tkcallredraw)
       def copy(self):
               return multiputter(self.x, self.y, self.dx, self.dy, self.n, self.pe.copy())
       def cbMultiAuf(self): # Multiputter in viele putter aufl�sen
               # Referenz zum enthaltenden Bild holen
               putters = self.tkparent.pic.pc.putters # pic ist eigentlich nicht n�tig
               # Neue Putter f�r n - 1 erstellen und sich selbst zur�ckstufen
               for z in range(1, self.n):
                       pecopy = self.pe.copy()
                       newputter = multiputter(self.x + z * self.dx, self.y + z * self.dy, 0, 0, 1, pecopy)
                       putters.append(newputter)
               self.dx = 0
               self.dy = 0
               self.n = 1
               # �nderungen anzeigen
               self.tkparent.update()
               self.tkframe.destroy()
       def cbPicAuf(self):
               self.undraw(self.tkparent.canvas)
               # Referenz zum enthaltenden Bild holen
               putters = self.tkparent.pic.pc.putters # pic ist eigentlich nicht n�tig

               # Alle Bildelemente aus Klasse duplizieren und direkt in die enthaltende Klasse einf�gen
               for z in range(self.n):
                       for putter in self.pe.pc.putters:
                               pecopy = putter.pe.copy()
                               pecopy.doscale(self.pe.scale)
                               newputter = multiputter(self.pe.scale * (putter.x + self.dx * z) + self.x,
                                                       self.pe.scale * (putter.y + self.dy * z) + self.y,
                                                       self.pe.scale * putter.dx,
                                                       self.pe.scale * putter.dy,
                                                       putter.n, pecopy)
                               putters.append(newputter)
               del putters[putters.index(self)] # Die Instanz entfernt den letzten Bezug auf sich selbst.
               # �nderungen anzeigen
               self.tkparent.update()
               if self.__dict__.has_key("tkframe"):
                       self.tkframe.destroy()
       def notifydone(self, parent):
               self.tkparent = parent
               if self.pe.__class__ == picture and self.pe.pc.name == CLIPBOARDNAME:
                       self.cbPicAuf()
       def notifyunpickle(self, parent): # Dies wird f�r jedes frisch entpickelte Element aufgerufen.
               if self.pe.__class__ == eps:
                       self.pe.loadfile(self.pe.filename, parent)
       def notifypickle(self, parent): # Dies wird f�r jedes frisch gepickelte Element aufgerufen.
               if self.pe.__class__ == eps:
                       self.pe.loadfile(self.pe.filename, parent)
                       parent.update()

class sizechanger: # �ndert dazu noch den Namen
       def __init__(self, root, sizex, sizey, name, names, changedcb):
               # Alte Werte; names: Liste der benutzten Namen; changedcb(x, y, name)
               self.sizex = sizex
               self.sizey = sizey
               self.name = name
               self.names = names
               self.changedcb = changedcb

               # Fenster erstellen
               self.tl = Toplevel(root)

               self.tkx = DoubleVar()
               self.tky = DoubleVar()
               self.tkname = StringVar()
               self.tkx.set(sizex)
               self.tky.set(sizey)
               self.tkname.set(name)
               Label(self.tl, text = SIZECHANGEX[LANG]).grid(row = 0, column = 0)
               Label(self.tl, text = SIZECHANGEY[LANG]).grid(row = 1, column = 0)
               Label(self.tl, text = SIZECHANGENAME[LANG]).grid(row = 2, column = 0)
               Entry(self.tl, textvariable = self.tkx).grid(row = 0, column = 1, sticky = E + W)
               Entry(self.tl, textvariable = self.tky).grid(row = 1, column = 1, sticky = E + W)
               Entry(self.tl, textvariable = self.tkname).grid(row = 2, column = 1, sticky = E + W)
               Button(self.tl, command = self.apply, text = SIZECHANGEOK[LANG], state = ACTIVE).grid(row = 3, column = 0, columnspan = 2)
               self.tl.bind("<Return>", self.apply)
       def apply(self, event = None):
               xgot, ygot, namegot = self.tkx.get(), self.tky.get(), self.tkname.get()
               if xgot < 0:
                       xgot = self.sizex
               if ygot < 0:
                       ygot = self.sizey
               if namegot in self.names:
                       namegot = self.name
               self.changedcb(xgot, ygot, namegot)
               self.tl.destroy()

rootpc = pictureclass(NEWPIC[0], NEWPIC[1], NEWPIC[2])

win = mainwindow(rootpc)
win.parts.append(rootpc)

win.update()

win.status(PROGNAME)

win.root.mainloop()