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 lib.wordwrap import wordwrap
32 from wx.lib.pubsub import Publisher
34 from document import Document
35 from gui.dialogs import (ProgressDialog, OptionsDialog, AboutDialog, InfoDialog)
36 from gui.gmain import (PdfFrame,
37 ID_INVERT_SELECTION, ID_SELECT_ODD,
39 ID_SELECT_EVEN, ID_DOC_INFO,
45 Return the standard location on this platform for application data
47 sp = wx.StandardPaths.Get()
48 return sp.GetUserDataDir()
51 if not os.path.exists(GetDataDir()):
52 os.makedirs(GetDataDir())
54 config = wx.FileConfig(
55 localFilename=os.path.join(GetDataDir(), "options"))
61 self.__doc = Document()
66 self.__progress = None
68 self.__can_save = False
69 self.__can_viewinfo = False
71 self.view = PdfFrame()
72 wx.GetApp().SetTopWindow(self.view)
73 # Call Show after the current and pending event
74 # handlers have been completed. Otherwise on MSW
75 # we see the frame been draw and after that we saw
77 wx.CallAfter(self.view.Show)
79 self.options = OptionsDialog(self.view)
80 self.__ReadConfigurationFile()
82 self.view.toolbar_preview_type.SetSelection(0)
84 Publisher.subscribe(self.OnPageChanged, "PAGE_CHANGED")
85 Publisher.subscribe(self.OnFileLoaded, "FILE_LOADED")
86 Publisher.subscribe(self.OnFileNotLoaded, "FILE_NOT_LOADED")
87 Publisher.subscribe(self.OnDiffSizes, "DIFF_SIZES")
88 Publisher.subscribe(self.OnThumbnailAdded, "THUMBNAIL_ADDED")
89 Publisher.subscribe(self.OnThumbnailDone, "THUMBNAIL_DONE")
90 Publisher.subscribe(self.OnProgressBegin, "SWF_BEGIN_SAVE")
91 Publisher.subscribe(self.OnProgressUpdate, "SWF_PAGE_SAVED")
92 Publisher.subscribe(self.OnProgressDone, "SWF_FILE_SAVED")
93 Publisher.subscribe(self.OnCombineError, "SWF_COMBINE_ERROR")
94 Publisher.subscribe(self.OnFileDroped, "FILE_DROPED")
95 Publisher.subscribe(self.OnFilesDroped, "FILES_DROPED")
96 Publisher.subscribe(self.OnPluginOnePagePerFileNotSupported,
97 "PLUGIN_ONE_PAGE_PER_FILE_NOT_SUPPORTED")
98 Publisher.subscribe(self.OnPluginError, "PLUGIN_ERROR")
100 self.view.Bind(wx.EVT_MENU, self.OnMenuOpen, id=wx.ID_OPEN)
101 self.view.Bind(wx.EVT_MENU, self.OnMenuSave, id=wx.ID_SAVE)
102 self.view.Bind(wx.EVT_MENU, self.OnMenuSaveSelected, id=wx.ID_SAVEAS)
103 self.view.Bind(wx.EVT_MENU, self.OnMenuExit, id=wx.ID_EXIT)
104 self.view.Bind(wx.EVT_MENU_RANGE, self.OnFileHistory,
105 id=wx.ID_FILE1, id2=wx.ID_FILE9)
107 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_SAVE)
108 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_SAVEAS)
110 self.view.Bind(wx.EVT_MENU, self.OnMenuSelectAll, id=wx.ID_SELECTALL)
111 self.view.Bind(wx.EVT_MENU,
112 self.OnMenuInvertSelection, id=ID_INVERT_SELECTION)
113 self.view.Bind(wx.EVT_MENU, self.OnMenuSelectOdd, id=ID_SELECT_ODD)
114 self.view.Bind(wx.EVT_MENU, self.OnMenuSelectEven, id=ID_SELECT_EVEN)
115 self.view.Bind(wx.EVT_MENU, self.OnMenuOptions, id=wx.ID_PREFERENCES)
117 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_SELECTALL)
118 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_INVERT_SELECTION)
119 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_SELECT_ODD)
120 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_SELECT_EVEN)
122 self.view.Bind(wx.EVT_MENU, self.OnAbout, id=wx.ID_ABOUT)
124 self.view.Bind(wx.EVT_MENU, self.OnZoom, id=wx.ID_ZOOM_IN)
125 self.view.Bind(wx.EVT_MENU, self.OnZoom, id=wx.ID_ZOOM_OUT)
126 self.view.Bind(wx.EVT_MENU, self.OnZoom, id=wx.ID_ZOOM_100)
127 self.view.Bind(wx.EVT_MENU, self.OnFit, id=wx.ID_ZOOM_FIT)
128 self.view.Bind(wx.EVT_MENU, self.OnShowDocInfo, id=ID_DOC_INFO)
130 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_IN)
131 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_OUT)
132 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_100)
133 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_FIT)
134 self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUIInfo, id=ID_DOC_INFO)
136 self.view.page_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelectItem)
137 self.view.Bind(wx.EVT_CLOSE, self.OnMenuExit)
139 self.view.toolbar_preview_type.Bind(wx.EVT_CHOICE,
142 # statusbar cancel thumbanails generation button
143 self.view.statusbar.btn_cancel.Bind(wx.EVT_BUTTON,
144 self.OnThumbnailCancel)
146 def OnFilesDroped(self, evt):
147 dlg = wx.MessageDialog(self.view,
148 u"You must drop only one file.",
150 style=wx.OK, pos=wx.DefaultPosition)
154 def OnFileDroped(self, message):
155 self.__Load(message.data["filename"])
157 def OnPluginOnePagePerFileNotSupported(self, message):
158 self.Message(u"Selected viewer does not support "
159 u"one page per file. ")
161 def OnPluginError(self, message):
162 self.Message(u"Error applying selected viewer")
164 def OnFileHistory(self, evt):
165 # get the file based on the menu ID
166 fileNum = evt.GetId() - wx.ID_FILE1
167 filename = self.view.filehistory.GetHistoryFile(fileNum)
169 self.__Load(filename)
171 def OnProgressBegin(self, message):
172 pages = message.data["pages"]
174 wx.PD_APP_MODAL|wx.PD_ELAPSED_TIME|
175 wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT|
178 self.__progress = ProgressDialog(u"Saving...",
179 u"Start saving SWF pages",
181 parent=self.view, style=style)
182 self.__progress.Show()
183 self.view.SetStatusText(u"Saving document...")
185 def OnProgressUpdate(self, message):
186 pagenr = message.data["pagenr"]
187 pages = message.data["pages"]
189 keep_running = self.__progress.Update(
191 u"Saving SWF page %d of %d" % (pagenr, pages)
194 if not keep_running and self.__threads.has_key("progress"):
195 self.view.SetStatusText(u"Cancelling...")
196 self.__threads.pop("progress").Stop()
199 def OnProgressDone(self, message):
200 if self.__threads.has_key("progress"): # it goes all the way?
201 self.__threads.pop("progress")
202 self.view.SetStatusText(u"SWF document saved successfully.")
204 self.view.SetStatusText(u"")
206 self.__progress.Destroy()
207 self.__progress = None
209 def OnCombineError(self, message):
210 from wx.lib.dialogs import ScrolledMessageDialog
211 ScrolledMessageDialog(self.view, message.data, u"Notice").ShowModal()
214 def OnThumbnailAdded(self, message):
215 self.view.statusbar.SetGaugeValue(message.data['pagenr'])
216 tot = self.view.page_list.GetItemCount()
217 self.view.SetStatusText(u"Generating thumbnails %s/%d" %
218 (message.data['pagenr'], tot), 0)
220 def OnThumbnailDone(self, message):
221 self.view.statusbar.SetGaugeValue(0)
222 self.view.SetStatusText(u"", 0)
223 if self.__threads.has_key("thumbnails"):
224 self.__threads.pop("thumbnails")
225 self.view.SendSizeEvent()
227 def OnThumbnailCancel(self, event):
228 if self.__threads.has_key("thumbnails"):
229 self.__threads["thumbnails"].Stop()
231 def OnSelectItem(self, event):
232 self.__doc.ChangePage(event.GetIndex() + 1)
234 def OnPreviewType(self, event):
235 filename = self.__doc.filename
237 self.__Load(filename)
239 def OnPageChanged(self, message):
240 # ignore if we have more than one item selected
241 if self.view.page_list.GetSelectedItemCount() > 1:
244 self.view.page_preview.DisplayPage(message.data)
247 name = wx.GetApp().GetAppName()
248 filename = os.path.basename(self.__doc.filename)
249 if self.__doc.title != "n/a":
250 t = "%s - %s (%s)" % (name, filename, self.__doc.title)
252 t = "%s - %s" % (name, filename)
253 self.view.SetTitle(t)
255 def OnFileLoaded(self, message):
257 self.__progress.Destroy()
258 self.__progress = None
260 self.__can_viewinfo = True
265 if self.__doc.oktocopy == 'no':
266 self.__can_save = False
267 self.view.page_list.DisplayEmptyThumbnails(0)
268 self.view.page_preview.Clear()
269 self.view.SetStatusText(u"")
271 u"This PDF disallows copying, cannot be converted."
275 #if not self.__doc.oktoprint:
276 self.view.SetStatusText(u"Document loaded successfully.")
278 self.view.page_list.DisplayEmptyThumbnails(message.data["pages"])
279 thumbs = self.__doc.GetThumbnails()
280 t = self.view.page_list.DisplayThumbnails(thumbs)
281 self.__threads["thumbnails"] = t
282 self.view.statusbar.SetGaugeRange(message.data["pages"])
285 def OnFileNotLoaded(self, message):
286 self.__can_save = False
287 self.__can_viewinfo = False
289 self.view.SetStatusText(u"")
291 u"Could not open file %s" % message.data['filename']
294 def OnDiffSizes(self, message):
295 # just let the user know- for now, we can't handle this properly
297 u"In this PDF, width or height are not the same for "
298 u"each page. This might cause problems if you export "
299 u"pages of different dimensions into the same SWF file."
302 def OnMenuOpen(self, event):
303 dlg = wx.FileDialog(self.view, u"Choose PDF File:",
304 style=wx.OPEN|wx.CHANGE_DIR,
305 wildcard = u"PDF files (*.pdf)|*.pdf|all files (*.*)|*.*")
307 if dlg.ShowModal() == wx.ID_OK:
308 filename = dlg.GetPath()
309 self.__Load(filename)
311 def OnMenuSave(self, event, pages=None):
312 defaultFile = self.__doc.lastsavefile
313 if "wxMSW" in wx.PlatformInfo:
317 self.view.SetStatusText(u"")
318 dlg = wx.FileDialog(self.view, u"Choose Save Filename:",
319 style = wx.SAVE | wx.OVERWRITE_PROMPT,
320 defaultFile=os.path.basename(defaultFile),
321 wildcard=u"SWF files (*.swf)|*.swf"
322 "|all files (%s)|%s" % (allFiles, allFiles))
325 if dlg.ShowModal() == wx.ID_OK:
326 menubar = self.view.GetMenuBar()
327 one_file_per_page = menubar.IsChecked(ID_ONE_PAGE_PER_FILE)
329 self.__threads["progress"] = self.__doc.SaveSWF(dlg.GetPath(),
333 def OnUpdateUI(self, event):
334 menubar = self.view.GetMenuBar()
335 menubar.Enable(event.GetId(), self.__can_save)
337 self.view.GetToolBar().EnableTool(event.GetId(), self.__can_save)
339 def OnUpdateUIInfo(self, event):
340 menubar = self.view.GetMenuBar()
341 menubar.Enable(event.GetId(), self.__can_viewinfo)
343 self.view.GetToolBar().EnableTool(event.GetId(), self.__can_viewinfo)
345 def OnMenuSaveSelected(self, event):
347 page = self.view.page_list.GetFirstSelected()
351 page = self.view.page_list.GetNextSelected(page)
356 self.OnMenuSave(event, pages)
358 def OnMenuExit(self, event):
359 self.view.SetStatusText(u"Cleaning up...")
361 # Stop any running thread
365 self.view.filehistory.Save(config)
367 # A little extra cleanup is required for the FileHistory control
368 del self.view.filehistory
370 # Save quality options
371 dirpath = GetDataDir()
372 data = self.options.quality_panel.pickle()
374 f = file(os.path.join(dirpath, 'quality.pkl'), 'wb')
380 # Save viewer options
382 f = file(os.path.join(dirpath, 'viewers.pkl'), 'wb')
383 data = self.options.viewers_panel.pickle()
391 def OnMenuSelectAll(self, event):
392 for i in range(0, self.view.page_list.GetItemCount()):
393 self.view.page_list.Select(i, True)
395 def OnMenuInvertSelection(self, event):
396 for i in range(0, self.view.page_list.GetItemCount()):
397 self.view.page_list.Select(i, not self.view.page_list.IsSelected(i))
399 def OnMenuSelectOdd(self, event):
400 for i in range(0, self.view.page_list.GetItemCount()):
401 self.view.page_list.Select(i, not bool(i%2))
403 def OnMenuSelectEven(self, event):
404 for i in range(0, self.view.page_list.GetItemCount()):
405 self.view.page_list.Select(i, bool(i%2))
407 def OnMenuOptions(self, event):
408 self.options.ShowModal()
410 def OnFit(self, event):
411 self.__doc.Fit(self.view.page_preview.GetClientSize())
413 def OnZoom(self, event):
419 self.__doc.Zoom(zoom[event.GetId()])
421 def OnShowDocInfo(self, event):
422 info = InfoDialog(self.view)
423 info.info.display(self.__doc)
426 def OnAbout(self, evt):
427 AboutDialog(self.view)
429 def __Load(self, filename):
430 self.__can_save = True
432 self.view.SetStatusText(u"Loading document...")
433 self.__busy = wx.BusyInfo(u"One moment please, "
434 u"opening pdf document...")
436 self.view.filehistory.AddFileToHistory(filename)
438 # I dont care if this, for some reason,
439 # give some error. I just swallow it
440 os.chdir(os.path.dirname(filename))
444 # Need to delay the file load a little bit
445 # for the BusyInfo get a change to repaint itself
446 #wx.FutureCall(150, self.__doc.Load, filename)
447 sel = self.view.toolbar_preview_type.GetSelection()
450 0 : [('bitmap', '1'), ('poly2bitmap', '0'), ('bitmapfonts', '1'),],
451 1 : [('bitmap', '0'), ('poly2bitmap', '1'), ('bitmapfonts', '0'),],
452 2 : [('bitmap', '0'), ('poly2bitmap', '0'), ('bitmapfonts', '0'),],
454 self.__doc.preview_parameters = PREV_TYPE[sel]
455 wx.CallAfter(self.__doc.Load, filename)
457 def __StopThreads(self):
458 for n, t in self.__threads.items():
464 for n, t in self.__threads.items():
465 running = running + t.IsRunning()
468 def __ReadConfigurationFile(self):
470 self.view.filehistory.Load(config)
472 dirpath = GetDataDir()
474 f = file(os.path.join(dirpath, 'quality.pkl'), 'rb')
477 data = pickle.load(f)
478 self.options.quality_panel.unpickle(data)
481 # u"Error loading quality settings. "
482 # u"They will be reset to defaults. ")
488 f = file(os.path.join(dirpath, 'viewers.pkl'), 'rb')
491 data = pickle.load(f)
492 self.options.viewers_panel.unpickle(data)
495 # u"Error loading viewers settings. "
496 # u"They will be reset to defaults. ")
502 def Message(self, message):
503 dlg = wx.MessageDialog(self.view,
505 style=wx.OK, pos=wx.DefaultPosition)