fixed another mem leak
[swftools.git] / wx / gui / main.py
1 #!/usr/bin/env python
2 # -*- coding: UTF-8 -*-
3 #
4 # gpdf2swf.py
5 # graphical user interface for pdf2swf
6 #
7 # Part of the swftools package.
8
9 # Copyright (c) 2008,2009 Matthias Kramm <kramm@quiss.org> 
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
24
25 from __future__ import division
26 import os
27 import wx
28 from wx.lib.pubsub import Publisher
29 from wx.lib.embeddedimage import PyEmbeddedImage
30 import thread
31 import time
32
33 ICON_SIZE = 64
34
35 ID_INVERT_SELECTION = wx.NewId()
36 ID_SELECT_ODD = wx.NewId()
37 ID_SELECT_EVEN = wx.NewId()
38
39 # TODO: move into images.py
40 blank = PyEmbeddedImage(
41     "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAZiS0dE"
42     "AP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kHDAscKjCK/4UAAABo"
43     "SURBVHja7dABAcBAEAIgXf/Ofo8dRKDblqPa5stxAgQIECBAgAABAgQIECBAgAABAgQIECBA"
44     "gAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEPALTbLLAQ8OIAV9"
45     "8WNeKwAAAABJRU5ErkJggg==")
46 getblankData = blank.GetData
47 getblankImage = blank.GetImage
48 getblankBitmap = blank.GetBitmap
49
50 stop_width=8
51 stop_height=8
52 stop_data="""\xd5\x07\x07\xd5\x08\x08\xd5\x09\x09\xd4\x0a\x0a\xd4\x0a\x0a\xd5\x09\x09\xd5\x08\x08\xd5\x07\x07\xd5\x08\x08\xd4\x12\x12\xd4<<\xd1--\xd1,-\xd4;;\xd5\x12\x12\xd6\x08\x08\xd5\x0a\x0a\xd2@@\xd4\xc6\xc7\xd5kk\xd2gi\xd2\xc4\xc6\xd4<<\xd6\x0a\x0a\xd4\x0c\x0c\xcb66\xd6gi\xe3\xeb\xed\xe1\xeb\xed\xd4bc\xd3++\xd5\x0c\x0c\xd4\x0b\x0b\xd0--\xd3hi\xe2\xeb\xed\xe2\xed\xee\xd5cd\xd3++\xd5\x0c\x0c\xd5\x09\x09\xd4:;\xd3\xc5\xc7\xd3ij\xd3jj\xd3\xc5\xc7\xd4<<\xd6\x0a\x0a\xd5\x08\x08\xd4\x12\x12\xd5<=\xd2..\xd1,,\xd4;;\xd5\x12\x12\xd6\x08\x08\xd5\x07\x07\xd6\x08\x08\xd6\x0a\x0a\xd5\x0b\x0b\xd4\x0b\x0b\xd5\x09\x09\xd5\x08\x08\xd5\x07\x07"""
53
54
55 class _AppendThumbnailThread:
56     def __init__(self, win, thumbs):
57         self.__win = win
58         self.__thumbs = thumbs
59
60     def Start(self):
61         self.__keep_running = self.running = True
62         thread.start_new_thread(self.Run, ())
63
64     def IsRunning(self):
65         return self.running
66
67     def Stop(self):
68         self.__keep_running = False
69
70     def Run(self):
71         thumbs = self.__thumbs
72         different_sizes = False
73         for pos, thumb in enumerate(thumbs):
74             if pos == 0:
75                 width, height = thumb.width, thumb.height
76             else:
77                 if abs(width - thumb.width) > 2 or \
78                    abs(height - thumb.height) > 2:
79                        different_sizes = True
80
81             wx.CallAfter(self.__win.AppendThumbnail, pos,
82                          thumb.asImage(ICON_SIZE, ICON_SIZE))
83             wx.CallAfter(Publisher.sendMessage, "THUMBNAIL_ADDED",
84                                                 {'pagenr':pos+1,})
85             time.sleep(.05)
86             if not self.__keep_running:
87                 break
88
89         else:
90             if different_sizes:
91                 wx.CallAfter(Publisher.sendMessage, "DIFF_SIZES")
92                 time.sleep(.10)
93         wx.CallAfter(Publisher.sendMessage, "THUMBNAIL_DONE")
94         time.sleep(.10)
95
96         self.running = False
97
98 class FileDropTarget(wx.FileDropTarget):
99     def __init__(self, window):
100         wx.FileDropTarget.__init__(self)
101         self.window = window
102
103     def OnDropFiles(self, x, y, filenames):
104         if len(filenames) == 1:
105             Publisher.sendMessage("FILE_DROPED",
106                                    {'filename': filenames[0]}
107                                  )
108         else:
109             Publisher.sendMessage("FILES_DROPED",
110                                    {'filenames': filenames}
111                                  )
112
113 class PagePreviewWindow(wx.ScrolledWindow):
114     def __init__(self, parent):
115         wx.ScrolledWindow.__init__(self, parent)
116         self.SetBackgroundColour('grey')
117         self.SetScrollRate(20, 20)
118
119         self.__buffer = wx.EmptyBitmap(1, 1)
120
121         self.Bind(wx.EVT_PAINT, self.__OnPaint)
122
123     def DisplayPage(self, page):
124         thread.start_new_thread(self.__DisplayPageThread, (page,))
125
126     def __OnPaint(self, event):
127         dc = wx.BufferedPaintDC(self, self.__buffer, wx.BUFFER_VIRTUAL_AREA)
128
129     def __DisplayPage(self, w, h, page):
130         self.SetVirtualSize((w, h))
131         self.__buffer = wx.EmptyBitmap(w+2, h+2)
132         dc = wx.BufferedDC(None, self.__buffer)
133         dc.Clear()
134         dc.DrawRectangle(0, 0, w+2, h+2)
135         dc.DrawBitmap(wx.BitmapFromBuffer(w, h, page), 1, 1, True)
136         self.Refresh()
137
138     def __DisplayPageThread(self, page):
139         w = page['width']
140         h = page['height']
141         time.sleep(.02)
142         page = page["page"].asImage(w, h)
143         wx.CallAfter(self.__DisplayPage, w, h, page)
144
145
146 class PageListCtrl(wx.ListView):
147     def __init__(self, parent):
148         wx.ListView.__init__(self, parent, style=wx.LC_ICON)
149         self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)
150
151     def OnContextMenu(self, event):
152         menu = wx.Menu()
153         menu.Append(wx.ID_SELECTALL, u"Select All\tCTRL-A")
154         menu.AppendSeparator()
155         menu.Append(wx.ID_SAVE, u"Save SWF (all pages)\tCTRL-W")
156         menu.Append(wx.ID_SAVEAS, u"Save SWF (selected pages)\tCTRL-S")
157         self.PopupMenu(menu)
158         menu.Destroy()
159
160     def DisplayEmptyThumbnails(self, pages):
161         self.DeleteAllItems()
162         self.imglist = wx.ImageList(ICON_SIZE, ICON_SIZE, mask=True)
163         self.AssignImageList(self.imglist, wx.IMAGE_LIST_NORMAL)
164         bmp = getblankBitmap()
165         for pos in range(pages):
166             self.imglist.Add(bmp)
167             self.InsertImageStringItem(pos, u"Page %s" % (pos+1), pos)
168
169     def DisplayThumbnails(self, thumbs):
170         t = _AppendThumbnailThread(self, thumbs)
171         t.Start()
172         return t
173
174     def AppendThumbnail(self, pos, thumb):
175         bmp = wx.BitmapFromBuffer(ICON_SIZE, ICON_SIZE, thumb)
176         self.imglist.Replace(pos, bmp)
177         if pos == 0:
178             wx.CallAfter(self.Select, 0)
179         self.Refresh()
180
181
182 class StatusBar(wx.StatusBar):
183     def __init__(self, parent):
184         wx.StatusBar.__init__(self, parent, -1)
185
186         self.sizeChanged = False
187         self.Bind(wx.EVT_SIZE, self.OnSize)
188         self.Bind(wx.EVT_IDLE, self.OnIdle)
189
190         self.gauge = wx.Gauge(self)
191
192         bmp = wx.BitmapFromImage(wx.ImageFromData(stop_width,stop_height,stop_data))
193         self.btn_cancel = wx.BitmapButton(self, bitmap=bmp,
194                                        style = wx.NO_BORDER)
195         self.gauge.Hide()
196         self.btn_cancel.Hide()
197         
198         self.Reposition()
199
200     def SetGaugeValue(self, value):
201         if value == 0:
202             self.gauge.Hide()
203             self.btn_cancel.Hide()
204         self.gauge.SetValue(value)
205
206     def SetGaugeRange(self, pages):
207         self.gauge.Show()
208         self.btn_cancel.Show()
209         self.gauge.SetRange(pages)
210
211     def OnSize(self, evt):
212         self.Reposition()
213         self.sizeChanged = True
214
215     def OnIdle(self, evt):
216         if self.sizeChanged:
217             self.Reposition()
218
219     def Reposition(self):
220         rect = self.GetFieldRect(0)
221         of = rect.width // 1.5
222         self.gauge.SetPosition((rect.x+of , rect.y+2))
223         self.gauge.SetSize((rect.width-of-24, rect.height-4))
224         self.btn_cancel.SetPosition((rect.width-22, rect.y+1))
225         self.btn_cancel.SetSize((22, rect.height-2))
226         self.sizeChanged = False
227
228
229 class PdfFrame(wx.Frame):
230     def __init__(self):
231         wx.Frame.__init__(self, None, size=(750,550), title=u"gpdf2swf")
232
233         icon = self.__MakeIcon(os.path.join("images", "pdf2swf_gui.ico"))
234         self.SetIcon(icon)
235         self.__CreateMenu()
236         self.__CreateToolbar()
237         self.statusbar = StatusBar(self)
238         self.SetStatusBar(self.statusbar)
239
240         dt = FileDropTarget(self)
241         self.SetDropTarget(dt)
242
243         hsplit = wx.SplitterWindow(self, style=wx.SP_3D|wx.SP_LIVE_UPDATE)
244         self.page_list = PageListCtrl(hsplit)
245         self.page_preview = PagePreviewWindow(hsplit)
246         hsplit.SplitVertically(self.page_list, self.page_preview,
247                                sashPosition=ICON_SIZE*2)
248         hsplit.SetMinimumPaneSize(ICON_SIZE*2)
249
250     def __MakeIcon(self, filename):
251         # TODO: Probably include the icon on a .py file
252         img = wx.Bitmap(filename).ConvertToImage()
253         if "wxMSW" in wx.PlatformInfo:
254             img = img.Scale(16, 16)
255         #elif "wxGTK" in wx.PlatformInfo:
256         #    img = img.Scale(22, 22)
257         # wxMac and wxGTK???? can be any size up to 128x128,
258         # so leave the source img alone....
259         icon = wx.IconFromBitmap(img.ConvertToBitmap())
260         return icon
261
262     def __CreateMenu(self):
263         menubar = wx.MenuBar()
264
265         menu = wx.Menu()
266         menu.Append(wx.ID_OPEN, u"Open PDF\tCTRL-O", u"Open a PDF document")
267         menu.AppendSeparator()
268         menu.Append(wx.ID_SAVE, u"Save SWF (all pages)\tCTRL-W",
269                                 u"Save all pages")
270         menu.Append(wx.ID_SAVEAS, u"Save SWF (selected pages)\tCTRL-S",
271                                   u"Save selected pages")
272         menu.AppendSeparator()
273         menu.Append(wx.ID_EXIT, u"Exit\tCTRL-Q")
274         menubar.Append(menu, u"&File")
275
276         self.filehistory = wx.FileHistory()
277         self.filehistory.UseMenu(menu)
278
279         menu = wx.Menu()
280         menu.Append(wx.ID_SELECTALL, u"Select All\tCTRL-A",
281                                      u"Select all pages")
282         menu.Append(ID_INVERT_SELECTION, u"Invert Selection",
283                                          u"Invert current selection")
284         menu.Append(ID_SELECT_ODD, u"Select Odd",
285                                    u"Select odd pages")
286         menu.Append(ID_SELECT_EVEN, u"Select Even",
287                                     u"Select even pages")
288         menu.AppendSeparator()
289         menu.Append(wx.ID_PREFERENCES, u"Options\tCTRL-R",
290                                        u"Show options dialog")
291         menubar.Append(menu, u"&Edit")
292
293         menu = wx.Menu()
294         menu.Append(wx.ID_ZOOM_IN, u"Zoom In\tCTRL-+")
295         menu.Append(wx.ID_ZOOM_OUT, u"Zoom Out\tCTRL--")
296         menu.Append(wx.ID_ZOOM_100, u"Normal Size\tCTRL-0")
297         menu.Append(wx.ID_ZOOM_FIT, u"Fit\tCTRL-1")
298         menubar.Append(menu, u"&View")
299
300         menu = wx.Menu()
301         menu.Append(wx.ID_ABOUT, u"About")
302         menubar.Append(menu, u"&Help")
303         self.SetMenuBar(menubar)
304
305     def __CreateToolbar(self):
306         tsize = (16,16)
307         wxart = wx.ArtProvider.GetBitmap
308         self.toolbar = self.CreateToolBar(wx.TB_HORIZONTAL |
309                                           wx.NO_BORDER | wx.TB_FLAT)
310         self.toolbar.AddSimpleTool(wx.ID_OPEN,
311                                    wxart(wx.ART_FILE_OPEN,
312                                          wx.ART_TOOLBAR, tsize),
313                                    u"Open")
314         self.toolbar.AddSimpleTool(wx.ID_SAVE,
315                                    wxart(wx.ART_FILE_SAVE,
316                                          wx.ART_TOOLBAR, tsize),
317                                    u"Save SWF (all pages)")
318         self.toolbar.AddSimpleTool(wx.ID_PREFERENCES,
319                                    wxart(wx.ART_LIST_VIEW,
320                                          wx.ART_TOOLBAR, tsize),
321                                    u"Options")
322         self.toolbar.Realize()
323