2 # -*- coding: UTF-8 -*-
5 # graphical user interface for pdf2swf
7 # Part of the swftools package.
9 # Copyright (c) 2008,2009 Matthias Kramm <kramm@quiss.org>
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.
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.
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 */
25 from __future__ import division
31 from wx.lib.wordwrap import wordwrap
32 from wx.lib.pubsub import Publisher
34 from document import Document
35 from gui import (PdfFrame, ProgressDialog, OptionsDialog, AboutDialog,
36 ID_INVERT_SELECTION, ID_SELECT_ODD, ID_SELECT_EVEN)
41 Return the standard location on this platform for application data
43 sp = wx.StandardPaths.Get()
44 return sp.GetUserDataDir()
47 if not os.path.exists(GetDataDir()):
48 os.makedirs(GetDataDir())
50 config = wx.FileConfig(
51 localFilename=os.path.join(GetDataDir(), "options"))
57 self.__doc = Document()
62 self.__progress = None
64 self.__can_save = False
66 self.view = PdfFrame()
67 wx.GetApp().SetTopWindow(self.view)
68 # Call Show after the current and pending event
69 # handlers have been completed. Otherwise on MSW
70 # we see the frame been draw and after that we saw
72 wx.CallAfter(self.view.Show)
74 self.options = OptionsDialog(self.view)
75 self.__ReadConfigurationFile()
77 Publisher.subscribe(self.OnPageChanged, "PAGE_CHANGED")
78 Publisher.subscribe(self.OnFileLoaded, "FILE_LOADED")
79 Publisher.subscribe(self.OnDiffSizes, "DIFF_SIZES")
80 Publisher.subscribe(self.OnThumbnailAdded, "THUMBNAIL_ADDED")
81 Publisher.subscribe(self.OnThumbnailDone, "THUMBNAIL_DONE")
82 Publisher.subscribe(self.OnProgressBegin, "SWF_BEGIN_SAVE")
83 Publisher.subscribe(self.OnProgressUpdate, "SWF_PAGE_SAVED")
84 Publisher.subscribe(self.OnProgressDone, "SWF_FILE_SAVED")
85 Publisher.subscribe(self.OnCombineError, "SWF_COMBINE_ERROR")
86 Publisher.subscribe(self.OnFileDroped, "FILE_DROPED")
87 Publisher.subscribe(self.OnFilesDroped, "FILES_DROPED")
89 self.view.Bind(wx.EVT_MENU, self.OnMenuOpen, id=wx.ID_OPEN)
90 self.view.Bind(wx.EVT_MENU, self.OnMenuSave, id=wx.ID_SAVE)
91 self.view.Bind(wx.EVT_MENU, self.OnMenuSaveSelected, id=wx.ID_SAVEAS)
92 self.view.Bind(wx.EVT_MENU, self.OnMenuExit, id=wx.ID_EXIT)
93 self.view.Bind(wx.EVT_MENU_RANGE, self.OnFileHistory,
94 id=wx.ID_FILE1, id2=wx.ID_FILE9)
96 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_SAVE)
97 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_SAVEAS)
99 self.view.Bind(wx.EVT_MENU, self.OnMenuSelectAll, id=wx.ID_SELECTALL)
100 self.view.Bind(wx.EVT_MENU,
101 self.OnMenuInvertSelection, id=ID_INVERT_SELECTION)
102 self.view.Bind(wx.EVT_MENU, self.OnMenuSelectOdd, id=ID_SELECT_ODD)
103 self.view.Bind(wx.EVT_MENU, self.OnMenuSelectEven, id=ID_SELECT_EVEN)
104 self.view.Bind(wx.EVT_MENU, self.OnMenuOptions, id=wx.ID_PREFERENCES)
106 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_SELECTALL)
107 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_INVERT_SELECTION)
108 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_SELECT_ODD)
109 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_SELECT_EVEN)
111 self.view.Bind(wx.EVT_MENU, self.OnAbout, id=wx.ID_ABOUT)
113 self.view.Bind(wx.EVT_MENU, self.OnZoom, id=wx.ID_ZOOM_IN)
114 self.view.Bind(wx.EVT_MENU, self.OnZoom, id=wx.ID_ZOOM_OUT)
115 self.view.Bind(wx.EVT_MENU, self.OnZoom, id=wx.ID_ZOOM_100)
116 self.view.Bind(wx.EVT_MENU, self.OnFit, id=wx.ID_ZOOM_FIT)
118 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_IN)
119 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_OUT)
120 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_100)
121 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_FIT)
123 self.view.page_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelectItem)
124 self.view.Bind(wx.EVT_CLOSE, self.OnMenuExit)
126 # statusbar cancel thumbanails generation button
127 self.view.statusbar.btn_cancel.Bind(wx.EVT_BUTTON,
128 self.OnThumbnailCancel)
130 def OnFilesDroped(self, evt):
131 dlg = wx.MessageDialog(self.view,
132 u"You must drop only one file.",
134 style=wx.OK, pos=wx.DefaultPosition)
138 def OnFileDroped(self, message):
139 self.__Load(message.data["filename"])
141 def OnFileHistory(self, evt):
142 # get the file based on the menu ID
143 fileNum = evt.GetId() - wx.ID_FILE1
144 filename = self.view.filehistory.GetHistoryFile(fileNum)
146 self.__Load(filename)
148 def OnProgressBegin(self, message):
150 self.__progress.Destroy()
151 pages = message.data["pages"]
153 wx.PD_APP_MODAL|wx.PD_ELAPSED_TIME|
154 wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT|
157 self.__progress = ProgressDialog(u"Saving...",
158 u"Start saving SWF pages",
160 parent=self.view, style=style)
161 self.__progress.Show()
162 self.view.SetStatusText(u"Saving document...")
164 def OnProgressUpdate(self, message):
165 pagenr = message.data["pagenr"]
166 pagenr0 = pagenr - 1 # 0 based
167 pages = message.data["pages"]
169 keep_running, skip = self.__progress.Update(
171 u"Saving SWF page %d of %d" % (pagenr, pages)
173 if not keep_running and self.__threads.has_key("progress"):
174 self.view.SetStatusText(u"Cancelling...")
175 self.__threads.pop("progress").Stop()
177 # Send size events to resize the progress dialog,
178 # this will allow the progress message label to resize accordingly.
179 # Here we minimize that events every 10 times.
180 if pagenr0 % 10 == 0:
181 self.__progress.SendSizeEvent()
183 def OnProgressDone(self, message):
184 self.__progress.Hide()
185 if self.__threads.has_key("progress"): # it goes all the way?
186 self.__threads.pop("progress")
187 self.view.SetStatusText(u"SWF document saved successfully.")
189 self.view.SetStatusText(u"")
191 def OnCombineError(self, message):
192 from wx.lib.dialogs import ScrolledMessageDialog
193 ScrolledMessageDialog(self.view, message.data, u"Notice").ShowModal()
196 def OnThumbnailAdded(self, message):
197 self.view.statusbar.SetGaugeValue(message.data['pagenr'])
198 tot = self.view.page_list.GetItemCount()
199 self.view.SetStatusText(u"Generating thumbnails %s/%d" %
200 (message.data['pagenr'], tot), 0)
202 def OnThumbnailDone(self, message):
203 self.view.statusbar.SetGaugeValue(0)
204 self.view.SetStatusText(u"", 0)
205 if self.__threads.has_key("thumbnails"):
206 self.__threads.pop("thumbnails")
208 def OnThumbnailCancel(self, event):
209 if self.__threads.has_key("thumbnails"):
210 self.__threads["thumbnails"].Stop()
212 def OnSelectItem(self, event):
213 self.__doc.ChangePage(event.GetIndex() + 1)
215 def OnPageChanged(self, message):
216 # ignore if we have more than one item selected
217 if self.view.page_list.GetSelectedItemCount() > 1:
220 self.view.page_preview.DisplayPage(message.data)
222 def OnFileLoaded(self, message):
224 self.__progress.Destroy()
225 self.__progress = None
227 self.view.SetStatusText(u"Document loaded successfully.")
228 self.view.page_list.DisplayEmptyThumbnails(message.data["pages"])
229 thumbs = self.__doc.GetThumbnails()
230 t = self.view.page_list.DisplayThumbnails(thumbs)
231 self.__threads["thumbnails"] = t
232 self.view.statusbar.SetGaugeRange(message.data["pages"])
235 def OnDiffSizes(self, message):
236 # just let the user know- for now, we can't handle this properly
238 u"In this PDF, width or height are not the same for "
239 u"each page. This might cause problems if you export "
240 u"pages of different dimensions into the same SWF file."
243 def OnMenuOpen(self, event):
244 dlg = wx.FileDialog(self.view, u"Choose PDF File:",
245 style = wx.FD_CHANGE_DIR|wx.FD_OPEN,
246 wildcard = u"PDF files (*.pdf)|*.pdf|all files (*.*)|*.*")
248 if dlg.ShowModal() == wx.ID_OK:
249 filename = dlg.GetPath()
250 self.__Load(filename)
252 def OnMenuSave(self, event, pages=None):
253 defaultFile = self.__doc.lastsavefile
254 allFiles = "*.*" if "wxMSW" in wx.PlatformInfo else "*"
255 self.view.SetStatusText(u"")
256 dlg = wx.FileDialog(self.view, u"Choose Save Filename:",
257 style = wx.SAVE | wx.OVERWRITE_PROMPT,
258 defaultFile=defaultFile,
259 wildcard=u"SWF files (*.swf)|*.swf"
260 "|all files (%s)|%s" % (allFiles, allFiles))
262 if dlg.ShowModal() == wx.ID_OK:
263 self.__threads["progress"] = self.__doc.SaveSWF(dlg.GetPath(),
266 def OnUpdateUI(self, event):
267 menubar = self.view.GetMenuBar()
268 menubar.Enable(event.GetId(), self.__can_save)
270 self.view.GetToolBar().EnableTool(event.GetId(), self.__can_save)
272 def OnMenuSaveSelected(self, event):
274 page = self.view.page_list.GetFirstSelected()
278 page = self.view.page_list.GetNextSelected(page)
283 self.OnMenuSave(event, pages)
285 def OnMenuExit(self, event):
286 self.view.SetStatusText(u"Cleaning up...")
288 # Stop any running thread
292 self.view.filehistory.Save(config)
294 # A little extra cleanup is required for the FileHistory control
295 del self.view.filehistory
297 # Save quality options
298 dirpath = GetDataDir()
299 data = self.options.quality_panel.pickle()
301 f = file(os.path.join(dirpath, 'quality.pkl'), 'wb')
307 # Save viewer options
309 f = file(os.path.join(dirpath, 'viewers.pkl'), 'wb')
310 data = self.options.viewers_panel.pickle()
318 def OnMenuSelectAll(self, event):
319 for i in range(0, self.view.page_list.GetItemCount()):
320 self.view.page_list.Select(i, True)
322 def OnMenuInvertSelection(self, event):
323 for i in range(0, self.view.page_list.GetItemCount()):
324 self.view.page_list.Select(i, not self.view.page_list.IsSelected(i))
326 def OnMenuSelectOdd(self, event):
327 for i in range(0, self.view.page_list.GetItemCount()):
328 self.view.page_list.Select(i, not bool(i%2))
330 def OnMenuSelectEven(self, event):
331 for i in range(0, self.view.page_list.GetItemCount()):
332 self.view.page_list.Select(i, bool(i%2))
334 def OnMenuOptions(self, event):
335 self.options.ShowModal()
337 def OnFit(self, event):
338 self.__doc.Fit(self.view.page_preview.GetClientSize())
340 def OnZoom(self, event):
346 self.__doc.Zoom(zoom[event.GetId()])
348 def OnAbout(self, evt):
349 AboutDialog(self.view)
351 def __Load(self, filename):
352 self.__can_save = True
354 self.view.SetStatusText(u"Loading document...")
355 self.__busy = wx.BusyInfo(u"One moment please, "
356 u"opening pdf document...")
358 self.view.filehistory.AddFileToHistory(filename)
360 # Need to delay the file load a little bit
361 # for the BusyInfo get a change to repaint itself
362 wx.FutureCall(150, self.__doc.Load, filename)
364 def __StopThreads(self):
365 for n, t in self.__threads.items():
371 for n, t in self.__threads.items():
372 running = running + t.IsRunning()
375 def __ReadConfigurationFile(self):
377 self.view.filehistory.Load(config)
379 dirpath = GetDataDir()
381 f = file(os.path.join(dirpath, 'quality.pkl'), 'rb')
383 data = pickle.load(f)
384 self.options.quality_panel.unpickle(data)
387 u"Error loading quality settings. "
388 u"They will be reset to defaults. ")
394 f = file(os.path.join(dirpath, 'viewers.pkl'), 'rb')
396 data = pickle.load(f)
397 self.options.viewers_panel.unpickle(data)
400 u"Error loading viewers settings. "
401 u"They will be reset to defaults. ")
407 def Message(self, message):
408 dlg = wx.MessageDialog(self.view,
410 style=wx.OK, pos=wx.DefaultPosition)