PM: BIPUv2 for version detection, command.
[re-lab:tools.git] / oletoy / cmd.py
1 # Copyright (C) 2007-2013       Valek Filippov (frob@df.ru)
2 #
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of version 3 or later of the GNU General Public
5 # License as published by the Free Software Foundation.
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9 # GNU General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
14 # USA
15 #
16
17 import sys,struct,os
18 import tree,gtk,cairo,zlib
19 import gobject
20 import difflib
21 import ole,escher,rx2,cdr,icc,mf,pict,chdraw,yep,cvx,pm6
22 from utils import *
23 from os.path import expanduser
24
25 try:
26         import gtksourceview2
27         usegtksv2 = True
28 except:
29         usegtksv2 = False
30
31 cdrloda = {0xa:"Outl ID",0x14:"Fild ID",0x1e:"Coords",0xc8:"Stlt ID",
32                                         0x2af8:"Polygon",0x3e8:"Name",0x2efe:"Rotation",0x7d0:"Palette",
33                                         0x1f40:"Lens",0x1f45:"Container"}
34
35
36 class SelectWindow(gtk.Window):
37         def __init__(self, mainapp, parent=None):
38                 gtk.Window.__init__(self)
39                 self.mainapp = mainapp
40                 self.set_title("OLE Toy: Select data to compare")
41                 
42                 table = gtk.Table(3, 8, False)
43
44                 left_lbl = gtk.Label("Left Panel")
45                 right_lbl = gtk.Label("Right Panel")
46                 
47                 tab_lbl = gtk.Label("File:")
48                 tab_lbl.set_alignment(xalign=0.0, yalign=0.5)
49                 self.ltab_cb = gtk.ComboBoxEntry()
50                 self.rtab_cb = gtk.ComboBoxEntry()
51                 self.ltab_cb.child.set_can_focus(False)
52                 self.rtab_cb.child.set_can_focus(False)
53
54                 self.tab_cbm = gtk.ListStore(gobject.TYPE_STRING)
55                 for i in self.mainapp.das:
56                         li = self.tab_cbm.append()
57                         self.tab_cbm.set_value(li,0,"%s (tab %s)"%(self.mainapp.das[i].pname,i))
58
59                 pathcb_lbl = gtk.Label("Record:")
60                 pathcb_lbl.set_alignment(xalign=0.0, yalign=0.5)
61                 self.lpath_cb = gtk.ComboBoxEntry()
62                 self.rpath_cb = gtk.ComboBoxEntry()
63                 self.lpath_cb.child.set_can_focus(False)
64                 self.rpath_cb.child.set_can_focus(False)
65
66
67                 path_lbl = gtk.Label("Path:")
68                 path_lbl.set_alignment(xalign=0.0, yalign=0.5)
69                 self.lpath_entry = gtk.Entry()
70                 self.rpath_entry = gtk.Entry()
71
72                 soff_lbl = gtk.Label("Start offset:")
73                 soff_lbl.set_alignment(xalign=0.0, yalign=0.5)
74                 self.lsoff_spb = gtk.SpinButton()
75                 self.rsoff_spb = gtk.SpinButton()
76
77                 eoff_lbl = gtk.Label("End offset:")
78                 eoff_lbl.set_alignment(xalign=0.0, yalign=0.5)
79                 self.leoff_spb = gtk.SpinButton()
80                 self.reoff_spb = gtk.SpinButton()
81
82                 len_lbl = gtk.Label("Length:")
83                 len_lbl.set_alignment(xalign=0.0, yalign=0.5)
84                 self.llen_spb = gtk.SpinButton()
85                 self.rlen_spb = gtk.SpinButton()
86
87                 table.attach(left_lbl,
88                         1, 2, 0, 1, gtk.EXPAND | gtk.FILL, 0, 0, 0);
89                 table.attach(right_lbl,
90                         2, 3, 0, 1, gtk.EXPAND | gtk.FILL, 0, 0, 0);
91
92                 table.attach(tab_lbl,
93                         0, 1, 1, 2, gtk.FILL, 0, 0, 0);
94                 table.attach(self.ltab_cb,
95                         1, 2, 1, 2, gtk.EXPAND | gtk.FILL, 0, 0, 0);
96                 table.attach(self.rtab_cb,
97                         2, 3, 1, 2, gtk.EXPAND | gtk.FILL, 0, 0, 0);
98
99                 table.attach(pathcb_lbl,
100                         0, 1, 2, 3, gtk.FILL, 0, 0, 0);
101                 table.attach(self.lpath_cb,
102                         1, 2, 2, 3, gtk.EXPAND | gtk.FILL, 0, 0, 0);
103                 table.attach(self.rpath_cb,
104                         2, 3, 2, 3, gtk.EXPAND | gtk.FILL, 0, 0, 0);
105
106                 table.attach(path_lbl,
107                         0, 1, 3, 4, gtk.FILL, 0, 0, 0);
108                 table.attach(self.lpath_entry,
109                         1, 2, 3, 4, gtk.EXPAND | gtk.FILL, 0, 0, 0);
110                 table.attach(self.rpath_entry,
111                         2, 3, 3, 4, gtk.EXPAND | gtk.FILL, 0, 0, 0);
112
113                 table.attach(soff_lbl,
114                         0, 1, 4, 5, gtk.FILL, 0, 0, 0);
115                 table.attach(self.lsoff_spb,
116                         1, 2, 4, 5, gtk.EXPAND | gtk.FILL, 0, 0, 0);
117                 table.attach(self.rsoff_spb,
118                         2, 3, 4, 5, gtk.EXPAND | gtk.FILL, 0, 0, 0);
119
120                 table.attach(eoff_lbl,
121                         0, 1, 5, 6, gtk.FILL, 0, 0, 0);
122                 table.attach(self.leoff_spb,
123                         1, 2, 5, 6, gtk.EXPAND | gtk.FILL, 0, 0, 0);
124                 table.attach(self.reoff_spb,
125                         2, 3, 5, 6, gtk.EXPAND | gtk.FILL, 0, 0, 0);
126
127                 table.attach(len_lbl,
128                         0, 1, 6, 7, gtk.FILL, 0, 0, 0);
129                 table.attach(self.llen_spb,
130                         1, 2, 6, 7, gtk.EXPAND | gtk.FILL, 0, 0, 0);
131                 table.attach(self.rlen_spb,
132                         2, 3, 6, 7, gtk.EXPAND | gtk.FILL, 0, 0, 0);
133
134                 ok_btn = gtk.Button("Ok")
135                 ok_btn.connect("clicked",self.ok_button_clicked)
136
137                 table.attach(ok_btn,
138                         1, 3, 7, 8, 0, 0, 0, 0);
139
140                 self.init_controls()
141                 # signals here to not react on init_controls
142                 self.ltab_cb.connect ('changed', self.changed_cb,"ltab")
143                 self.rtab_cb.connect ('changed', self.changed_cb,"rtab")
144                 self.lpath_cb.connect ('changed', self.changed_cb,"lpath")
145                 self.rpath_cb.connect ('changed', self.changed_cb,"rpath")
146                 self.add(table)
147                 self.changed = 1
148
149         def changed_cb(self,entry,cb):
150                 self.changed = 1
151                 if cb[1:] == "tab":
152                         pn = entry.get_active()
153                         doc = self.mainapp.das[pn]
154                         model = self.mainapp.das[pn].model
155                         dlen = model.get_value(model.get_iter_first(),2)
156                         if cb == "ltab":
157                                 self.lpath_cbm = model
158                                 self.lpath_cb.set_model(self.lpath_cbm)
159                                 self.lpath_cb.set_active(0)
160                                 self.lpath_entry.set_text("0")
161                                 self.lsoff_spb.set_value(0)
162                                 self.leoff_spb.set_value(dlen)
163                                 self.llen_spb.set_value(dlen)
164                         else:
165                                 self.rpath_cbm = model
166                                 self.rpath_cb.set_model(self.rpath_cbm)
167                                 self.rpath_cb.set_active(0)
168                                 self.rpath_entry.set_text("0")
169                                 self.rsoff_spb.set_value(0)
170                                 self.reoff_spb.set_value(dlen)
171                                 self.rlen_spb.set_value(dlen)
172                 elif cb[1:] == "path":
173                         model = entry.get_model()
174                         iter1 = entry.get_active_iter()
175                         txt = model.get_string_from_iter(iter1)
176                         dlen = model.get_value(iter1,2)
177                         if cb == "lpath":
178                                 self.lpath_entry.set_text(txt)
179                                 self.lsoff_spb.set_value(0)
180                                 self.leoff_spb.set_value(dlen)
181                                 self.llen_spb.set_value(dlen)
182                         else:
183                                 self.rpath_entry.set_text(txt)
184                                 self.rsoff_spb.set_value(0)
185                                 self.reoff_spb.set_value(dlen)
186                                 self.rlen_spb.set_value(dlen)
187
188         def on_dw_destroy(self,widget):
189                 self.changed = 1
190
191         def ok_button_clicked(self, button):
192                 if self.changed:
193                         self.mainapp.dw = DiffWindow(self.mainapp)
194                         self.mainapp.dw.connect("destroy", self.on_dw_destroy)
195                         m1 = self.lpath_cb.get_model()
196                         m2 = self.rpath_cb.get_model()
197                         iter1 = self.lpath_cb.get_active_iter()
198                         iter2 = self.rpath_cb.get_active_iter()
199                         self.mainapp.dw.diffdata1 = m1.get_value(iter1,3)
200                         self.mainapp.dw.diffdata2 = m2.get_value(iter2,3)
201                         self.changed = 0
202                         self.mainapp.dw.diff_test(self.mainapp.dw.diffdata1,self.mainapp.dw.diffdata2)
203                         self.mainapp.dw.show_all()
204
205         def init_controls(self):
206                 self.ltab_cb.set_model(self.tab_cbm)
207                 self.ltab_cb.set_text_column(0)
208                 self.rtab_cb.set_model(self.tab_cbm)
209                 self.rtab_cb.set_text_column(0)
210
211                 pn1 = self.mainapp.notebook.get_current_page()
212                 if pn1 != -1:
213                         pn2 = min(pn1+1,len(self.mainapp.das)-1)
214                         self.ltab_cb.set_active(pn1)
215                         self.rtab_cb.set_active(pn2)
216
217                         self.lpath_cbm = self.mainapp.das[pn1].model
218                         self.lpath_cb.set_model(self.lpath_cbm)
219                         self.lpath_cb.set_text_column(0)
220                         self.rpath_cbm = self.mainapp.das[pn2].model
221                         self.rpath_cb.set_model(self.rpath_cbm)
222                         self.rpath_cb.set_text_column(0)
223
224                         doc1 = self.mainapp.das[pn1]
225                         doc2 = self.mainapp.das[pn2]
226                         s1 = doc1.view.get_selection()
227                         m1, iter1 = s1.get_selected()
228                         if iter1 == None:
229                                 iter1 = doc1.model.get_iter_first()
230                         s2 = doc2.view.get_selection()
231                         m2, iter2 = s2.get_selected()
232                         if iter2 == None:
233                                 iter2 = doc2.model.get_iter_first()
234                         
235                         self.lpath_cb.set_active_iter(iter1)
236                         self.rpath_cb.set_active_iter(iter2)
237                         self.lpath_entry.set_text(m1.get_string_from_iter(iter1))
238                         self.rpath_entry.set_text(m2.get_string_from_iter(iter2))
239                         
240                         llen = m1.get_value(iter1,2)
241                         rlen = m2.get_value(iter2,2)
242                         lsadj = gtk.Adjustment(0, 0, llen-1, 1, 256, 0)
243                         leadj = gtk.Adjustment(llen-1, 0, llen-1, 1, 256, 0)
244                         lladj = gtk.Adjustment(llen-1, 0, llen-1, 1, 256, 0)
245                         rsadj = gtk.Adjustment(0, 0, rlen-1, 1, 256, 0)
246                         readj = gtk.Adjustment(rlen-1, 0, rlen-1, 1, 256, 0)
247                         rladj = gtk.Adjustment(rlen-1, 0, rlen-1, 1, 256, 0)
248
249                         self.lsoff_spb.set_adjustment(lsadj)
250                         self.lsoff_spb.set_value(0)
251                         self.leoff_spb.set_adjustment(leadj)
252                         self.leoff_spb.set_value(llen-1)
253                         self.llen_spb.set_adjustment(lladj)
254                         self.llen_spb.set_value(llen-1)
255
256                         self.rsoff_spb.set_adjustment(rsadj)
257                         self.rsoff_spb.set_value(0)
258                         self.reoff_spb.set_adjustment(readj)
259                         self.reoff_spb.set_value(rlen-1)
260                         self.rlen_spb.set_adjustment(rladj)
261                         self.rlen_spb.set_value(rlen-1)
262
263                         self.lpath_entry.set_sensitive(False)
264                         self.rpath_entry.set_sensitive(False)
265                         self.lsoff_spb.set_sensitive(False)
266                         self.rsoff_spb.set_sensitive(False)
267                         self.leoff_spb.set_sensitive(False)
268                         self.reoff_spb.set_sensitive(False)
269                         self.llen_spb.set_sensitive(False)
270                         self.rlen_spb.set_sensitive(False)
271
272
273 class DiffWindow(gtk.Window):
274         def __init__(self, mainapp, parent=None):
275                 self.mainapp = mainapp
276                 # Create the toplevel window
277                 gtk.Window.__init__(self)
278                 self.set_title("OLE Toy DIFF")
279
280                 s = gtk.ScrolledWindow()
281                 s.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
282                 s.set_size_request(1165,400)
283                 da = gtk.DrawingArea()
284                 da.connect('expose_event', self.draw_diff,s)
285                 s.add_with_viewport(da)
286                 self.damm = gtk.DrawingArea()
287                 self.damm.connect('expose_event', self.draw_diffmm,s)
288                 self.damm.set_size_request(42,1)
289                 va = s.get_vadjustment()
290                 va.connect('value-changed',self.on_diff_va_changed,self.damm,s)
291
292                 hbox= gtk.HBox()
293                 hbox.pack_start(self.damm,0,0,0)
294                 hbox.pack_start(s,1,1,0)
295
296                 tbox = gtk.HBox()
297                 flr = gtk.HBox()
298                 flr.set_size_request(42,1)
299                 vbl = gtk.VBox()
300                 vbr = gtk.VBox()
301                 tbox.pack_start(flr,0,0,0)
302                 tbox.pack_start(vbl,1,1,0)
303                 tbox.pack_start(vbr,1,1,0)
304                 # Create statusbar
305                 self.statusbar = gtk.HBox()
306                 self.sblabel = gtk.Label()
307                 self.sblabel.set_use_markup(True)
308                 exp_btn = gtk.Button("Export")
309                 exp_btn.set_alignment(1,0.5)
310                 exp_btn.connect("clicked",self.activate_export)
311                 self.statusbar.pack_start(self.sblabel, True,True,2)
312                 self.statusbar.pack_start(exp_btn, 0,0,0)
313
314                 vbox = gtk.VBox()
315                 vbox.pack_start(tbox,0,0,0)
316                 vbox.pack_start(hbox,1,1,1)
317                 vbox.pack_start(self.statusbar,0,0,0)
318                 self.add(vbox)
319
320                 self.diffcs = None # cached CairoSurface
321                 self.wt = None
322                 self.ht = None
323                 self.cswidth = None
324                 self.csheight = None
325                 self.draft = 1
326                 self.diffarr = []
327                 self.diffsize = None
328                 self.diffdata1 = None
329                 self.diffdata2 = None
330
331                 sw = self.mainapp.sw
332                 self.f1name = sw.ltab_cb.child.get_text()
333                 self.f2name = sw.rtab_cb.child.get_text()
334                 self.r1name = "%s (%s)"%(sw.lpath_cb.child.get_text(),sw.lpath_entry.get_text())
335                 self.r2name = "%s (%s)"%(sw.rpath_cb.child.get_text(),sw.rpath_entry.get_text())
336                 filelbll = gtk.Label(self.f1name)
337                 filelblr = gtk.Label(self.f2name)
338                 reclbll = gtk.Label(self.r1name)
339                 reclblr = gtk.Label(self.r2name)
340                 filelbll.set_alignment(xalign=0.0, yalign=0.5)
341                 filelblr.set_alignment(xalign=0.0, yalign=0.5)
342                 reclbll.set_alignment(xalign=0.0, yalign=0.5)
343                 reclblr.set_alignment(xalign=0.0, yalign=0.5)
344                 vbl.pack_start(filelbll,0,0,0)
345                 vbl.pack_start(reclbll,0,0,0)
346                 vbr.pack_start(filelblr,0,0,0)
347                 vbr.pack_start(reclblr,0,0,0)
348
349
350         def activate_export(self, button):
351                 fname = self.mainapp.file_open('Save',None,gtk.FILE_CHOOSER_ACTION_SAVE,"not_implemented_yet.html")
352                 if fname:
353                         f = open(fname,'w')
354                         f.write("<!DOCTYPE html><html><body>")
355                         f.write("<head>\n<meta charset='utf-8'>\n") 
356                         f.write("<style type='text/css'>\ntr.top1 td { border-top: 1px solid black; }")
357                         f.write("tr.title td { border-bottom: 3px solid black; }\n")
358                         f.write(".mid { border-left: 1px solid black; border-right: 1px solid black;}\n")
359                         f.write(".mid2 { border-right: 3px solid black; border-right-style: double}\n")
360                         f.write("</style>\n</head>\n")
361                         f.write("<table style='font-family:%s;' cellspacing=0 cellpadding=2>\n"%self.mainapp.font)
362                         f.write("<tr>")
363                         f.write("<td colspan=3>%s</td><td colspan=3>%s</td>"%(self.f1name,self.f2name))
364                         f.write("</tr>\n")
365                         f.write("<tr class='title'>")
366                         f.write("<td colspan=3>%s</td><td colspan=3>%s</td>"%(self.r1name,self.r2name))
367                         f.write("</tr>\n")
368
369                         addr = 1
370                         loff = 0
371                         roff = 0
372 #                       addr1 |Hex1| Asc1 || addr2 |Hex2| Asc2
373                         for i in self.diffarr:
374                                 ta,tb,tag = i
375                                 if tag == 'delete':
376                                         hexa = d2hex(ta, " ", 16).split("\n")
377                                         asca = d2asc(ta,16).split("\n") 
378                                         clr = "128,192,255"
379                                         clrsp="<span style='background-color: rgba(%s,0.3);'>"%clr
380                                         for j in range(len(hexa)):
381                                                 hpad = "&nbsp;"*(47-len(hexa[j]))
382                                                 apad = "&nbsp;"*(16-len(asca[j]))
383                                                 f.write("<tr>")
384                                                 f.write("<td>%06x</td><td class='mid'>%s%s</span></td><td class='mid2'>%s%s</span></td>"%(loff,clrsp,hexa[j]+hpad,clrsp,asca[j]+apad))
385                                                 f.write("<td></td><td class='mid'></td><td></td>")
386                                                 f.write("</tr>\n")
387                                                 addr += 1
388                                                 loff += len(asca[j])
389                                 if tag == 'insert':
390                                         hexb = d2hex(tb, " ", 16).split("\n")
391                                         ascb = d2asc(tb,16).split("\n") 
392                                         clr = "128,255,192"
393                                         clrsp="<span style='background-color: rgba(%s,0.3);'>"%clr
394                                         for j in range(len(hexb)):
395                                                 hpad = "&nbsp;"*(47-len(hexb[j]))
396                                                 apad = "&nbsp;"*(16-len(ascb[j]))
397                                                 f.write("<tr>")
398                                                 f.write("<td></td><td class='mid'></td><td class='mid2'></td>")
399                                                 f.write("<td>%06x</td><td class='mid'>%s%s</span></td><td>%s%s</span></td>"%(roff,clrsp,hexb[j]+hpad,clrsp,ascb[j]+apad))
400                                                 f.write("</tr>\n")
401                                                 roff += len(ascb[j])
402                                                 addr += 1
403                                 if tag == 'equal':
404                                         hexa = d2hex(ta, " ", 16).split("\n")
405                                         asca = d2asc(ta,16).split("\n") 
406                                         for j in range(len(hexa)):
407                                                 hpad = "&nbsp;"*(47-len(hexa[j]))
408                                                 apad = "&nbsp;"*(16-len(asca[j]))
409                                                 f.write("<tr>")
410                                                 f.write("<td>%06x</td><td class='mid'>%s</td><td class='mid2'>%s</td>"%(loff,hexa[j]+hpad,asca[j]+apad))
411                                                 f.write("<td>%06x</td><td class='mid'>%s</td><td>%s</td>"%(roff,hexa[j]+hpad,asca[j]+apad))
412                                                 f.write("</tr>\n")
413                                                 loff += len(asca[j])
414                                                 roff += len(asca[j])
415                                                 addr += 1
416                                 if tag == 'replace':
417                                         hexa = d2hex(ta, " ", 16).split("\n")
418                                         asca = d2asc(ta,16).split("\n") 
419                                         hexb = d2hex(tb, " ", 16).split("\n")
420                                         ascb = d2asc(tb,16).split("\n") 
421                                         clr = "255,192,128"
422                                         clrsp="<span style='background-color: rgba(%s,0.3);'>"%clr
423                                         for j in range(min(len(hexa),len(hexb))):
424                                                 hpada = "&nbsp;"*(47-len(hexa[j]))
425                                                 apada = "&nbsp;"*(16-len(asca[j]))
426                                                 hpadb = "&nbsp;"*(47-len(hexb[j]))
427                                                 apadb = "&nbsp;"*(16-len(ascb[j]))
428                                                 f.write("<tr>")
429                                                 f.write("<td>%06x</td><td class='mid'>%s%s</span></td><td class='mid2'>%s%s</span></td>"%(loff,clrsp,hexa[j]+hpada,clrsp,asca[j]+apada))
430                                                 f.write("<td>%06x</td><td class='mid'>%s%s</span></td><td>%s%s</span></td>"%(roff,clrsp,hexb[j]+hpadb,clrsp,ascb[j]+apadb))
431                                                 f.write("</tr>\n")
432                                                 loff += len(asca[j])
433                                                 roff += len(ascb[j])
434                                                 addr += 1
435                                         # print leftovers
436                                         if len(hexa) > len(hexb):
437                                                 lb = len(hexb)
438                                                 for j in range(len(hexa)-lb):
439                                                         hpada = "&nbsp;"*(47-len(hexa[j+lb]))
440                                                         apada = "&nbsp;"*(16-len(asca[j+lb]))
441                                                         f.write("<tr>")
442                                                         f.write("<td>%06x</td><td class='mid'>%s%s</span></td><td class='mid2'>%s%s</span></td>"%(loff,clrsp,hexa[j+lb]+hpada,clrsp,asca[j+lb]+apada))
443                                                         f.write("<td></td><td class='mid'></td><td></td>")
444                                                         f.write("</tr>\n")
445                                                         loff += len(asca[j+lb])
446                                                         addr += 1
447                                         elif len(hexb)>len(hexa):
448                                                 la = len(hexa)
449                                                 for j in range(len(hexb)-la):
450                                                         hpadb = "&nbsp;"*(47-len(hexb[j+la]))
451                                                         apadb = "&nbsp;"*(16-len(ascb[j+la]))
452                                                         f.write("<tr>")
453                                                         f.write("<td></td><td class='mid'></td><td class='mid2'></td>")
454                                                         f.write("<td>%06x</td><td class='mid'>%s%s</span></td><td>%s%s</span></td>"%(roff,clrsp,hexb[j+la]+hpadb,clrsp,ascb[j+la]+apadb))
455                                                         f.write("</tr>\n")
456                                                         roff += len(ascb[j+la])
457                                                         addr += 1
458
459                         f.write("<tr class='top1'><td colspan=6></td></tr>\n")
460                         f.write("</table></body></html>")
461                         f.close()
462                 else:
463                         print "Nothing to export"
464
465         def on_diff_va_changed (self,va,damm,s):
466                 self.draw_diffmm(damm,None,s)
467
468
469         def diff_test(self,data1,data2):
470                 del self.diffarr
471                 self.diffarr = []
472                 if data1 != data2:
473                         sm = difflib.SequenceMatcher(None, data1, data2, False)
474                         ta = ""
475                         tb = ""
476                         clra = 1,1,1
477                         clrb = 1,1,1
478                         for tag, i1, i2, j1, j2 in sm.get_opcodes():
479                                 if tag == 'delete':
480                                         ta = data1[i1:i2]
481                                         tb = ""
482                                 if tag == 'insert':
483                                         tb = data2[j1:j2]
484                                         ta = ""
485                                 if tag == 'equal':
486                                         ta = data1[i1:i2]
487                                         tb = ta
488                                 if tag == 'replace':
489                                         ta = data1[i1:i2]
490                                         tb = data2[j1:j2]
491                                 self.diffarr.append((ta,tb,tag))
492                 # exactly the same records
493                 else:
494                         self.diffarr.append((data1,data1,"equal"))
495
496
497
498         def draw_diffmm (self, widget, event,scrollbar):
499                 x,y,width,height = widget.allocation
500                 mctx = widget.window.cairo_create()
501                 cs = cairo.ImageSurface (cairo.FORMAT_ARGB32, width, height)
502                 ctx = cairo.Context (cs)
503                 if self.diffcs == None:
504                         return
505
506                 # to calculate how many text lines could be on the screen
507                 ctx.select_font_face(self.mainapp.font, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
508                 ctx.set_font_size(14)
509                 ctx.set_line_width(1)
510                 (xt, yt, wt, ht, dx, dy) = ctx.text_extents("o")
511                 wt = int(dx)
512                 ht = int(ht+4)
513
514                 wscale = 42./self.cswidth
515                 hscale = height*1./self.csheight
516                 if height*1./(self.diffsize*ht) > 1:
517                         hscale = 1
518                 ctx.scale(wscale,hscale)
519                 ctx.set_source_surface(self.diffcs,0,0)
520                 ctx.paint()
521
522                 if hscale != 1:
523                         va = scrollbar.get_vadjustment()
524                         ctx.set_source_rgb(0,0,0)
525                         mms = va.get_value()*height/va.get_upper()
526                         mmh = va.get_page_size()*height/va.get_upper()
527                         ctx.scale(1./wscale,1./hscale)
528                         ctx.rectangle(1.5,int(mms)+0.5,39,int(mmh))
529                         ctx.stroke()
530                         ctx.scale(wscale,hscale)
531
532                 mctx.set_source_surface(cs,0,0)
533                 mctx.paint()
534
535
536         def draw_diff (self, widget, event, scrollbar):
537                 # FIXME!
538                 # need to move diffsize & diffarr from AppMainWin to DiffWindow
539                 mctx = widget.window.cairo_create()
540                 if self.draft == 1:
541                         x,y,width,height = widget.allocation
542                         if self.csheight != None:
543                                 width = self.cswidth
544                                 height = self.csheight
545                                 self.draft = 0
546                         cs = cairo.ImageSurface (cairo.FORMAT_ARGB32, width, height)
547                         ctx = cairo.Context (cs)
548                         ctx.select_font_face(self.mainapp.font, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
549                         ctx.set_font_size(14)
550                         ctx.set_line_width(1)
551                         (xt, yt, wt, ht, dx, dy) = ctx.text_extents("o")
552                         wt = int(dx)
553                         ht = int(ht+4)
554                         
555                 # clear everything
556                         ctx.set_source_rgb(0.95,0.95,0.95)
557                         ctx.rectangle(0,0,width,height)
558                         ctx.fill()
559                         addr = 1
560                         loff = 0
561                         roff = 0
562                         for i in self.diffarr:
563                                 ta,tb,tag = i
564                                 if tag == 'delete':
565                                         hexa = d2hex(ta, " ", 16).split("\n")
566                                         asca = d2asc(ta,16).split("\n") 
567                                         r,g,b = 0.5,0.75,1
568                                         for j in range(len(hexa)):
569                                                 ctx.set_source_rgb(r,g,b)
570                                                 ctx.rectangle(wt*7,ht*(addr-1),wt*64,ht)
571                                                 ctx.fill()
572                                                 ctx.set_source_rgb(0,0,0)
573                                                 ctx.move_to(0,ht*addr)
574                                                 ctx.show_text("%06x"%loff)
575                                                 ctx.move_to(wt*7,ht*addr)
576                                                 ctx.show_text(hexa[j])
577                                                 ctx.move_to(wt*55,ht*addr)
578                                                 ctx.show_text(asca[j])
579                                                 addr += 1
580                                                 loff += len(asca[j])
581                                 if tag == 'insert':
582                                         hexb = d2hex(tb, " ", 16).split("\n")
583                                         ascb = d2asc(tb,16).split("\n") 
584                                         r,g,b = 0.5,1,0.75
585                                         for j in range(len(hexb)):
586                                                 ctx.set_source_rgb(r,g,b)
587                                                 ctx.rectangle(wt*79,ht*(addr-1),wt*64,ht)
588                                                 ctx.fill()
589                                                 ctx.set_source_rgb(0,0,0)
590                                                 ctx.move_to(wt*72,ht*addr)
591                                                 ctx.show_text("%06x"%roff)
592                                                 ctx.move_to(wt*79,ht*addr)
593                                                 ctx.show_text(hexb[j])
594                                                 ctx.move_to(wt*127,ht*addr)
595                                                 ctx.show_text(ascb[j])
596                                                 roff += len(ascb[j])
597                                                 addr += 1
598                                 if tag == 'equal':
599                                         hexa = d2hex(ta, " ", 16).split("\n")
600                                         asca = d2asc(ta,16).split("\n") 
601                                         for j in range(len(hexa)):
602                                                 ctx.set_source_rgb(0,0,0)
603                                                 ctx.move_to(0,ht*addr)
604                                                 ctx.show_text("%06x"%loff)
605                                                 ctx.move_to(wt*72,ht*addr)
606                                                 ctx.show_text("%06x"%roff)
607                                                 ctx.move_to(wt*7,ht*addr)
608                                                 ctx.show_text(hexa[j])
609                                                 ctx.move_to(wt*55,ht*addr)
610                                                 ctx.show_text(asca[j])
611                                                 ctx.move_to(wt*79,ht*addr)
612                                                 ctx.show_text(hexa[j])
613                                                 ctx.move_to(wt*127,ht*addr)
614                                                 ctx.show_text(asca[j])
615                                                 loff += len(asca[j])
616                                                 roff += len(asca[j])
617                                                 addr += 1
618                                 if tag == 'replace':
619                                         hexa = d2hex(ta, " ", 16).split("\n")
620                                         asca = d2asc(ta,16).split("\n") 
621                                         hexb = d2hex(tb, " ", 16).split("\n")
622                                         ascb = d2asc(tb,16).split("\n") 
623                                         r,g,b = 1,0.75,0.5
624                                         for j in range(min(len(hexa),len(hexb))):
625                                                 ctx.set_source_rgb(r,g,b)
626                                                 ctx.rectangle(wt*7,ht*(addr-1),wt*64,ht)
627                                                 ctx.rectangle(wt*79,ht*(addr-1),wt*64,ht)
628                                                 ctx.fill()
629                                                 ctx.set_source_rgb(0,0,0)
630                                                 ctx.move_to(0,ht*addr)
631                                                 ctx.show_text("%06x"%loff)
632                                                 ctx.move_to(wt*72,ht*addr)
633                                                 ctx.show_text("%06x"%roff)
634                                                 ctx.move_to(wt*7,ht*addr)
635                                                 ctx.show_text(hexa[j])
636                                                 ctx.move_to(wt*55,ht*addr)
637                                                 ctx.show_text(asca[j])
638                                                 ctx.move_to(wt*79,ht*addr)
639                                                 ctx.show_text(hexb[j])
640                                                 ctx.move_to(wt*127,ht*addr)
641                                                 ctx.show_text(ascb[j])
642                                                 loff += len(asca[j])
643                                                 roff += len(ascb[j])
644                                                 addr += 1
645                                         # print leftovers
646                                         if len(hexa) > len(hexb):
647                                                 lb = len(hexb)
648                                                 for j in range(len(hexa)-lb):
649                                                         ctx.set_source_rgb(r,g,b)
650                                                         ctx.rectangle(wt*7,ht*(addr-1),wt*64,ht)
651                                                         ctx.fill()
652                                                         ctx.set_source_rgb(0,0,0)
653                                                         ctx.move_to(0,ht*addr)
654                                                         ctx.show_text("%06x"%loff)
655                                                         ctx.move_to(wt*7,ht*addr)
656                                                         ctx.show_text(hexa[j+lb])
657                                                         ctx.move_to(wt*55,ht*addr)
658                                                         ctx.show_text(asca[j+lb])
659                                                         loff += len(asca[j+lb])
660                                                         addr += 1
661                                         elif len(hexb)>len(hexa):
662                                                 la = len(hexa)
663                                                 for j in range(len(hexb)-la):
664                                                         ctx.set_source_rgb(r,g,b)
665                                                         ctx.rectangle(wt*79,ht*(addr-1),wt*64,ht)
666                                                         ctx.fill()
667                                                         ctx.set_source_rgb(0,0,0)
668                                                         ctx.move_to(wt*72,ht*addr)
669                                                         ctx.show_text("%06x"%roff)
670                                                         ctx.move_to(wt*79,ht*addr)
671                                                         ctx.show_text(hexb[j+la])
672                                                         ctx.move_to(wt*127,ht*addr)
673                                                         ctx.show_text(ascb[j+la])
674                                                         roff += len(ascb[j+la])
675                                                         addr += 1
676                                                         
677                         ctx.set_source_rgb(0,0,0)
678                         ctx.move_to(int(wt*6.5)+0.5,0)
679                         ctx.line_to(int(wt*6.5)+0.5,height)
680                         ctx.move_to(int(wt*54.5)+0.5,0)
681                         ctx.line_to(int(wt*54.5)+0.5,height)
682                         ctx.move_to(int(wt*71.5)-0.5,0)
683                         ctx.line_to(int(wt*71.5)-0.5,height)
684                         ctx.move_to(int(wt*71.5)+1.5,0)
685                         ctx.line_to(int(wt*71.5)+1.5,height)
686                         ctx.move_to(int(wt*78.5)-0.5,0)
687                         ctx.line_to(int(wt*78.5)-0.5,height)
688                         ctx.move_to(int(wt*126.5)+0.5,0)
689                         ctx.line_to(int(wt*126.5)+0.5,height)
690                         ctx.move_to(int(wt*143.5)+0.5,0)
691                         ctx.line_to(int(wt*143.5)+0.5,height)
692                         ctx.stroke()
693                         self.diffsize = addr-1
694                         self.diffcs = cs
695                         self.wt = wt
696                         self.ht = ht
697                         self.cswidth = int(wt*143.5)+1
698                         self.csheight = int(ht*self.diffsize)
699                         self.draw_diff(widget,event,scrollbar)
700                         self.draw_diffmm(self.damm,event,scrollbar)
701                 else:
702                         cs = self.diffcs
703                         wt = self.wt
704                         ht = self.ht
705                 mctx.set_source_surface(cs,0,0)
706                 mctx.paint()
707                 widget.set_size_request(int(wt*143.5)+1,int(ht*self.diffsize))
708
709
710
711 class OSD_Entry(gtk.Window):
712         def __init__(self, parent, oid, mode="snippet"):
713                 gtk.Window.__init__(self,gtk.WINDOW_TOPLEVEL)
714                 self.Doc = parent
715                 self.oid = oid
716                 self.mode = mode
717                 self.set_resizable(True)
718                 self.set_modal(True)
719                 self.set_decorated(False)
720                 self.set_border_width(0)
721                 self.xs,self.ys = 0,0
722                 self.entry = gtk.Entry()
723                 self.entry.connect("key-press-event",self.entry_key_pressed)
724                 self.add(self.entry)
725
726         def entry_key_pressed(self, entry, event):
727                 # Changing Label
728                 if event.keyval == 65307: # Esc
729                         self.hide()
730                         entry.set_text("")
731                         if self.mode == "snippet" and self.Doc.OSD_txt:
732                                 self.Doc.add_snippet("noname",self.Doc.OSD_txt)
733                                 self.Doc.OSD_txt = ""
734                 elif event.keyval == 65293: # Enter
735                         self.hide()
736                         txt = entry.get_text()
737                         if self.mode == "snippet" and self.Doc.OSD_txt:
738                                 self.Doc.add_snippet(txt,self.Doc.OSD_txt)
739                                 self.Doc.OSD_txt = ""
740
741
742 class TabLabel(gtk.HBox):
743         __gsignals__ = {"close-clicked": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),}
744
745         def __init__(self, label_text):
746                 gtk.HBox.__init__(self)
747                 label = gtk.Label(label_text)
748                 image = gtk.Image()
749                 image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
750                 btn = gtk.Button()
751                 btn.set_image(image)
752                 btn.set_relief(gtk.RELIEF_NONE)
753                 btn.set_focus_on_click(False)
754                 btn.connect("clicked",self.tab_button_clicked)
755                 self.eb = gtk.EventBox()
756                 self.eb.add(label)
757                 self.pack_start(self.eb,1,1,0)
758                 self.pack_start(btn,0,0,0)
759                 self.show_all()
760                 #eb.modify_bg(gtk.STATE_NORMAL,self.get_colormap().alloc_color("green"))
761
762         def change_text(self,text):
763                 self.eb.get_children()[0].set_text(text)
764
765         def get_label_text (self):
766                 return self.eb.get_children()[0].get_text()
767
768         def tab_button_clicked(self, button):
769                 self.emit("close-clicked")
770
771         def on_tab_close_clicked(self, tab_label, notebook, tab_widget, arr, tabtype):
772                 """ Callback for the "close-clicked" emitted by custom TabLabel widget. """
773                 # FROB: need to ask for confirmation of file/page removal
774                 pn = notebook.page_num(tab_widget)
775                 if pn != -1:
776                         del arr[pn]
777                         notebook.remove_page(pn)
778                 else: #if len(arr) == 0:
779                         if tabtype == "doc":
780                                 gtk.main_quit()
781                 if pn < len(arr):  ## not the last page
782                         for i in range(pn,len(arr)):
783                                 arr[i] = arr[i+1]
784                         del arr[len(arr)-1]
785
786
787
788 def make_cli_view(cli):
789         model = gtk.TreeStore(
790         gobject.TYPE_STRING,    # 0 Snippet Name
791         gobject.TYPE_STRING,    # 1 Snippet text
792         )
793         view = gtk.TreeView(model)
794         view.set_reorderable(True)
795         view.columns_autosize()
796         view.set_enable_tree_lines(True)
797         cell = gtk.CellRendererText()
798         cell.set_property('family-set',True)
799         cell.set_property('font','monospace 10')
800         column0 = gtk.TreeViewColumn('SnipName', cell, text=0)
801         view.append_column(column0)
802         view.set_headers_visible(False)
803         view.set_tooltip_column(1)
804         treescr = gtk.ScrolledWindow()
805         treescr.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
806         treescr.add(view)
807         treescr.set_size_request(150,-1)
808         
809         target_entries = [('text/plain', 0, 0)]
810
811         view.enable_model_drag_source(
812                 gtk.gdk.BUTTON1_MASK, target_entries, gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_COPY)
813         view.enable_model_drag_dest(target_entries,gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_COPY)
814         view.connect('drag-data-received', cli.on_drag_data_received)
815         view.connect("key-press-event", cli.on_row_keypressed)
816
817         return view, model, treescr
818
819
820
821
822 class CliWindow(gtk.Window):
823         def __init__(self, app):
824                 gtk.Window.__init__(self)
825                 self.app = app
826                 self.scripts = {}
827                 self.snipsdir = app.snipsdir
828                 self.OSD = None
829                 self.OSD_txt = ""
830
831                 tb,tv = self.create_tbtv()
832                 s = gtk.ScrolledWindow()
833                 s.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
834                 s.set_size_request(660,400)
835                 s.add(tv)
836                 s.show_all()
837                 
838                 self.clinb = gtk.Notebook()
839                 frame = gtk.Frame("Snippets")
840                 self.snview,self.snmodel,self.scroll = make_cli_view(self)
841                 self.snview.connect("row-activated", self.on_row_activated)
842
843                 frame.add(self.scroll)
844                 self.restore_state()
845                 if len(self.scripts) == 0:
846                         tab_lbl = TabLabel("New 1")
847                         tab_lbl.connect("close-clicked", tab_lbl.on_tab_close_clicked, self.clinb, s, self.scripts, "script")
848                         self.clinb.append_page(s, tab_lbl)
849                         self.clinb.set_tab_reorderable(s, True)
850                         self.clinb.show_all()
851                         self.scripts[len(self.scripts)] = [tb,"New 1",tab_lbl]
852
853                 mainhb = gtk.HBox()
854                 mainhb.pack_start(self.clinb,1,1,0)
855                 mainhb.pack_start(frame,0,0,0)
856
857                 new_btn = gtk.Button("New")
858                 open_btn = gtk.Button("Open")
859                 save_btn = gtk.Button("Save")
860                 run_btn = gtk.Button("Run")
861
862                 hbox = gtk.HBox()
863                 hbox.pack_start(new_btn,0,0,0)
864                 hbox.pack_start(open_btn,0,0,0)
865                 hbox.pack_start(save_btn,0,0,0)
866                 hbox.pack_end(run_btn,0,0,0)
867
868                 vbox = gtk.VBox()
869                 vbox.pack_start(mainhb)
870                 vbox.pack_start(hbox,0,0,0)
871
872                 runwin = gtk.Window(gtk.WINDOW_TOPLEVEL)
873                 accgrp = gtk.AccelGroup()
874                 accgrp.connect_group(110, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE, self.on_key_press) #N
875                 accgrp.connect_group(111, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE, self.on_key_press) #O
876                 accgrp.connect_group(114, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE, self.on_key_press) #R
877                 accgrp.connect_group(115, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE, self.on_key_press) #S
878
879                 runwin.add_accel_group(accgrp)
880                 runwin.set_resizable(True)
881                 runwin.set_border_width(2)
882                 runwin.add(vbox)
883                 runwin.set_title("OLEToy CLI")
884                 runwin.connect ("destroy", self.del_runwin)
885                 run_btn.connect("button-press-event",self.cli_on_run)
886                 new_btn.connect("button-press-event",self.cli_on_new)
887                 open_btn.connect("button-press-event",self.cli_on_open)
888                 save_btn.connect("button-press-event",self.cli_on_save)
889                 runwin.show_all()
890                 tv.grab_focus()
891                 self.app.run_win = runwin
892
893
894         def add_snippet(self,name,text):
895                 iter = self.snmodel.append(None,None)
896                 self.snmodel.set(iter,0,name,1,text)
897                 self.save_state("snippets")
898
899         def save_state(self,mode="all"):
900                 # save the state, ignore 'New' files
901                 if mode in ("files","all"):
902                         try:
903                                 fs = open(os.path.join(self.snipsdir,"cli_state"),"wb")
904                                 for s in self.scripts.values():
905                                         d = s[1]
906                                         fn = s[2].get_label_text()
907                                         if d != fn:
908                                                 fs.write(os.path.join(d,fn))
909                                                 fs.write("\n")
910                                 fs.close()
911                         except:
912                                 print 'Failed to save state'
913
914                 if mode in ("snippets","all"):
915 #                       try:
916                                 fs = open(os.path.join(self.snipsdir,"cli_snippets"),"wb")
917                                 fs.write("# OLEToy snippets file #\n")
918                                 for i in  range(self.snmodel.iter_n_children(None)):
919                                         ch = self.snmodel.iter_nth_child(None,i)
920                                         n,t = self.snmodel.get_value(ch,0),self.snmodel.get_value(ch,1)
921                                         fs.write("%s %d\n"%(n,len(t)))
922                                         fs.write(t)
923                                         fs.write("\n")
924                                 fs.close()
925 #                       except:
926 #                               print 'Failed to save snippets'
927
928         def restore_state(self):
929                 # open recent files
930                 try:
931                         fs = open(os.path.join(self.snipsdir,"cli_state"),"rb")
932                         for l in fs:
933                                 try:
934                                         self.cli_on_open(None,None,l[:-1])
935                                 except:
936                                         pass # file not loaded
937                 except:
938                         print 'No saved CLI state was found'
939
940                 # load snippets
941                 try:
942                         fs = open(os.path.join(self.snipsdir,"cli_snippets"),"rb")
943                         l = fs.readline() # skip 'header'
944                         while l:
945                                 l = fs.readline()
946                                 if l:
947                                         name,tlen = l.split()
948                                         txt = fs.read(int(tlen)+1)
949                                         self.add_snippet(name,txt[:-1])
950                         fs.close()
951                 except:
952                         print 'Failed to load snippets'
953
954
955         def create_tbtv(self):
956                 # rely on gtksourceview2
957                 if usegtksv2:
958                         tb = gtksourceview2.Buffer()
959                         tv = gtksourceview2.View(tb)
960                         lm = gtksourceview2.LanguageManager()
961                         lp = lm.get_language("python")
962                         tb.set_highlight_syntax(True)
963                         tb.set_language(lp)
964                         tb.set_max_undo_levels(16)
965                         tb.set_highlight_matching_brackets(False)
966                         tv.set_show_line_marks(True)
967                         tv.set_show_line_numbers(True)
968                         tv.set_draw_spaces(True)
969                         tv.set_tab_width(4)
970                         tv.set_smart_home_end(True)
971                         tv.set_auto_indent(True)
972                         tv.set_property("draw-spaces",51) # space, tab, leading, text
973                 else:
974                         tv = gtk.TextView()
975                         tb = tv.get_buffer()
976                 return tb,tv
977
978         def del_runwin (self, action):
979                 self.app.run_win = None
980
981         def on_key_press(self,a1,a2,a3,a4):
982                 if a3 == 110:
983                         self.cli_on_new (None,None)
984                 elif a3 == 111:
985                         self.cli_on_open (None,None)
986                 elif a3 == 114:
987                         self.cli_on_run (None,None)
988                 elif a3 == 115:
989                         self.cli_on_save (None,None)
990                 return True
991
992
993         def on_row_activated(self,view,path,column):
994                 iter1 = self.snmodel.get_iter(path)
995                 txt = self.snmodel.get_value(iter1,1)
996                 pn = self.clinb.get_current_page()
997                 if pn != -1:
998                         tb = self.scripts[pn][0]
999                         tb.insert_at_cursor(txt)
1000
1001         def on_row_keypressed (self, view, event):
1002                 treeSelection = view.get_selection()
1003                 model, iter1 = treeSelection.get_selected()
1004                 if iter1:
1005                         intPath = model.get_path(iter1)
1006                         if event.keyval == 65535:
1007                                 model.remove(iter1)
1008                                 if model.get_iter_first():
1009                                         if intPath >= 0:
1010                                                 view.set_cursor(intPath)
1011                                                 view.grab_focus()
1012
1013
1014         def cli_on_open (self,wg,event,fname=None):
1015                 home = expanduser("~")
1016                 if fname is None:
1017                         fname = self.app.file_open('Open',None,os.path.join(home,".oletoy"))
1018                         if fname:
1019                                 manager = gtk.recent_manager_get_default()
1020                                 manager.add_item(fname)
1021                 if fname:
1022                         offset = 0
1023                         f = open(fname,"rb")
1024                         buf = f.read()
1025                         if buf:
1026                                 self.cli_on_new(wg,event,buf,fname)
1027                         f.close()
1028
1029
1030         def curpage_is_empty(self):
1031                 pn = self.clinb.get_current_page()
1032                 if pn != -1:
1033                         script = self.scripts[pn]
1034                         if not len(script[0].get_text(script[0].get_start_iter(),script[0].get_end_iter())):
1035                                 return True
1036                 return False
1037
1038
1039         def cli_on_save (self,wg,event):
1040                 pn = self.clinb.get_current_page()
1041                 if pn != -1:
1042                         script = self.scripts[pn]
1043                         fname = script[2].get_label_text()
1044                         home = expanduser("~")
1045                         fname = self.app.file_open('Save',None,script[1],fname)
1046                         if fname:
1047                                 txt = script[0].get_text(script[0].get_start_iter(),script[0].get_end_iter())
1048                                 f = open(fname,'wb')
1049                                 f.write(txt)
1050                                 f.close()
1051                                 manager = gtk.recent_manager_get_default()
1052                                 manager.add_item(fname)
1053                                 # need to change tab label and store fname
1054                                 dname,pname = os.path.split(fname)
1055                                 script[1] = dname
1056                                 script[2].change_text(pname)
1057                 self.save_state()
1058
1059
1060         def cli_on_new (self,wg,event,txt="",fname=""):
1061                 if fname == "":
1062                         fname = "New %s"%(len(self.scripts)+1)
1063                 dname,pname = os.path.split(fname)
1064                 if self.curpage_is_empty():
1065                         pn = self.clinb.get_current_page()
1066                         if pn != -1:
1067                                 script = self.scripts[pn]
1068                                 script[0].set_text(txt)
1069                                 script[1] = dname
1070                                 script[2].change_text(pname)
1071                 else:
1072                         tb,tv = self.create_tbtv()
1073                         s = gtk.ScrolledWindow()
1074                         s.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
1075                         s.set_size_request(660,400)
1076                         s.add(tv)
1077                         s.show_all()
1078         
1079                         tab_lbl = TabLabel(pname)
1080                         tab_lbl.connect("close-clicked", tab_lbl.on_tab_close_clicked, self.clinb, s, self.scripts, "script")
1081                         self.clinb.append_page(s, tab_lbl)
1082                         self.clinb.set_current_page(-1)
1083                         if txt != "":
1084                                 tb.set_text(txt)
1085                         self.scripts[len(self.scripts)] = [tb,dname,tab_lbl]
1086
1087
1088         def cli_on_run (self,wg,event):
1089                 pn = self.app.notebook.get_current_page()
1090                 if pn != -1:
1091                         rapp = self.app
1092                         rpage = self.app.das[pn]
1093                         treeSelection = self.app.das[pn].view.get_selection()
1094                         rmodel, riter = treeSelection.get_selected()
1095                         if riter:
1096                                 rbuf = rmodel.get_value(riter,3)
1097                         else:
1098                                 rbuf = ""
1099                         pnb = self.clinb.get_current_page()
1100                         if pnb != -1:
1101                                 script = self.scripts[pnb]
1102                                 txt = script[0].get_text(script[0].get_start_iter(),script[0].get_end_iter())
1103                                 exec(txt)
1104
1105
1106         def on_drag_data_received(self, view, drag_context, x, y, selection_data, info, eventtime):
1107                 self.OSD_txt = selection_data.get_text()
1108                 if self.OSD is None:
1109                         self.OSD = OSD_Entry(self,None,"snippet")
1110                 xv,yv,w,h = self.scroll.allocation
1111                 xw,yw = self.scroll.get_parent_window().get_position()
1112                 self.OSD.hide()
1113                 self.OSD.show_all()
1114                 self.OSD.move(x+xw+xv,y+yw+yv)
1115                 drag_context.finish(success=True, del_=False, time=eventtime)
1116
1117
1118 def arg_conv (ctype,carg):
1119         data = ''
1120         if ctype.lower() == 'x':
1121                 data = hex2d(carg)
1122         elif ctype.lower() == 'u':
1123                 data = carg.encode("utf-16")[2:]
1124         elif ctype.lower() == 'a' or ctype.lower() == 'r':
1125                 data = carg
1126         return data
1127
1128 def xlsfind (model,path,iter,(page,rowaddr,coladdr)):
1129         rname = model.get_value(iter,0)
1130         rdata = model.get_value(iter,3)
1131         if rname == 'Dimensions':
1132                 rwmin = struct.unpack('<I',rdata[4:8])[0]
1133                 rwmax = struct.unpack('<I',rdata[8:12])[0]
1134                 colmin = struct.unpack('<H',rdata[12:14])[0]
1135                 colmax = struct.unpack('<H',rdata[14:16])[0]
1136                 if rowaddr < rwmin or rowaddr > rwmax or coladdr < colmin or coladdr > colmax:
1137                         return True
1138         if rname == 'LabelSst' or rname == 'Number' or rname == 'Blank' or rname == 'Formula' or rname == 'RK':
1139                 rw = struct.unpack('<H',rdata[4:6])[0]
1140                 col = struct.unpack('<H',rdata[6:8])[0]
1141                 if rowaddr == rw and coladdr == col:
1142                         s_iter = page.search.append(None,None)
1143                         page.search.set_value(s_iter,0,model.get_string_from_iter(iter))
1144                         page.search.set_value(s_iter,2,"%s (%d)"%(rname,model.get_value(iter,2)))
1145 #                       print 'Found',model.get_string_from_iter(iter)
1146                         return True
1147
1148 def recfind (model,path,iter,(page,data)):
1149         rec = model.get_value(iter,0)
1150         # for CDR only
1151         # ?rloda#hexarg
1152         # means 'search for record "loda" with hexarg equals some arg ID
1153         # show value for this hexarg
1154         # without hexarg -- show list of all hexargs in all loda-s
1155         carg = data.find("#")
1156         arg = -1
1157         if page.type[0:3] == "CDR" and carg != -1:
1158                 rdata1 = data[:carg]
1159                 rdata2 = data[carg+1:]
1160
1161         else:
1162         # ?rRECORD:uUNITEXT
1163         # data -> "RECORD:uUNITEXT"
1164                 arg = data.find(":")
1165                 rdata1 = data
1166                 if arg != -1:
1167                         # rdata1 -> "RECORD"
1168                         # rdata2 -> "uUNITEXT" -> converted to "UNITEXT"
1169                         rdata1 = data[:arg]
1170                         ctype = data[arg+1]
1171                         rdata2 = arg_conv(ctype,data[arg+2:])
1172         pos = rec.find(rdata1)
1173         if pos != -1:
1174                 # found record, looks for value in normal case
1175                 if arg != -1:
1176                         recdata = model.get_value(iter,3)
1177                         pos2 = recdata.find(rdata2)
1178                         if pos2 == -1:
1179                                 return
1180                 if carg == -1:
1181                         s_iter = page.search.append(None,None)
1182                         page.search.set_value(s_iter,0,model.get_string_from_iter(iter))
1183                         page.search.set_value(s_iter,2,"%s (%d)"%(rec,model.get_value(iter,2)))
1184                         page.search.set_value(s_iter,3,page.search.iter_n_children(None))
1185                 # looks for args in CDR record
1186                 else:
1187                         recdata = model.get_value(iter,3)
1188                         n_args = struct.unpack('<i', recdata[4:8])[0]
1189                         s_args = struct.unpack('<i', recdata[8:0xc])[0]
1190                         s_types = struct.unpack('<i', recdata[0xc:0x10])[0]
1191
1192                         for i in range(n_args, 0, -1):
1193                                 off1 = struct.unpack('<L',recdata[s_args+i*4-4:s_args+i*4])[0]
1194                                 off2 = struct.unpack('<L',recdata[s_args+i*4:s_args+i*4+4])[0]
1195                                 argtype = struct.unpack('<L',recdata[s_types + (n_args-i)*4:s_types + (n_args-i)*4+4])[0]
1196                                 argtxt = "%04x"%argtype
1197                                 argvalue = d2hex(recdata[off1:off2])
1198                                 if rdata2 != "":
1199                                         if rdata2 == argtxt:
1200                                                 if cdrloda.has_key(argtype):
1201                                                         argtxt = cdrloda[argtype]
1202                                                 s_iter = page.search.append(None,None)
1203                                                 page.search.set_value(s_iter,0,model.get_string_from_iter(iter))
1204                                                 page.search.set_value(s_iter,2,"%s [%s %s]"%(rec,argtxt,argvalue))
1205                                 else:
1206                                         if cdrloda.has_key(argtype):
1207                                                 argtxt = cdrloda[argtype]
1208                                         s_iter = page.search.append(None,None)
1209                                         page.search.set_value(s_iter,0,model.get_string_from_iter(iter))
1210                                         page.search.set_value(s_iter,2,"%s [%s %s]"%(rec,argtxt,argvalue))
1211                                         page.search.set_value(s_iter,3,page.search.iter_n_children(None))
1212
1213 def cmdfind (model,path,iter,(page,data)):
1214         # in cdr look for leaf chunks only, avoid duplication
1215         if page.type[0:3] == "CDR" and model.iter_n_children(iter)>0:
1216                 return
1217         buf = model.get_value(iter,3)
1218         test = -1
1219         try:
1220                 while test < len(buf):
1221                         test = buf.find(data,test+1)
1222                         if test != -1:
1223                                 s_iter = page.search.append(None,None)
1224                                 page.search.set_value(s_iter,0,model.get_string_from_iter(iter))
1225                                 page.search.set_value(s_iter,1,test)
1226                                 page.search.set_value(s_iter,2,"%04x (%s)"%(test,model.get_value(iter,0)))
1227                                 page.search.set_value(s_iter,3,page.search.iter_n_children(None))
1228                         else:
1229                                 return
1230         except:
1231                 pass
1232
1233 def cmp_children (page1, model1, model2, it1, it2, carg):
1234         for i in range(model1.iter_n_children(it1)):
1235                 iter1 = model1.iter_nth_child(it1,i)
1236                 iter2 = model2.iter_nth_child(it2,i)
1237                 if model1.iter_n_children(iter1) > 0:
1238                         try:
1239                                 cmp_children (page1, model1, model2, iter1, iter2, carg)
1240                         except:
1241                                 pass
1242                 else:
1243                         data1 = model1.get_value(iter1,3)
1244                         data2 = model2.get_value(iter2,3)
1245                         if len(data1) == len(data2):
1246                                 if carg =="*":
1247                                         for j in range(len(data1)):
1248                                                 if ord(data1[j]) != ord(data2[j]):
1249                                                         s_iter = page1.search.append(None,None)
1250                                                         page1.search.set_value(s_iter,0,model1.get_string_from_iter(iter1))
1251                                                         page1.search.set_value(s_iter,1,j)
1252                                                         page1.search.set_value(s_iter,2,"%04x (%s)"%(j,model1.get_value(iter1,0)))
1253                                 else:
1254                                         for j in range(len(data1)):
1255                                                 if ord(data1[j])+carg == ord(data2[j]):
1256                                                         s_iter = page1.search.append(None,None)
1257                                                         page1.search.set_value(s_iter,0,model1.get_string_from_iter(iter1))
1258                                                         page1.search.set_value(s_iter,1,j)
1259                                                         page1.search.set_value(s_iter,2,"%04x (%s)"%(j,model1.get_value(iter1,0)))
1260                         else:
1261                                 s_iter = page1.search.append(None,None)
1262                                 page1.search.set_value(s_iter,0,model1.get_string_from_iter(iter1))
1263                                 page1.search.set_value(s_iter,1,0)
1264                                 page1.search.set_value(s_iter,2,"Size mismatch (%s)"%(model1.get_value(iter1,0)))
1265
1266 def compare (cmd, entry, page1, page2):
1267         model1 = page1.view.get_model()
1268         model2 = page2.view.get_model()
1269         page1.search = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_INT)
1270
1271         if len(cmd) > 1:
1272                 carg = int(cmd[1:])
1273         else:
1274                 carg = "*"
1275                 print "Search in progress..."
1276                 treeSelection = page1.view.get_selection()
1277                 tmp, iter1 = treeSelection.get_selected()
1278                 if iter1 != None:
1279                         p = model1.get_path(iter1)
1280                         iter2 = model2.get_iter(p)
1281                         try:
1282                                 cmp_children (page1, model1, model2, iter1, iter2, carg)
1283                                 page1.show_search("Diff %s"%carg)
1284                         except:
1285                                 print "Search failed"
1286                         return
1287
1288         for i in range(model1.iter_n_children(None)):
1289                 iter1 = model1.iter_nth_child(None,i)
1290                 iter2 = model2.iter_nth_child(None,i)
1291                 if model1.iter_n_children(iter1) > 0:
1292                         try:
1293                                 cmp_children (page1, model1, model2, iter1, iter2, carg)
1294                         except:
1295                                 pass
1296                 else:
1297                         data1 = model1.get_value(iter1,3)
1298                         data2 = model2.get_value(iter2,3)
1299                         if len(data1) == len(data2):
1300                                 if carg =="*":
1301                                         for j in range(len(data1)):
1302                                                 if ord(data1[j]) != ord(data2[j]):
1303                                                         s_iter = page1.search.append(None,None)
1304                                                         page1.search.set_value(s_iter,0,model1.get_string_from_iter(iter1))
1305                                                         page1.search.set_value(s_iter,1,j)
1306                                                         page1.search.set_value(s_iter,2,"%04x (%s)"%(j,model1.get_value(iter1,0)))
1307                                 else:
1308                                         for j in range(len(data1)):
1309                                                 if ord(data1[j])+carg == ord(data2[j]):
1310                                                         s_iter = page1.search.append(None,None)
1311                                                         page1.search.set_value(s_iter,0,model1.get_string_from_iter(iter1))
1312                                                         page1.search.set_value(s_iter,1,j)
1313                                                         page1.search.set_value(s_iter,2,"%04x (%s)"%(j,model1.get_value(iter1,0)))
1314                         else:
1315                                 s_iter = page1.search.append(None,None)
1316                                 page1.search.set_value(s_iter,0,model1.get_string_from_iter(iter1))
1317                                 page1.search.set_value(s_iter,1,0)
1318                                 page1.search.set_value(s_iter,2,"Size mismatch (%s)"%(model1.get_value(iter1,0)))
1319                                 
1320         page1.show_search("Diff %s"%carg)
1321
1322 def parse (cmd, entry, page):
1323         if cmd[0] == "$":
1324                 pos = cmd.find("@")
1325                 if pos != -1:
1326                         chtype = cmd[1:pos]
1327                         chaddr = cmd[pos+1:]
1328                 else:
1329                         chtype = cmd[1:]
1330                         chaddr = "0"
1331                 print "Command: ",chtype,chaddr
1332                 
1333                 treeSelection = page.view.get_selection()
1334                 model, iter1 = treeSelection.get_selected()
1335                 if iter1 == None:
1336                         page.view.set_cursor_on_cell(0)
1337                         treeSelection = page.view.get_selection()
1338                         model, iter1 = treeSelection.get_selected()
1339                 buf = model.get_value(iter1,3)
1340
1341                 if "ole" == chtype.lower():
1342                         if buf[int(chaddr,16):int(chaddr,16)+8] == "\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1":
1343                                 ole.ole_open (buf[int(chaddr,16):],page,iter1)
1344                         else:
1345                                 print "OLE stream not found at ",chaddr
1346                 elif "b64" == chtype.lower():
1347                         b64decode (page,buf[int(chaddr,16):],iter1)
1348                 elif "cvx" == chtype.lower():
1349                         cvx.parse (page,buf[int(chaddr,16):],iter1)
1350                 elif "esc" == chtype.lower():
1351                         escher.parse (model,buf[int(chaddr,16):],iter1)
1352                 elif "cmx" == chtype.lower():
1353                         cdr.cdr_open (buf[int(chaddr,16):],page,iter1)
1354                 elif "icc" == chtype.lower():
1355                         icc.parse (page,buf[int(chaddr,16):],iter1)
1356                 elif "cdx" == chtype.lower():
1357                         chdraw.open (page,buf[int(chaddr,16):],iter1)
1358                 elif "yep" == chtype.lower():
1359                         yep.parse (page,buf[int(chaddr,16):],iter1)
1360                 elif "yep0" == chtype.lower():
1361                         yep.parse (page,buf[int(chaddr,16):],iter1,0)
1362                         
1363                 elif "emf" == chtype.lower():
1364                         pt = page.type
1365                         page.type = "EMF"
1366                         mf.mf_open (buf[int(chaddr,16):],page,iter1)
1367                         page.type = pt
1368                 elif "pix" == chtype.lower():
1369 #                       try:
1370                                 off = int(chaddr,16)
1371                                 ntype = model.get_value(iter1,1)
1372                                 if off:
1373                                         iter2 = add_pgiter(page,"Picture","escher","Blip",buf[off:],iter1)
1374                                         model.set_value(iter2,1,("escher","odraw","Blip"))
1375                                 else:
1376                                         model.set_value(iter1,1,("escher","odraw","Blip"))
1377                                         page.hd.hv.parent.on_row_activated(page.hd.hv,model.get_path(iter1),None)
1378 #                       except:
1379 #                               print "Failed to add as a picture"
1380                 elif "dump" == chtype.lower():
1381                         dlg = gtk.FileChooserDialog('Save...', action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK,gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL))
1382                         dlg.set_local_only(True)
1383                         resp = dlg.run()
1384                         fname = dlg.get_filename()
1385                         dlg.hide()
1386                         if resp != gtk.RESPONSE_CANCEL:
1387                                 nlen = model.get_value(iter1,2)
1388                                 if chaddr != 0:
1389                                         pos = chaddr.find(":")
1390                                         if pos != -1:
1391                                                 endaddr = chaddr[pos+1:]
1392                                                 chaddr = chaddr[:pos]
1393                                                 value = model.get_value(iter1,3)[int(chaddr,16):int(endaddr,16)]
1394                                         else:
1395                                                 value = model.get_value(iter1,3)[int(chaddr,16):]
1396                                 else:
1397                                         value = model.get_value(iter1,3)[int(chaddr,16):]
1398
1399                                 if nlen != None:
1400                                         f = open(fname,'wb')
1401                                         f.write(value)
1402                                         f.close()
1403                                 else:
1404                                         print "Nothing to save"
1405                 elif "wmf" == chtype.lower() or "apwmf" == chtype.lower():
1406                         pt = page.type
1407                         page.type = chtype.upper()
1408                         mf.mf_open (buf[int(chaddr,16):],page,iter1)
1409                         page.type = pt
1410                 elif "xls" == chtype.lower():
1411                         ch2 = chaddr[1]
1412                         if ch2.isdigit():
1413                                 coladdr = ord(chaddr[0].lower()) - 97
1414                                 rowaddr = int(chaddr[1:]) - 1
1415                         else:
1416                                 coladdr = 26*(ord(chaddr[0].lower()) - 96)+ ord(chaddr[1].lower()) - 97
1417                                 rowaddr = int(chaddr[2:]) - 1
1418                         page.search = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_INT)
1419                         model.foreach(xlsfind,(page,rowaddr,coladdr))
1420                         page.show_search("XLS: cell %s"%chaddr)
1421                 elif "rx2" == chtype.lower():
1422                         newL = struct.unpack('>I', buf[int(chaddr,16)+4:int(chaddr,16)+8])[0]
1423                         rx2.parse (model,buf[int(chaddr,16):int(chaddr,16)+newL],0,iter1)
1424                 elif "dib" == chtype.lower():
1425                         iter2 = add_pgiter (page,"[BMP]","",0,dib2bmp(buf[int(chaddr,16):]),iter1)
1426                         model.set_value(iter2,1,("escher","odraw","Blip"))
1427                 elif "pct" == chtype.lower():
1428                         pict.parse (page,buf,iter1)
1429                 elif "pm6" == chtype.lower():
1430                         off = int(chaddr,16)
1431                         pm6.open (page,buf,iter1,off)
1432                 elif "zip" == chtype.lower():
1433                         try:
1434                                 print int(chaddr,16)
1435                                 output = zlib.decompress(buf[int(chaddr,16):])
1436                                 add_pgiter (page,"[Decompressed data]","",0,output,iter1)
1437                         except:
1438                                 print "Failed to decompress"
1439
1440         elif cmd[0] == "?":
1441                 ctype = cmd[1]
1442                 carg = cmd[2:]
1443                 # convert line to hex or unicode if required
1444                 data = arg_conv(ctype,carg)
1445                 model = page.view.get_model()
1446                 page.search = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_INT)
1447                 if ctype == 'r' or ctype == 'R':
1448                         model.foreach(recfind,(page,data))
1449                 else:
1450                         model.foreach(cmdfind,(page,data))
1451                 page.show_search(carg)
1452
1453