removed obsolete issues (many of them fixed with AE)
[docutils.git] / sandbox / gschwant / docfactory / factory / main.py
blob0b1d884871ab056aafb9753aad5544f4c0ec6d96
1 #!/usr/bin/env python
3 """
4 :author: Dr. Gunnar Schwant
5 :contact: g.schwant@gmx.de
6 :version: 0.3
7 """
9 import browser, images, re, sys, os, time, ConfigParser
10 from wxPython.wx import *
11 from wxPython.lib.dialogs import wxMultipleChoiceDialog, \
12 wxScrolledMessageDialog
13 from wxPython.lib.imagebrowser import ImageDialog
14 from wxPython.help import *
15 from dialogs import *
16 from controls import CustomStyledTextCtrl
17 from controls import CustomTreeCtrl
18 from controls import CustomStatusBar
19 from docutilsadapter import publish_document, get_errors, \
20 get_rest_bibl_fields, publishers, publish_document
21 from docutils.utils import relative_path
22 from urllib import quote
23 from wxPython.lib.buttons import *
26 #-------------------------------------------------------------------------
27 # global variables
28 #-------------------------------------------------------------------------
30 NAME = 'DocFactory'
32 try:
33 factory_path = os.path.dirname(__file__)
34 except:
35 factory_path = os.path.abspath(sys.path[0])
37 if not os.path.isdir(factory_path):
38 factory_path = os.path.abspath(sys.path[0])
40 try:
41 f = open(os.path.join(factory_path, 'conf.pth'))
42 DATA = f.readline().splitlines()[0]
43 f.close()
44 except:
45 DATA = os.path.join(factory_path, 'docfactory.dat')
47 # need some IDs
48 [wxID_WXNEWPROJ, wxID_WXNEWREST, wxID_WXOPENFILE,
49 wxID_WXREMFILE, wxID_WXSAVEFILE, wxID_WXEXITAPP, wxID_WXPUBL, wxID_WXPUBLALL,
50 wxID_WXPROJSETTINGS, wxID_WXINSERTPATH, wxID_WXINSERTIMAGE,
51 wxID_WXINSERTFIGURE, wxID_WXCLOSEFILE, wxID_WXVIEWEOLS, wxID_WXEOLSTOCR,
52 wxID_WXEOLSTOLF, wxID_WXEOLSTOCRLF, wxID_WXEOLSTO, wxID_WXVIEWEDGE,
53 wxID_WXCUTSELECTION, wxID_WXCOPYSELECTION, wxID_WXPASTESELECTION,
54 wxID_WXUNDO, wxID_WXREDO, wxID_WXGOTO, wxID_WXSELECTALL, wxID_WXVIEWWS,
55 wxID_WXDELETEPROJ, wxID_WXFONTSIZE, wxID_WXSMALLFONT, wxID_WXNORMALFONT,
56 wxID_WXBIGFONT, wxID_WXINSERT, wxID_WXBTNBOLD, wxID_WXBTNITALIC,
57 wxID_WXBTNLITERAL, wxID_WXBTNLINK, wxID_WXBTNIMAGE, wxID_WXBTNTOOL,
58 wxID_WXINSERTHYPERLINK, wxID_WXBTNPASTE, wxID_WXBTNCOPY, wxID_WXRUNTOOL,
59 wxID_WXBTNCUT, wxID_WXBTNREDO, wxID_WXBTNUNDO, wxID_WXBTNSAVE,
60 wxID_WXBTNOPEN, wxID_WXBTNNEW, wxID_WXBTNPUBLISH, wxID_WXBTNABOUT,
61 wxID_WXFINDREPLACE, wxID_WXBACKUPFILES] = map(lambda init_menubar: wxNewId(), range(53))
63 # Accelerator-Table for key commands
64 ACCEL = [(wxACCEL_NORMAL,WXK_F7,wxID_WXPUBL),
65 (wxACCEL_NORMAL,WXK_F8,wxID_WXRUNTOOL),
66 (wxACCEL_CTRL,ord('F'),wxID_WXFINDREPLACE),
67 (wxACCEL_CTRL,ord('G'),wxID_WXGOTO),
68 (wxACCEL_CTRL,ord('N'),wxID_WXNEWREST),
69 (wxACCEL_CTRL,ord('O'),wxID_WXOPENFILE),
70 (wxACCEL_CTRL,ord('S'),wxID_WXSAVEFILE),
71 (wxACCEL_ALT,ord('X'),wxID_WXEXITAPP)]
73 #-------------------------------------------------------------------------
75 def get_file_extension(filename):
76 return os.path.splitext(filename)[1]
78 #-------------------------------------------------------------------------
80 class DocProject:
81 def __init__(self):
82 self.name = ''
83 self.directory = 'Nobody expects the spamish inquisition.'
84 self.files = []
86 def add(self, file):
87 if file not in self.files:
88 self.files.append(file)
89 else:
90 raise 'File is already part of this project.'
92 def remove(self, file):
93 if file in self.files:
94 self.files.remove(file)
96 #-------------------------------------------------------------------------
98 class CustomLog(wxPyLog):
99 def __init__(self, textCtrl, logTime=0):
100 wxPyLog.__init__(self)
101 self.tc = textCtrl
102 self.logTime = logTime
104 def DoLogString(self, message, timeStamp):
105 if self.logTime:
106 message = time.strftime('%X', time.localtime(timeStamp)) + \
107 ': ' + message
108 self.tc.AppendText(message + '\n')
110 #-------------------------------------------------------------------------
112 class DocFactoryFrame(wxFrame):
113 def __init__(self, projects=[], initial_file=None):
114 wxFrame.__init__(
115 self, NULL, -1, NAME, wxDefaultPosition, (800, 600),
116 style=wxDEFAULT_FRAME_STYLE)
117 self.Centre()
118 self.projects = projects
119 self.formats = {}
120 self.files = []
121 self.project = None
122 self.editor = None
123 self.activeitem = None
124 self.imagedir = None
125 self.language_code = 'en'
127 # Application-Icon
128 bmp = images.getLogoSmallBitmap()
129 mask = wxMaskColour(bmp, wxWHITE)
130 bmp.SetMask(mask)
131 logoicon = wxEmptyIcon()
132 logoicon.CopyFromBitmap(bmp)
133 self.SetIcon(logoicon)
135 self.init_preferences()
136 self.init_menubar()
137 self.init_toolbar()
139 self.SetAcceleratorTable(wxAcceleratorTable(ACCEL))
141 # splitter windows
142 splitter = wxSplitterWindow(self, -1, style=wxNO_3D|wxSP_3D)
143 splitter2 = wxSplitterWindow(splitter, -1, style=wxNO_3D|wxSP_3D)
145 # Set up a log
146 self.log = wxTextCtrl(splitter2, -1,
147 style = wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL)
149 # Set the wxWindows log target to be this textctrl
150 wxLog_SetActiveTarget(CustomLog(self.log))
152 # tree
153 tID = wxNewId()
154 self.tree = CustomTreeCtrl(splitter, tID, style=wxTR_HAS_BUTTONS |
155 wxTR_HAS_VARIABLE_ROW_HEIGHT)
156 EVT_TREE_ITEM_ACTIVATED(self.tree, tID, self.on_tree_item_activated)
158 # make an image list for tree
159 self.im0 = self.im1 = self.im2 = -1
160 self.il = wxImageList(16, 16)
161 self.im0 = self.il.Add(images.getLogoSmallBitmap())
162 self.im1 = self.il.Add(images.getProjectBitmap())
163 self.im2 = self.il.Add(images.getFile1Bitmap())
164 self.tree.SetImageList(self.il)
166 self.init_tree()
168 # Create a Notebook
169 tID = wxNewId()
170 self.nb = wxNotebook(splitter2, tID, style=wxCLIP_CHILDREN)
171 EVT_NOTEBOOK_PAGE_CHANGED(self.nb, tID, self.on_notebook_page_changed)
173 # editor-page
174 self.InitEditorPage()
176 # create statusbar
177 self.sb = CustomStatusBar(self)
178 self.SetStatusBar(self.sb)
180 # initial file
181 if initial_file != None:
182 self.load_initial_file(initial_file)
184 self.Show(1)
186 # add the windows to the splitter and split it.
187 splitter2.SplitHorizontally(self.nb, self.log)
188 splitter.SplitVertically(self.tree, splitter2)
190 splitter.SetSashPosition(180, 1)
191 splitter.SetMinimumPaneSize(20)
192 splitter2.SetSashPosition(450, 1)
193 splitter2.SetMinimumPaneSize(20)
195 # Some global state variables.
196 self.projectdirty = 0
198 EVT_CLOSE(self, self.on_close_window)
200 def init_menubar(self):
201 self.mainmenu = wxMenuBar()
202 mainwindow = self
204 # File
205 menu=wxMenu()
206 menu.Append(wxID_WXNEWREST, 'New\tCtrl+N',
207 'Create a new txt-file and add to project')
208 EVT_MENU(self, wxID_WXNEWREST, self.on_file_new)
209 menu.Append(wxID_WXOPENFILE, 'Open...\tCtrl+O',
210 'Open a file')
211 EVT_MENU(self, wxID_WXOPENFILE, self.on_file_open)
212 menu.Append(wxID_WXCLOSEFILE, 'Close',
213 'Close file')
214 EVT_MENU(self, wxID_WXCLOSEFILE, self.on_file_close)
215 menu.Enable(wxID_WXCLOSEFILE, 0)
216 menu.Append(wxID_WXSAVEFILE, 'Save\tCtrl+S', 'Save file now')
217 EVT_MENU(self, wxID_WXSAVEFILE, self.on_file_save)
218 menu.Enable(wxID_WXSAVEFILE, 0)
219 menu.Append(wxID_WXREMFILE, 'Remove From Project',
220 'Remove file from project')
221 EVT_MENU(self, wxID_WXREMFILE, self.on_file_remove)
222 menu.Enable(wxID_WXREMFILE, 0)
223 menu.AppendSeparator()
224 menu.Append(wxID_WXPUBL, 'Publish...\tF7',
225 'Publish file')
226 EVT_MENU(self, wxID_WXPUBL, self.on_publish)
227 menu.Enable(wxID_WXPUBL, 0)
228 menu.AppendSeparator()
229 menu.Append(wxID_WXEXITAPP, 'Exit\tAlt+X', 'Exit program')
230 EVT_MENU(self, wxID_WXEXITAPP, self.on_app_exit)
231 self.mainmenu.Append (menu, '&File')
233 # Edit
234 menu=wxMenu()
235 menu.Append(wxID_WXUNDO, 'Undo\tCtrl-Z', 'Undo')
236 EVT_MENU(self, wxID_WXUNDO, self.on_undo)
237 menu.Enable(wxID_WXUNDO, 0)
238 menu.Append(wxID_WXREDO, 'Redo\tCtrl-Y', 'Redo')
239 EVT_MENU(self, wxID_WXREDO, self.on_redo)
240 menu.Enable(wxID_WXREDO, 0)
241 menu.AppendSeparator()
242 menu.Append(wxID_WXCUTSELECTION, 'Cut\tCtrl-X', 'Cut')
243 EVT_MENU(self, wxID_WXCUTSELECTION, self.on_cut)
244 menu.Enable(wxID_WXCUTSELECTION, 0)
245 menu.Append(wxID_WXCOPYSELECTION, 'Copy\tCtrl-C', 'Copy')
246 EVT_MENU(self, wxID_WXCOPYSELECTION, self.on_copy)
247 menu.Enable(wxID_WXCOPYSELECTION, 0)
248 menu.Append(wxID_WXPASTESELECTION, 'Paste\tCtrl-V', 'Paste')
249 EVT_MENU(self, wxID_WXPASTESELECTION, self.on_paste)
250 menu.Enable(wxID_WXPASTESELECTION, 0)
251 menu.Append(wxID_WXSELECTALL, 'Select all\tCtrl-A', 'Select the entire file')
252 EVT_MENU(self, wxID_WXSELECTALL, self.on_select_all)
253 menu.Enable(wxID_WXSELECTALL, 0)
254 menu.AppendSeparator()
255 submenu=wxMenu()
256 submenu.Append(wxID_WXINSERTFIGURE, 'Figure',
257 'Insert a figure')
258 EVT_MENU(self, wxID_WXINSERTFIGURE, self.on_insert_figure)
259 submenu.Append(wxID_WXINSERTHYPERLINK, 'Hyperlink', 'Insert a hyperlink')
260 EVT_MENU(self, wxID_WXINSERTHYPERLINK, self.on_btn_hyperlink)
261 submenu.Append(wxID_WXINSERTIMAGE, 'Image', 'Insert an image')
262 EVT_MENU(self, wxID_WXINSERTIMAGE, self.on_insert_image)
263 submenu.Append(wxID_WXINSERTPATH, 'Path', 'Insert a path')
264 EVT_MENU(self, wxID_WXINSERTPATH, self.on_insert_path)
265 menu.AppendMenu(wxID_WXINSERT, 'Insert', submenu)
266 menu.Enable(wxID_WXINSERT, 0)
267 menu.AppendSeparator()
268 submenu=wxMenu()
269 submenu.Append(wxID_WXEOLSTOCR,'CR',
270 'Change all end of line characters to CR (\\r)')
271 EVT_MENU(self, wxID_WXEOLSTOCR, self.on_eols_to_cr)
272 submenu.Append(wxID_WXEOLSTOLF,'LF',
273 'Change all end of line characters to LF (\\n)')
274 EVT_MENU(self, wxID_WXEOLSTOLF, self.on_eols_to_lf)
275 submenu.Append(wxID_WXEOLSTOCRLF,'CRLF',
276 'Change all end of line characters to LF (\\r\\n)')
277 EVT_MENU(self, wxID_WXEOLSTOCRLF, self.on_eols_to_crlf)
278 menu.AppendMenu(wxID_WXEOLSTO, 'Change all EOLs to', submenu)
279 menu.Enable(wxID_WXEOLSTO, 0)
280 menu.AppendSeparator()
281 menu.Append(wxID_WXFINDREPLACE, 'Find && Replace...\tCtrl-F', 'Find and replace')
282 EVT_MENU(self, wxID_WXFINDREPLACE, self.on_findreplace_show)
283 EVT_COMMAND_FIND(self, -1, self.on_find)
284 EVT_COMMAND_FIND_NEXT(self, -1, self.on_find)
285 EVT_COMMAND_FIND_REPLACE(self, -1, self.on_find)
286 EVT_COMMAND_FIND_REPLACE_ALL(self, -1, self.on_find)
287 EVT_COMMAND_FIND_CLOSE(self, -1, self.on_find_close)
288 menu.Append(wxID_WXGOTO, 'Goto line...\tCtrl-G', 'Goto a specific line in the file')
289 EVT_MENU(self, wxID_WXGOTO, self.on_goto)
290 menu.Enable(wxID_WXGOTO, 0)
291 self.mainmenu.Append (menu, '&Edit')
293 # Project
294 menu=wxMenu()
295 menu.Append(wxID_WXNEWPROJ, 'New', 'Create a new project')
296 EVT_MENU(self, wxID_WXNEWPROJ, self.on_project_new)
297 menu.Append(wxID_WXDELETEPROJ, 'Delete...',
298 'Delete one or more projects')
299 EVT_MENU(self, wxID_WXDELETEPROJ, self.on_project_delete)
300 menu.Append(wxID_WXPUBLALL, 'Publish...',
301 'Publish all txt-files of active project')
302 EVT_MENU(self, wxID_WXPUBLALL, self.on_publish_all)
303 menu.Enable(wxID_WXPUBLALL, 0)
304 menu.AppendSeparator()
305 menu.Append(wxID_WXPROJSETTINGS, 'Project Settings...',
306 'Edit project settings')
307 EVT_MENU(self, wxID_WXPROJSETTINGS, self.on_project_settings)
308 if len(self.projects) == 0:
309 menu.Enable(wxID_WXDELETEPROJ, 0)
310 menu.Enable(wxID_WXPROJSETTINGS, 0)
311 self.mainmenu.Append (menu, 'Pr&oject')
313 self.init_tools()
315 # Preferences
316 menu=wxMenu()
317 menu.Append(wxID_WXVIEWEOLS, 'View EOL markers',
318 'Show or hide end-of-line markers', wxITEM_CHECK)
319 EVT_MENU(self, wxID_WXVIEWEOLS, self.on_view_eols)
320 menu.Check(wxID_WXVIEWEOLS, self.preferences['eol_markers'])
321 menu.Append(wxID_WXVIEWEDGE, 'View right edge indicator',
322 'Toggle display of the right edge indicator (75 characters)',
323 wxITEM_CHECK)
324 EVT_MENU(self, wxID_WXVIEWEDGE, self.on_view_edge)
325 menu.Check(wxID_WXVIEWEDGE, self.preferences['right_edge_indicator'])
326 menu.Append(wxID_WXVIEWWS, 'View whitespace',
327 'Show or hide whitespace', wxITEM_CHECK)
328 EVT_MENU(self, wxID_WXVIEWWS, self.on_view_ws)
329 menu.Check(wxID_WXVIEWWS, self.preferences['whitespace'])
330 menu.AppendSeparator()
331 submenu=wxMenu()
332 submenu.Append(wxID_WXSMALLFONT,'Small',
333 'Reduce font', wxITEM_RADIO)
334 EVT_MENU(self, wxID_WXSMALLFONT, self.on_font_small)
335 submenu.Append(wxID_WXNORMALFONT,'Normal',
336 'Restore font', wxITEM_RADIO)
337 EVT_MENU(self, wxID_WXNORMALFONT, self.on_font_normal)
338 submenu.Append(wxID_WXBIGFONT,'Big',
339 'Magnify font', wxITEM_RADIO)
340 EVT_MENU(self, wxID_WXBIGFONT, self.on_font_big)
341 if self.preferences['fontsize'] == 'small':
342 submenu.Check(wxID_WXSMALLFONT, 1)
343 elif self.preferences['fontsize'] == 'big':
344 submenu.Check(wxID_WXBIGFONT, 1)
345 else:
346 submenu.Check(wxID_WXNORMALFONT, 1)
347 menu.AppendMenu(wxID_WXFONTSIZE, 'Fontsize', submenu)
348 menu.AppendSeparator()
349 menu.Append(wxID_WXBACKUPFILES, 'Backup files',
350 'Backup files as [filename].bak', wxITEM_CHECK)
351 EVT_MENU(self, wxID_WXBACKUPFILES, self.on_backup_files)
352 menu.Check(wxID_WXBACKUPFILES, self.preferences['backup_files'])
353 self.mainmenu.Append(menu, '&Preferences')
355 # Toolbox
356 menu=wxMenu()
357 exitID=wxNewId()
358 menu.Append(exitID, 'Configure', 'Configure toolbox')
359 EVT_MENU(self, exitID, self.on_configure_tools)
360 menu.Append(wxID_WXRUNTOOL, 'Open...\tF8', 'Open toolbox')
361 EVT_MENU(self, wxID_WXRUNTOOL, self.on_run_tool)
362 self.mainmenu.Append (menu, '&Toolbox')
364 # Help
365 menu=wxMenu()
366 exitID=wxNewId()
367 menu.Append(exitID, 'About', 'About')
368 EVT_MENU(self, exitID, self.on_help_about)
369 self.mainmenu.Append (menu, '&Help')
371 self.SetMenuBar(self.mainmenu)
373 def init_preferences(self):
374 self.preferences = {}
375 if os.path.exists(DATA):
376 try:
377 cfg = ConfigParser.ConfigParser()
378 cfg.read(DATA)
379 if cfg.has_section('preferences'):
380 for pref in cfg.options('preferences'):
381 if pref in ('eol_markers', 'right_edge_indicator', \
382 'whitespace', 'backup_files'):
383 self.preferences[pref] = cfg.getboolean('preferences', pref)
384 else:
385 self.preferences[pref] = cfg.get('preferences', pref)
386 except:
387 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
388 if not self.preferences.has_key('eol_markers'):
389 self.preferences['eol_markers'] = 0
390 if not self.preferences.has_key('right_edge_indicator'):
391 self.preferences['right_edge_indicator'] = 1
392 if not self.preferences.has_key('whitespace'):
393 self.preferences['whitespace'] = 0
394 if not self.preferences.has_key('fontsize'):
395 self.preferences['fontsize'] = 'normal'
396 if not self.preferences.has_key('backup_files'):
397 self.preferences['backup_files'] = 0
399 def init_toolbar(self):
400 self.toolbar = tb = self.CreateToolBar(wxTB_HORIZONTAL|wxTB_FLAT|wxNO_BORDER)
401 bmp = images.getNewBitmap()
402 mask = wxMaskColour(bmp, wxBLUE)
403 bmp.SetMask(mask)
404 tb.AddLabelTool(wxID_WXBTNNEW, 'New', bmp, shortHelp='New',
405 longHelp='Create a new file')
406 EVT_TOOL(tb, wxID_WXBTNNEW, self.on_file_new)
407 bmp = images.getOpenBitmap()
408 mask = wxMaskColour(bmp, wxWHITE)
409 bmp.SetMask(mask)
410 tb.AddLabelTool(wxID_WXBTNOPEN, 'Open', bmp, shortHelp='Open',
411 longHelp='Open an existing file')
412 EVT_TOOL(tb, wxID_WXBTNOPEN, self.on_file_open)
413 bmp = images.getSaveBitmap()
414 mask = wxMaskColour(bmp, wxWHITE)
415 bmp.SetMask(mask)
416 tb.AddLabelTool(wxID_WXBTNSAVE, 'Save', bmp, shortHelp='Save',
417 longHelp='Save the active file')
418 EVT_TOOL(tb, wxID_WXBTNSAVE, self.on_file_save)
419 tb.EnableTool(wxID_WXBTNSAVE, 0)
420 tb.AddSeparator()
421 bmp = images.getCutBitmap()
422 mask = wxMaskColour(bmp, wxWHITE)
423 bmp.SetMask(mask)
424 tb.AddLabelTool(wxID_WXBTNCUT, 'Cut', bmp, shortHelp='Cut',
425 longHelp='Cut the selection and put it on the Clipboard')
426 EVT_TOOL(tb, wxID_WXBTNCUT, self.on_cut)
427 tb.EnableTool(wxID_WXBTNCUT, 0)
428 bmp = images.getCopyBitmap()
429 mask = wxMaskColour(bmp, wxBLUE)
430 bmp.SetMask(mask)
431 tb.AddLabelTool(wxID_WXBTNCOPY, 'Copy', bmp, shortHelp='Copy',
432 longHelp='Copy the selection and put it on the Clipboard')
433 EVT_TOOL(tb, wxID_WXBTNCOPY, self.on_copy)
434 tb.EnableTool(wxID_WXBTNCOPY, 0)
435 bmp = images.getPasteBitmap()
436 mask = wxMaskColour(bmp, wxBLUE)
437 bmp.SetMask(mask)
438 tb.AddLabelTool(wxID_WXBTNPASTE, 'Paste', bmp, shortHelp='Paste',
439 longHelp='Insert Clipboard contents')
440 EVT_TOOL(tb, wxID_WXBTNPASTE, self.on_paste)
441 tb.EnableTool(wxID_WXBTNPASTE, 0)
442 tb.AddSeparator()
443 bmp = images.getUndoBitmap()
444 mask = wxMaskColour(bmp, wxWHITE)
445 bmp.SetMask(mask)
446 tb.AddLabelTool(wxID_WXBTNUNDO, 'Undo', bmp, shortHelp='Undo',
447 longHelp='Undo the last action')
448 EVT_TOOL(tb, wxID_WXBTNUNDO, self.on_undo)
449 tb.EnableTool(wxID_WXBTNUNDO, 0)
450 bmp = images.getRedoBitmap()
451 mask = wxMaskColour(bmp, wxWHITE)
452 bmp.SetMask(mask)
453 tb.AddLabelTool(wxID_WXBTNREDO, 'Redo', bmp, shortHelp='Redo',
454 longHelp='Redo the previously undone action')
455 EVT_TOOL(tb, wxID_WXBTNREDO, self.on_redo)
456 tb.EnableTool(wxID_WXBTNREDO, 0)
457 tb.AddSeparator()
458 bmp = images.getLinkBitmap()
459 mask = wxMaskColour(bmp, wxBLUE)
460 bmp.SetMask(mask)
461 tb.AddLabelTool(wxID_WXBTNLINK, 'Hyperlink', bmp,
462 shortHelp='Insert hyperlink',
463 longHelp='Insert hyperlink')
464 EVT_TOOL(tb, wxID_WXBTNLINK, self.on_btn_hyperlink)
465 tb.EnableTool(wxID_WXBTNLINK, 0)
466 bmp = images.getImageBitmap()
467 mask = wxMaskColour(bmp, wxBLUE)
468 bmp.SetMask(mask)
469 tb.AddLabelTool(wxID_WXBTNIMAGE, 'Image', bmp,
470 shortHelp='Insert image',
471 longHelp='Insert image')
472 EVT_TOOL(tb, wxID_WXBTNIMAGE, self.on_btn_image)
473 tb.EnableTool(wxID_WXBTNIMAGE, 0)
474 tb.AddSeparator()
475 bmp = images.getPublishBitmap()
476 mask = wxMaskColour(bmp, wxRED)
477 bmp.SetMask(mask)
478 tb.AddLabelTool(wxID_WXBTNPUBLISH, 'Publish', bmp, shortHelp='Publish file',
479 longHelp='Publish file')
480 EVT_TOOL(tb, wxID_WXBTNPUBLISH, self.on_publish)
481 tb.EnableTool(wxID_WXBTNPUBLISH, 0)
482 tb.AddSeparator()
483 bmp = images.getToolBitmap()
484 mask = wxMaskColour(bmp, wxBLUE)
485 bmp.SetMask(mask)
486 tb.AddLabelTool(wxID_WXBTNTOOL, 'Toolbox', bmp, shortHelp='Open toolbox',
487 longHelp='Open toolbox')
488 EVT_TOOL(tb, wxID_WXBTNTOOL, self.on_run_tool)
489 tb.AddSeparator()
490 bmp = images.getBoldBitmap()
491 mask = wxMaskColour(bmp, wxWHITE)
492 bmp.SetMask(mask)
493 tb.AddLabelTool(wxID_WXBTNBOLD, 'Bold', bmp, shortHelp='Bold', longHelp='Bold')
494 EVT_TOOL(tb, wxID_WXBTNBOLD, self.on_format_word)
495 tb.EnableTool(wxID_WXBTNBOLD, 0)
496 bmp = images.getItalicBitmap()
497 mask = wxMaskColour(bmp, wxWHITE)
498 bmp.SetMask(mask)
499 tb.AddLabelTool(wxID_WXBTNITALIC, 'Italic', bmp, shortHelp='Italic', longHelp='Italic')
500 EVT_TOOL(tb, wxID_WXBTNITALIC, self.on_format_word)
501 tb.EnableTool(wxID_WXBTNITALIC, 0)
502 bmp = images.getPreBitmap()
503 mask = wxMaskColour(bmp, wxWHITE)
504 bmp.SetMask(mask)
505 tb.AddLabelTool(wxID_WXBTNLITERAL, 'literal', bmp, shortHelp='Literal', longHelp='Literal')
506 EVT_TOOL(tb, wxID_WXBTNLITERAL, self.on_format_word)
507 tb.EnableTool(wxID_WXBTNLITERAL, 0)
508 if wxPlatform != '__WXMAC__':
509 tb.AddSeparator()
510 self.formats = {'Title' : '=', 'Subtitle' : '-',
511 'Heading 1' : '=', 'Heading 2' : '-',
512 'Heading 3' : '~', 'Heading 4' : '`'}
513 formats = self.formats.keys()
514 formats.sort()
515 exitID=wxNewId()
516 self.combobox_format = wxComboBox(tb, exitID, '',
517 choices = formats,
518 style=wxCB_DROPDOWN|wxCB_READONLY)
519 tb.AddControl(self.combobox_format)
520 EVT_COMBOBOX(tb, exitID, self.on_format_paragraph)
521 tb.AddSeparator()
522 bmp = images.getAboutBitmap()
523 mask = wxMaskColour(bmp, wxWHITE)
524 bmp.SetMask(mask)
525 tb.AddLabelTool(wxID_WXBTNABOUT, 'About', bmp, shortHelp='About',
526 longHelp='Display program information')
527 EVT_TOOL(tb, wxID_WXBTNABOUT, self.on_help_about)
528 tb.Realize()
530 def init_tools(self):
531 self.tools = {}
532 if os.path.exists(DATA):
533 try:
534 cfg = ConfigParser.ConfigParser()
535 cfg.read(DATA)
536 if cfg.has_section('tools'):
537 for i in range(len(cfg.options('tools'))):
538 self.tools[i+1] = [i+1] \
539 + cfg.get('tools', str(i+1)).split(';')
540 except:
541 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
543 def init_tree(self):
544 self.tree.DeleteAllItems()
545 self.root = self.tree.AddRoot('Workspace', self.im0)
546 self.tree.SetPyData(self.root, None)
547 for proj in self.projects:
548 child = self.tree.AppendItem(self.root, proj.name, self.im1)
549 self.tree.SetPyData(child, None)
550 for file in proj.files:
551 last = self.tree.AppendItem(child, file, self.im2)
552 self.tree.SetPyData(last, None)
553 self.tree.SortChildren(self.root)
554 for file in self.files:
555 last = self.tree.AppendItem(self.root, file, self.im2)
556 self.tree.SetPyData(last, None)
557 self.project = None
558 self.activateMenuItemsProjectOpen(0)
559 self.activateMenuItemsFileSelected(0)
560 self.activeitem = self.root
561 self.tree.SelectItem(self.activeitem)
562 self.tree.Expand(self.activeitem)
563 if self.editor != None:
564 self.nb.SetPageText(0, 'Editor')
565 self.editor.Clear()
566 self.editor.Enable(0)
567 self.activateMenuItemsFileSelected(0)
568 if self.nb.GetPageCount() > 1:
569 self.nb.DeletePage(1)
571 def InitEditorPage(self):
572 # init editor
573 edID = wxNewId()
574 self.editor = CustomStyledTextCtrl(self.nb, edID, self.log)
575 self.editor.SetViewEOL(self.preferences['eol_markers'])
576 self.editor.SetEdgeMode(self.preferences['right_edge_indicator'])
577 self.editor.SetViewWhiteSpace(self.preferences['whitespace'])
578 if self.preferences['fontsize'] == 'small':
579 self.editor.SetZoom(-2)
580 elif self.preferences['fontsize'] == 'big':
581 self.editor.SetZoom(2)
582 else:
583 self.editor.SetZoom(0)
584 self.editor.Clear()
585 self.editor.Enable(0)
586 self.nb.AddPage(self.editor, 'Editor')
587 self.nb.SetSelection(0)
589 # --------------------------------------------------------------------
590 # handlers
591 # --------------------------------------------------------------------
593 def activateMenuItemsProjectOpen(self, value):
594 menu = self.mainmenu.GetMenu(self.mainmenu.FindMenu('Project'))
595 if len(self.projects) > 0 and value:
596 menu.Enable(wxID_WXDELETEPROJ, value)
597 menu.Enable(wxID_WXPROJSETTINGS, value)
598 menu.Enable(wxID_WXPUBLALL, value)
600 def activateMenuItemsFileSelected(self, value):
601 menu = self.mainmenu.GetMenu(self.mainmenu.FindMenu('File'))
602 if self.project != None:
603 menu.Enable(wxID_WXREMFILE, value)
604 else:
605 menu.Enable(wxID_WXREMFILE, 0)
606 menu.Enable(wxID_WXCLOSEFILE, value)
607 menu.Enable(wxID_WXSAVEFILE, value)
608 menu.Enable(wxID_WXPUBL, value)
609 menu = self.mainmenu.GetMenu(self.mainmenu.FindMenu('Edit'))
610 menu.Enable(wxID_WXUNDO, value)
611 menu.Enable(wxID_WXREDO, value)
612 menu.Enable(wxID_WXCUTSELECTION, value)
613 menu.Enable(wxID_WXCOPYSELECTION, value)
614 menu.Enable(wxID_WXPASTESELECTION, value)
615 menu.Enable(wxID_WXSELECTALL, value)
616 menu.Enable(wxID_WXINSERT, value)
617 menu.Enable(wxID_WXEOLSTO, value)
618 menu.Enable(wxID_WXFINDREPLACE, value)
619 menu.Enable(wxID_WXGOTO, value)
620 self.toolbar.EnableTool(wxID_WXBTNSAVE, value)
621 self.toolbar.EnableTool(wxID_WXBTNLINK, value)
622 self.toolbar.EnableTool(wxID_WXBTNIMAGE, value)
623 self.toolbar.EnableTool(wxID_WXBTNBOLD, value)
624 self.toolbar.EnableTool(wxID_WXBTNITALIC, value)
625 self.toolbar.EnableTool(wxID_WXBTNLITERAL, value)
626 self.toolbar.EnableTool(wxID_WXBTNPASTE, value)
627 self.toolbar.EnableTool(wxID_WXBTNCOPY, value)
628 self.toolbar.EnableTool(wxID_WXBTNCUT, value)
629 self.toolbar.EnableTool(wxID_WXBTNREDO, value)
630 self.toolbar.EnableTool(wxID_WXBTNUNDO, value)
631 self.toolbar.EnableTool(wxID_WXBTNPUBLISH, value)
633 def CheckEditorChanges(self):
634 go_ahead = 1
635 if self.editor != None:
636 if self.editor.IsModified:
637 dlg=wxMessageDialog(self, 'Save changes?', NAME,
638 wxYES_NO | wxCANCEL | wxICON_QUESTION)
639 result = dlg.ShowModal()
640 if result == wxID_YES:
641 file = self.tree.GetItemText(self.activeitem)
642 go_ahead = self.editor.SaveFile(file, self.preferences['backup_files'])
643 if result == wxID_CANCEL:
644 go_ahead = 0
645 dlg.Destroy()
646 return go_ahead
648 def delete_project(self, project):
649 if project in self.projects:
650 self.projects.remove(project)
651 try:
652 self.save_projects()
653 except:
654 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
655 self.init_tree()
657 def htmlfile(self, file, dir):
658 htmlfile = os.path.join(dir,
659 os.path.splitext(os.path.basename(file))[0] \
660 + '.html')
661 return htmlfile
663 def insert_hyperlink(self):
664 item = self.activeitem
665 itemimage = self.tree.GetItemImage(item)
666 file = self.tree.GetItemText(item)
667 if self.project != None:
668 dir = self.project.directory
669 else:
670 dir = os.path.dirname(file)
671 go_ahead = 1
672 dlg = hyperlinkDlg(self, directory=dir, project=self.project)
673 dlg.Centre()
674 if dlg.ShowModal() == wxID_OK:
675 target = dlg.GetPath()
676 else:
677 go_ahead = 0
678 dlg.Destroy()
679 if go_ahead:
680 selection = self.editor.GetSelectedText()
681 curpos = self.editor.GetCurrentPos()
682 if selection != '':
683 self.editor.BeginUndoAction()
684 selection = '`%s`' % selection
685 self.editor.ReplaceSelection(selection + '_')
686 endpos = self.editor.GetTextLength()
687 self.editor.GotoPos(endpos)
688 text = '\n\n.. _%s: %s' % (selection, target)
689 self.editor.ReplaceSelection(text)
690 self.editor.GotoPos(curpos + 3)
691 self.editor.EndUndoAction()
692 else:
693 text = '\n\n.. _``: %s\n' % target
694 self.editor.ReplaceSelection(text)
695 self.editor.GotoPos(curpos + 7)
697 def insert_image(self, directive):
698 item = self.activeitem
699 itemimage = self.tree.GetItemImage(item)
700 file = self.tree.GetItemText(item)
701 if self.imagedir == None:
702 if self.project != None:
703 dir = self.project.directory
704 else:
705 dir = os.path.dirname(file)
706 else:
707 dir = self.imagedir
708 dlg = ImageDialog(self, dir)
709 dlg.Centre()
710 go_ahead = 1
711 if dlg.ShowModal() == wxID_OK:
712 target = dlg.GetFile()
713 self.imagedir = os.path.dirname(target)
714 else:
715 go_ahead = 0
716 dlg.Destroy()
717 if go_ahead:
718 if self.project != None:
719 dir = self.project.directory
720 else:
721 dir = os.path.dirname(file)
722 dlg = wxDirDialog(self, 'Calculate path relative'
723 ' to which outputdirectory?', dir)
724 if dlg.ShowModal() == wxID_OK:
725 dir = dlg.GetPath()
726 else:
727 go_ahead = 0
728 dlg.Destroy()
729 if go_ahead:
730 selection = self.editor.GetSelectedText()
731 if selection != '' and directive == 'image':
732 self.editor.BeginUndoAction()
733 curpos = self.editor.GetCurrentPos()
734 selection = '|%s|' % selection
735 self.editor.ReplaceSelection(selection)
736 endpos = self.editor.GetTextLength()
737 self.editor.GotoPos(endpos)
738 text = '\n\n.. %s %s:: %s' % (selection, directive,
739 quote(relative_path(self.htmlfile(file,dir),
740 target)))
741 self.editor.ReplaceSelection(text)
742 self.editor.GotoPos(curpos + 2)
743 self.editor.EndUndoAction()
744 else:
745 text = '\n\n.. %s:: %s\n\n' % (directive,
746 quote(relative_path(self.htmlfile(file,dir),
747 target)))
748 self.editor.ReplaceSelection(text)
750 def load_initial_file(self, file):
751 if os.path.exists(file):
752 try:
753 self.files.append(file)
754 dir = ''
755 parent = self.root
756 self.open_file_in_editor(file)
757 last = self.tree.AppendItem(parent, file, self.im2)
758 self.tree.SetPyData(last, None)
759 self.tree.SetItemBold(self.activeitem, 0)
760 self.tree.SetItemTextColour(self.activeitem, wxBLACK)
761 self.activeitem = last
762 self.tree.SetItemBold(self.activeitem, 1)
763 self.tree.SetItemTextColour(self.activeitem, wxBLUE)
764 self.tree.EnsureVisible(self.activeitem)
765 self.tree.SelectItem(self.activeitem)
766 parent = self.tree.GetItemParent(self.activeitem)
767 self.tree.SetItemBold(parent, 1)
768 self.tree.SetItemTextColour(parent, wxBLUE)
769 except:
770 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
771 else:
772 customMsgBox(self, 'Can not find %s.' % file, 'error')
774 def open_file_in_editor(self, file):
775 if self.nb.GetPageCount() > 1:
776 self.nb.DeletePage(1)
777 self.nb.SetSelection(0)
778 self.editor.LoadFile(file)
779 self.set_editor_language_code(file)
780 self.nb.SetPageText(0, 'Editor: %s' %
781 os.path.basename(file))
782 self.editor.Enable(1)
783 self.activateMenuItemsFileSelected(1)
784 self.editor.IsModified = 0
786 def project_save(self):
787 try:
788 self.save_projects()
789 self.projectdirty = 0
790 except:
791 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
793 def publishFile(self, infile, outfile, outdir, writer):
794 wxBeginBusyCursor()
795 try:
796 self.log.Clear()
797 t = time.localtime(time.time())
798 st = time.strftime('%d-%b-%Y, %H:%M:%S', t)
799 wxLogMessage('%s: Publishing %s' % (st, writer))
800 wxLogMessage('SOURCE: %s' % infile)
801 outfile_fullpath = os.path.join(outdir, outfile)
802 wxLogMessage('DESTINATION: %s' % outfile_fullpath)
803 if outfile_fullpath == infile:
804 customMsgBox(self, 'Destination and source are identical.'
805 '\nNo processing.', 'wakeup')
806 warning_lines = error_lines = []
807 else:
808 try:
809 publish_document(writer, infile, outfile, outdir)
810 finally:
811 warning_lines, error_lines = get_errors(self.log.GetValue())
812 linecount = self.editor.GetLineCount()
813 self.editor.MarkerDeleteAll(0)
814 self.editor.MarkerDeleteAll(1)
815 if warning_lines != []:
816 for line in warning_lines:
817 if line < linecount:
818 self.editor.MarkerAdd(line, 0)
819 self.editor.GotoLine(warning_lines[-1])
820 if error_lines != []:
821 for line in error_lines:
822 if line < linecount:
823 self.editor.MarkerAdd(line, 1)
824 self.editor.GotoLine(error_lines[-1])
825 self.editor.IsModified = 0
826 if os.path.exists(outfile_fullpath):
827 if self.nb.GetPageCount() > 1:
828 self.nb.DeletePage(1)
829 if get_file_extension(outfile) in ['.html', '.htm']:
830 # init html-viewer page
831 if wxPlatform == '__WXMSW__':
832 htmlprv = browser.IEHtmlPanel(self.nb, self, self.log,
833 'file://%s' % quote(outfile_fullpath))
834 else:
835 htmlprv = browser.HtmlPanel(self.nb, self, self.log,
836 outfile_fullpath)
837 self.nb.AddPage(htmlprv, 'HTML-Viewer: %s'
838 % outfile)
839 if warning_lines == error_lines == []:
840 self.nb.SetSelection(1)
841 t = time.localtime(time.time())
842 st = time.strftime('%d-%b-%Y, %H:%M:%S: ', t)
843 wxLogMessage('%sFinished.' % st)
844 finally:
845 wxEndBusyCursor()
847 def save_preferences(self):
848 cfg = ConfigParser.ConfigParser()
849 try:
850 cfg.read(DATA)
851 if not cfg.has_section('preferences'):
852 cfg.add_section('preferences')
853 for pref in self.preferences.keys():
854 cfg.set('preferences', pref,
855 self.preferences[pref])
856 f = open(DATA, 'wt')
857 cfg.write(f)
858 f.close()
859 except:
860 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
862 def save_projects(self):
863 cfg = ConfigParser.ConfigParser()
864 cfg.read(DATA)
865 for section in cfg.sections():
866 if section[:19] == 'docfactory_project:':
867 cfg.remove_section(section)
868 for project in self.projects:
869 section = 'docfactory_project: %s' % project.name
870 cfg.add_section(section)
871 cfg.set(section, 'outputdirectory', project.directory)
872 files = ''
873 for file in project.files:
874 files = '%s;%s' % (files, file)
875 if len(files) > 1:
876 files = files[1:]
877 cfg.set(section, 'files', files)
878 f = open(DATA, 'wt')
879 cfg.write(f)
880 f.close()
882 def set_editor_language_code(self, file=None):
883 language_code = 'en'
884 if self.project != None:
885 directory = self.project.directory
886 else:
887 directory = os.path.split(os.path.abspath(file))[0]
888 docutils_conf = os.path.join(directory, 'docutils.conf')
889 if os.path.exists(docutils_conf):
890 try:
891 cfg = ConfigParser.ConfigParser()
892 cfg.read(docutils_conf)
893 if cfg.has_option('general', 'language_code'):
894 language_code = cfg.get('general', 'language_code')
895 except:
896 print '%s:\n%s\n%s' % sys.exc_info()
897 self.editor.bibliographic_fields = get_rest_bibl_fields(language_code)
899 # --------------------------------------------------------------------
900 # event handlers
901 # --------------------------------------------------------------------
903 def on_app_exit(self, event):
904 self.Close()
906 def on_backup_files(self, event):
907 self.preferences['backup_files'] = not self.preferences['backup_files']
909 def on_btn_image(self, event):
910 self.insert_image('image')
912 def on_close_window(self, event):
913 go_ahead = 1
914 if self.projectdirty:
915 dlg=wxMessageDialog(self, 'Save project?', NAME,
916 wxYES_NO | wxCANCEL | wxICON_QUESTION)
917 result = dlg.ShowModal()
918 if result == wxID_YES:
919 self.project_save()
920 if result == wxID_CANCEL:
921 go_ahead = 0
922 dlg.Destroy()
924 if go_ahead and self.CheckEditorChanges():
925 self.Destroy()
927 def on_configure_tools(self, event):
928 dlg = toolsDlg(self)
929 dlg.Centre()
930 if dlg.ShowModal() == wxID_OK:
931 self.tools = dlg.get_tools()
932 dlg.Destroy()
933 cfg = ConfigParser.ConfigParser()
934 try:
935 cfg.read(DATA)
936 if cfg.has_section('tools'):
937 cfg.remove_section('tools')
938 cfg.add_section('tools')
939 for tool in self.tools.values():
940 cfg.set('tools', str(tool[0]),
941 '%s;%s;%s' % (tool[1],tool[2],tool[3]))
942 f = open(DATA, 'wt')
943 cfg.write(f)
944 f.close()
945 except:
946 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
948 def on_copy(self, event):
949 self.editor.Copy()
951 def on_cut(self, event):
952 self.editor.Cut()
954 def on_eols_to_cr(self, event):
955 wxBeginBusyCursor()
956 self.editor.ConvertEOLs(1)
957 wxEndBusyCursor()
959 def on_eols_to_lf(self, event):
960 wxBeginBusyCursor()
961 self.editor.ConvertEOLs(2)
962 wxEndBusyCursor()
964 def on_eols_to_crlf(self, event):
965 wxBeginBusyCursor()
966 self.editor.ConvertEOLs(0)
967 wxEndBusyCursor()
969 def on_file_close(self, event):
970 if self.CheckEditorChanges():
971 if self.nb.GetPageCount() > 1:
972 self.nb.DeletePage(1)
973 self.nb.SetPageText(0, 'Editor')
974 self.editor.Clear()
975 self.editor.Enable(0)
976 self.activateMenuItemsFileSelected(0)
977 item = self.activeitem
978 parent = self.tree.GetItemParent(item)
979 self.tree.SetItemBold(item, 0)
980 self.tree.SetItemTextColour(item, wxBLACK)
981 if parent == self.root:
982 self.files.remove(self.tree.GetItemText(item))
983 self.tree.Delete(item)
984 self.activeitem = parent
985 self.tree.SelectItem(self.activeitem)
987 def on_file_open(self, event):
989 go_ahead = 1
991 if self.CheckEditorChanges():
993 if self.project != None:
994 dir = self.project.directory
995 parent = self.tree.GetItemParent(self.activeitem)
996 if parent == self.root:
997 parent = self.activeitem
998 else:
999 dir = ''
1000 parent = self.root
1002 wildcard = 'Text (*.txt)|*.txt|' \
1003 'All files (*.*)|*.*'
1005 dlg = wxFileDialog (self, 'Open file',
1006 dir, '', wildcard,
1007 wxOPEN|wxFILE_MUST_EXIST)
1008 if dlg.ShowModal() == wxID_OK:
1009 file = dlg.GetPath()
1010 if parent == self.root:
1011 if file not in self.files:
1012 self.files.append(file)
1013 else:
1014 customMsgBox(self, '%s already in workspace.' % file,
1015 'wakeup')
1016 go_ahead = 0
1017 else:
1018 if file not in self.project.files:
1019 self.project.add(file)
1020 self.project_save()
1021 else:
1022 customMsgBox(self, '%s already part of project "%s".'
1023 % (file, self.project.name),
1024 'wakeup')
1025 go_ahead = 0
1026 else:
1027 go_ahead = 0
1028 dlg.Destroy()
1030 if go_ahead:
1031 self.open_file_in_editor(file)
1032 last = self.tree.AppendItem(parent, file, self.im2)
1033 self.tree.SetPyData(last, None)
1034 self.tree.SetItemBold(self.activeitem, 0)
1035 self.tree.SetItemTextColour(self.activeitem, wxBLACK)
1036 self.activeitem = last
1037 self.tree.SetItemBold(self.activeitem, 1)
1038 self.tree.SetItemTextColour(self.activeitem, wxBLUE)
1039 self.tree.EnsureVisible(self.activeitem)
1040 self.tree.SelectItem(self.activeitem)
1041 parent = self.tree.GetItemParent(self.activeitem)
1042 self.tree.SetItemBold(parent, 1)
1043 self.tree.SetItemTextColour(parent, wxBLUE)
1045 def on_file_new(self, event):
1046 go_ahead = 1
1048 if self.project != None:
1049 dir = self.project.directory
1050 else:
1051 dir = ''
1053 dlg = wxFileDialog (self, 'Create new file',
1054 dir, '', '*.txt',
1055 wxSAVE|wxOVERWRITE_PROMPT)
1056 if dlg.ShowModal() == wxID_OK:
1057 file = dlg.GetPath()
1058 else:
1059 go_ahead = 0
1060 dlg.Destroy()
1062 if go_ahead:
1063 dlg = wxTextEntryDialog(
1064 self, 'Enter a title for this document:', 'Title', '',
1065 wxOK | wxCANCEL)
1066 dlg.Centre()
1067 if dlg.ShowModal() == wxID_OK:
1068 title = dlg.GetValue()
1069 else:
1070 go_ahead = 0
1071 dlg.Destroy()
1073 if go_ahead:
1074 try:
1075 f = open(file, 'w')
1076 f.write(len(title)*'='+'\n')
1077 f.write(title+'\n')
1078 f.write(len(title)*'='+'\n')
1079 f.close()
1080 if self.project != None:
1081 self.project.add(file)
1082 self.project_save()
1083 parent = self.tree.GetItemParent(self.activeitem)
1084 if parent == self.root:
1085 parent = self.activeitem
1086 else:
1087 self.files.append(file)
1088 parent = self.root
1089 self.open_file_in_editor(file)
1090 last = self.tree.AppendItem(parent, file, self.im2)
1091 self.tree.SetPyData(last, None)
1092 self.tree.SetItemBold(self.activeitem, 0)
1093 self.tree.SetItemTextColour(self.activeitem, wxBLACK)
1094 self.activeitem = last
1095 self.tree.SetItemBold(self.activeitem, 1)
1096 self.tree.SetItemTextColour(self.activeitem, wxBLUE)
1097 self.tree.EnsureVisible(self.activeitem)
1098 self.tree.SelectItem(self.activeitem)
1099 except:
1100 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(),
1101 'error')
1102 dlg.Destroy()
1104 def on_file_remove(self, event):
1105 item = self.tree.GetSelection()
1106 file = self.tree.GetItemText(item)
1107 self.activeitem = self.tree.GetItemParent(item)
1108 self.tree.Delete(item)
1109 self.project.remove(file)
1110 self.project_save()
1111 self.nb.SetPageText(0, 'Editor')
1112 self.editor.Clear()
1113 self.editor.Enable(0)
1114 self.activateMenuItemsFileSelected(0)
1115 dlg=wxMessageDialog(
1116 self, 'Delete file %s from disk?' % file, NAME,
1117 wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)
1118 result = dlg.ShowModal()
1119 if result == wxID_YES:
1120 try:
1121 os.remove(file)
1122 except:
1123 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(),
1124 'error')
1125 dlg.Destroy()
1127 def on_file_save(self, event):
1128 go_ahead = 1
1129 file = self.tree.GetItemText(self.activeitem)
1130 wxLogMessage('Saving %s.' % file)
1131 go_ahead = self.editor.SaveFile(file, self.preferences['backup_files'])
1132 if go_ahead:
1133 if self.nb.GetPageCount() > 1:
1134 self.nb.DeletePage(1)
1136 def on_publish(self, event):
1137 go_ahead = 1
1138 item = self.activeitem
1139 file = self.tree.GetItemText(item)
1140 if self.editor.IsModified:
1141 wxLogMessage('Saving %s.' % file)
1142 wxBeginBusyCursor()
1143 go_ahead = self.editor.SaveFile(file, self.preferences['backup_files'])
1144 wxEndBusyCursor()
1145 if go_ahead and os.path.exists(file):
1146 dlg = publishDlg(self, infile=file, project=self.project)
1147 dlg.Centre()
1148 if dlg.ShowModal() == wxID_OK:
1149 outfile, outdir, writer = dlg.GetValues()
1150 else:
1151 go_ahead = 0
1152 dlg.Destroy()
1153 if go_ahead:
1154 self.publishFile(file, outfile, outdir, writer)
1156 def on_publish_all(self, event):
1157 go_ahead = 1
1158 item = self.activeitem
1159 file = self.tree.GetItemText(item)
1160 if self.editor.IsModified:
1161 wxLogMessage('Saving %s.' % file)
1162 wxBeginBusyCursor()
1163 go_ahead = self.editor.SaveFile(file, self.preferences['backup_files'])
1164 wxEndBusyCursor()
1165 if go_ahead:
1166 writers = publishers.keys()
1167 writers.sort()
1168 dlg = wxSingleChoiceDialog(self, 'Please select a writer:',
1169 'Publish "%s"' % self.project.name,
1170 writers, wxOK|wxCANCEL)
1171 dlg.SetSelection(writers.index('HTML'))
1172 dlg.Centre()
1173 if dlg.ShowModal() == wxID_OK:
1174 writer = writers[dlg.GetSelection()]
1175 else:
1176 go_ahead = 0
1177 dlg.Destroy()
1178 if go_ahead:
1179 self.log.Clear()
1180 files_to_publish = []
1181 for file in self.project.files:
1182 if os.path.splitext(file)[1] == '.txt':
1183 files_to_publish.append(file)
1184 max = len(files_to_publish)
1185 dlg = wxProgressDialog('Publishing "%s"' % self.project.name,
1187 max,
1188 self,
1189 wxPD_CAN_ABORT | wxPD_APP_MODAL)
1190 keepGoing = true
1191 count = 0
1192 error_files = []
1193 while keepGoing and count < max:
1194 infile = files_to_publish[count]
1195 outfile = '%s%s' % (os.path.splitext(os.path.split(infile)[1])[0],
1196 publishers[writer][2])
1197 outdir = self.project.directory
1198 keepGoing = dlg.Update(count, outfile)
1199 t = time.localtime(time.time())
1200 st = time.strftime('%d-%b-%Y, %H:%M:%S', t)
1201 wxLogMessage('\n%s: Publishing %s' % (st, writer))
1202 wxLogMessage('SOURCE: %s' % infile)
1203 outfile_fullpath = os.path.join(outdir, outfile)
1204 wxLogMessage('DESTINATION: %s' % outfile_fullpath)
1205 try:
1206 publish_document(writer, infile, outfile, outdir)
1207 except:
1208 wxLogMessage('ERROR: %s (%s)' % sys.exc_info()[:2])
1209 error_files.append((infile, sys.exc_info()[1]))
1210 count = count + 1
1211 dlg.Destroy()
1212 if error_files != []:
1213 msg = 'Due to severe errors the following files have ' \
1214 'not been published properly: \n'
1215 for file in error_files:
1216 msg = '%s\n%s\n(%s)\n' % (msg, file[0], file[1])
1217 msg = '%s\nHINT: You should try to publish the files ' \
1218 'in question separately first. If that works, ' \
1219 'you can try to publish all files again.' % msg
1220 dlg = wxScrolledMessageDialog(self, msg, "WARNING!")
1221 dlg.Centre()
1222 dlg.ShowModal()
1223 dlg.Destroy()
1225 def on_find(self, event):
1226 et = event.GetEventType()
1227 if et == wxEVT_COMMAND_FIND_REPLACE or et == wxEVT_COMMAND_FIND_REPLACE_ALL:
1228 replacetxt = event.GetReplaceString()
1229 self.sb.SetStatusText('', 0)
1230 findtxt = event.GetFindString()
1231 length = len(findtxt)
1232 lastpos = self.editor.GetTextLength()
1233 flags = event.GetFlags()
1234 if flags in (1, 3, 5, 7):
1235 if flags == 7:
1236 # whole word / match case
1237 regexp = re.compile(r'\b%s\b' % findtxt)
1238 elif flags == 5:
1239 # no whole word / match case
1240 regexp = re.compile(r'%s' % findtxt)
1241 elif flags == 3:
1242 # whole word / no match case
1243 regexp = re.compile(r'\b%s\b' % findtxt, re.IGNORECASE)
1244 else:
1245 # no whole word / no match case
1246 regexp = re.compile(findtxt, re.IGNORECASE)
1247 else:
1248 regexp = None
1249 print 'Unknown combination of flags.'
1250 if regexp != None:
1251 if et == wxEVT_COMMAND_FIND_REPLACE_ALL:
1252 wxBeginBusyCursor()
1253 self.editor.BeginUndoAction()
1254 origtxt = self.editor.GetText()
1255 if regexp.search(origtxt) != None:
1256 self.editor.SetText(regexp.sub(replacetxt, origtxt))
1257 self.sb.SetStatusText('Replaced.', 0)
1258 else:
1259 self.sb.SetStatusText('No match found.', 0)
1260 self.editor.EndUndoAction()
1261 wxEndBusyCursor()
1262 else:
1263 currpos = self.editor.GetCurrentPos()
1264 if et == wxEVT_COMMAND_FIND_REPLACE and currpos != 0:
1265 self.editor.ReplaceSelection(replacetxt)
1266 currpos = self.editor.GetCurrentPos()
1267 lastpos = self.editor.GetTextLength()
1268 origtxt = self.editor.GetTextRange(currpos, lastpos)
1269 position = len(regexp.split(origtxt)[0])
1270 startpos = currpos + position
1271 if startpos < lastpos:
1272 self.editor.GotoPos(startpos + len(regexp.findall(origtxt)[0]))
1273 self.editor.SetAnchor(startpos)
1274 else:
1275 self.sb.SetStatusText('Can not find "%s". Next search will '
1276 'start from beginning.' % findtxt, 0)
1277 self.editor.GotoPos(0)
1279 def on_find_close(self, event):
1280 event.GetDialog().Destroy()
1282 def on_findreplace_show(self, event):
1283 data = wxFindReplaceData()
1284 dlg = wxFindReplaceDialog(self, data, 'Find & Replace',
1285 wxFR_REPLACEDIALOG)
1286 dlg.data = data
1287 dlg.Show(1)
1289 def on_font_small(self, event):
1290 self.preferences['fontsize'] = 'small'
1291 self.editor.SetZoom(-2)
1292 self.save_preferences()
1294 def on_font_normal(self, event):
1295 self.preferences['fontsize'] = 'normal'
1296 self.editor.SetZoom(0)
1297 self.save_preferences()
1299 def on_font_big(self, event):
1300 self.preferences['fontsize'] = 'big'
1301 self.editor.SetZoom(2)
1302 self.save_preferences()
1304 def on_format_paragraph(self, event):
1305 self.nb.SetSelection(0)
1306 line_no = self.editor.GetCurrentLine()
1307 self.editor.GotoLine(line_no)
1308 startpos = self.editor.GetCurrentPos()
1309 line = self.editor.GetLine(line_no)
1310 endpos = startpos + len(line)
1311 self.editor.SetSelection(startpos, endpos)
1312 format = event.GetString()
1313 if format in ['Title','Subtitle']:
1314 overline = (len(line)-1)*self.formats[format]+'\n'
1315 else:
1316 overline = ''
1317 underline = (len(line)-1)*self.formats[format]+'\n'
1318 line_below = self.editor.GetLine(line_no+1)
1319 if line != underline and line_below != underline:
1320 self.editor.ReplaceSelection('\n%s%s%s\n' % (overline,
1321 line.strip(' '),
1322 underline))
1323 self.editor.GotoPos(startpos)
1325 def on_format_word(self, event):
1326 self.nb.SetSelection(0)
1327 selection = self.editor.GetSelectedText()
1328 if selection != '':
1329 if event.GetId() == wxID_WXBTNBOLD:
1330 symbol = '**'
1331 elif event.GetId() == wxID_WXBTNITALIC:
1332 symbol = '*'
1333 elif event.GetId() == wxID_WXBTNLITERAL:
1334 symbol = '``'
1335 else:
1336 symbol = ''
1337 selection = selection.replace('*','').replace('`','').strip()
1338 selection = '%s%s%s' % (symbol,selection,symbol)
1339 self.editor.ReplaceSelection(selection)
1341 def on_goto(self, event):
1342 values = []
1343 for i in range(self.editor.GetLineCount()+1)[1:]:
1344 values.append(str(i))
1345 dlg = wxSingleChoiceDialog(self, 'Select a line number:', 'Goto line...',
1346 values, wxOK|wxCANCEL)
1347 if dlg.ShowModal() == wxID_OK:
1348 self.editor.GotoLine(int(dlg.GetStringSelection())-1)
1349 dlg.Destroy()
1351 def on_help_about(self, event):
1353 Event handler for menu
1354 option *Help -> About*.
1356 dlg = aboutDlg(self)
1357 try:
1358 dlg.Centre()
1359 dlg.ShowModal()
1360 finally:
1361 dlg.Destroy()
1363 def on_insert_figure(self, event):
1364 self.insert_image('figure')
1366 def on_btn_hyperlink(self, event):
1367 self.insert_hyperlink()
1369 def on_insert_image(self, event):
1370 self.insert_image('image')
1372 def on_insert_path(self, event):
1373 item = self.activeitem
1374 itemimage = self.tree.GetItemImage(item)
1375 file = self.tree.GetItemText(item)
1376 if self.project != None:
1377 dir = self.project.directory
1378 else:
1379 dir = os.path.dirname(file)
1380 dlg = wxFileDialog (self, 'Choose file',
1381 dir, '', '*.*',
1382 wxOPEN|wxFILE_MUST_EXIST)
1383 go_ahead = 1
1384 if dlg.ShowModal() == wxID_OK:
1385 target = dlg.GetPath()
1386 else:
1387 go_ahead = 0
1388 if go_ahead:
1389 if self.project != None:
1390 dir = self.project.directory
1391 else:
1392 dir = os.path.dirname(file)
1393 dlg = wxDirDialog(self, 'Calculate path relative'
1394 ' to which outputdirectory?', dir)
1395 if dlg.ShowModal() == wxID_OK:
1396 dir = dlg.GetPath()
1397 else:
1398 go_ahead = 0
1399 dlg.Destroy()
1400 if go_ahead:
1401 self.editor.ReplaceSelection(
1402 quote(relative_path(self.htmlfile(file,dir), target)))
1403 dlg.Destroy()
1405 def on_notebook_page_changed(self, event):
1406 event.Skip()
1408 def on_paste(self, event):
1409 self.editor.Paste()
1411 def on_project_delete(self, event):
1412 available_projects = []
1413 for project in self.projects:
1414 available_projects.append(project.name)
1415 if available_projects != []:
1416 available_projects.sort()
1417 dlg = wxMultipleChoiceDialog(self, 'Select the projects which you want to' \
1418 '\ndelete or press "Cancel" to abort.',
1419 'Delete Projects',
1420 available_projects)
1421 dlg.Centre()
1422 if dlg.ShowModal() == wxID_OK:
1423 selection = dlg.GetValueString()
1424 for project_name in selection:
1425 for project in self.projects:
1426 if project.name == project_name:
1427 self.delete_project(project)
1428 dlg.Destroy()
1429 else:
1430 customMsgBox(self, 'Sorry, I don\'t remember any projects.',
1431 'info')
1433 def on_project_new(self, event):
1434 go_ahead = 1
1436 other_project_names = []
1437 for project in self.projects:
1438 other_project_names.append(project.name)
1440 if self.CheckEditorChanges():
1441 project = DocProject()
1442 dlg = projectSettingsDlg(self, project, other_project_names)
1443 dlg.Centre()
1444 if dlg.ShowModal() == wxID_CANCEL:
1445 go_ahead = 0
1446 else:
1447 self.project = project
1448 self.project.name, self.project.directory = dlg.getValues()
1449 dlg.Destroy()
1451 if go_ahead:
1452 try:
1453 self.projects.append(self.project)
1454 self.save_projects()
1455 self.init_tree()
1456 except:
1457 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
1459 def on_project_settings(self, event):
1460 go_ahead = 1
1462 other_project_names = []
1463 for project in self.projects:
1464 if project.name != self.project.name:
1465 other_project_names.append(project.name)
1467 dlg = projectSettingsDlg(self, self.project, other_project_names)
1468 dlg.Centre()
1469 if dlg.ShowModal() == wxID_CANCEL:
1470 go_ahead = 0
1471 else:
1472 name, directory = dlg.getValues()
1473 self.set_editor_language_code()
1474 dlg.Destroy()
1476 if go_ahead:
1477 sort_tree_new = 0
1478 if self.project.name != name:
1479 self.projectdirty = 1
1480 sort_tree_new = 1
1481 for project in self.projects:
1482 if project.name == self.project.name:
1483 project.name = name
1484 self.project.name = name
1485 if self.project.directory != directory:
1486 self.project.directory = directory
1487 self.projectdirty = 1
1489 if self.projectdirty:
1490 self.project_save()
1491 if sort_tree_new:
1492 self.init_tree()
1494 def on_redo(self, event):
1495 self.editor.Redo()
1497 def on_run_tool(self, event):
1498 go_ahead = 1
1499 choices = []
1500 for tool in self.tools.values():
1501 choices.append('%s: %s' % (tool[0],tool[1]))
1502 dlg = wxSingleChoiceDialog(self, 'Select a tool...', 'Toolbox',
1503 choices, wxOK|wxCANCEL)
1504 dlg.Centre()
1505 if dlg.ShowModal() == wxID_OK:
1506 tool_id=int(dlg.GetStringSelection().split(':')[0])
1507 for key in self.tools.keys():
1508 if self.tools[key][0] == tool_id:
1509 tool_key = key
1510 break
1511 else:
1512 go_ahead = 0
1513 dlg.Destroy()
1514 if go_ahead:
1515 self.log.Clear()
1516 tool = self.tools[tool_key]
1517 item = self.activeitem
1518 file = self.tree.GetItemText(item)
1519 curdir = os.getcwd()
1520 try:
1521 replmts = {'$[FileDir]': os.path.split(file)[0],
1522 '$[FilePath]': file,
1523 '$[FileName]': os.path.split(file)[1],
1524 '$[FileBase]': os.path.splitext(os.path.basename(file))[0],
1525 '$[ProjectDir]': self.project.directory}
1526 command = tool[2]
1527 for str in replmts.keys():
1528 command = command.replace(str, replmts[str])
1529 directory = tool[3]
1530 for str in replmts.keys():
1531 directory = directory.replace(str, replmts[str])
1532 except:
1533 go_ahead = 0
1534 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
1535 if go_ahead:
1536 t = time.localtime(time.time())
1537 st = time.strftime('%d-%b-%Y, %H:%M:%S', t)
1538 wxLogMessage('%s: %s' % (st, tool[1]))
1539 wxLogMessage('COMMAND: %s\nDIRECTORY: %s' % (command, directory))
1540 try:
1541 os.chdir(directory)
1542 os.system(command)
1543 t = time.localtime(time.time())
1544 st = time.strftime('%d-%b-%Y, %H:%M:%S', t)
1545 wxLogMessage('%s: Finished.' % st)
1546 except:
1547 customMsgBox(self, '%s:\n%s\n%s' % sys.exc_info(), 'error')
1548 os.chdir(curdir)
1550 def on_select_all(self, event):
1551 self.editor.SelectAll()
1553 def on_tree_item_activated(self, event):
1554 item=event.GetItem()
1555 go_ahead = 1
1557 if item != self.activeitem \
1558 and self.tree.GetItemImage(self.activeitem) == self.im2:
1559 go_ahead = self.CheckEditorChanges()
1561 if go_ahead:
1562 self.nb.SetSelection(0)
1563 if item != self.activeitem:
1564 if self.activeitem != self.root:
1565 olditemparent = self.tree.GetItemParent(self.activeitem)
1566 self.tree.SetItemBold(olditemparent, 0)
1567 self.tree.SetItemTextColour(olditemparent, wxBLACK)
1568 self.tree.SetItemBold(self.activeitem, 0)
1569 self.tree.SetItemTextColour(self.activeitem, wxBLACK)
1570 self.activeitem = item
1571 self.tree.SetItemBold(item, 1)
1572 if item != self.root:
1573 self.tree.SetItemTextColour(item, wxBLUE)
1574 else:
1575 go_ahead = 0
1577 if go_ahead:
1578 itemparent = self.tree.GetItemParent(item)
1579 itemimage = self.tree.GetItemImage(item)
1581 # item is file:
1582 if itemimage == self.im2:
1583 # file is part of project:
1584 if self.tree.GetItemImage(itemparent) == self.im1:
1585 self.tree.SetItemBold(itemparent, 1)
1586 self.tree.SetItemTextColour(itemparent, wxBLUE)
1587 project_name = self.tree.GetItemText(itemparent)
1588 for project in self.projects:
1589 if project.name == project_name:
1590 self.project = project
1591 self.activateMenuItemsProjectOpen(1)
1592 # file is not part of project:
1593 else:
1594 self.project = None
1595 self.activateMenuItemsProjectOpen(0)
1596 file=self.tree.GetItemText(item)
1597 if os.path.exists(file):
1598 self.open_file_in_editor(file)
1599 else:
1600 self.editor.Clear()
1601 self.editor.Enable(0)
1602 self.nb.SetPageText(0, 'Editor')
1603 self.activateMenuItemsFileSelected(0)
1604 parent = self.tree.GetItemParent(item)
1605 self.tree.SetItemBold(item, 0)
1606 self.tree.SetItemTextColour(item, wxBLACK)
1607 self.activeitem = parent
1608 dlg=wxMessageDialog(self, '%s does not exist.\nRemove '
1609 'from project?' % file,
1610 NAME, wxYES_NO | wxICON_QUESTION)
1611 if dlg.ShowModal() == wxID_YES:
1612 self.tree.Delete(item)
1613 self.project.remove(file)
1614 self.project_save()
1615 dlg.Destroy()
1616 self.tree.SelectItem(self.activeitem)
1618 # item is project:
1619 elif itemimage == self.im1:
1620 project_name = self.tree.GetItemText(item)
1621 for project in self.projects:
1622 if project.name == project_name:
1623 self.project = project
1624 self.activateMenuItemsProjectOpen(1)
1625 self.nb.SetPageText(0, 'Editor')
1626 self.editor.Clear()
1627 self.editor.Enable(0)
1628 self.activateMenuItemsFileSelected(0)
1630 # item is neither file nor project:
1631 else:
1632 self.nb.SetPageText(0, 'Editor')
1633 self.editor.Clear()
1634 self.editor.Enable(0)
1635 self.project = None
1636 self.activateMenuItemsProjectOpen(0)
1637 self.activateMenuItemsFileSelected(0)
1639 def on_undo(self, event):
1640 self.editor.Undo()
1642 def on_view_eols(self, event):
1643 self.preferences['eol_markers'] = not self.preferences['eol_markers']
1644 self.editor.SetViewEOL(self.preferences['eol_markers'])
1645 self.save_preferences()
1647 def on_view_edge(self, event):
1648 self.preferences['right_edge_indicator'] = not self.preferences['right_edge_indicator']
1649 self.editor.SetEdgeMode(self.preferences['right_edge_indicator'])
1650 self.save_preferences()
1652 def on_view_ws(self, event):
1653 self.preferences['whitespace'] = not self.preferences['whitespace']
1654 self.editor.SetViewWhiteSpace(self.preferences['whitespace'])
1655 self.save_preferences()
1657 #---------------------------------------------------------------------------
1659 class FactoryApp(wxApp):
1660 def OnInit(self):
1661 provider = wxSimpleHelpProvider()
1662 wxHelpProvider_Set(provider)
1663 self.projects = []
1664 if self.init_projects():
1665 wxInitAllImageHandlers()
1666 if len(sys.argv) > 1:
1667 frame = DocFactoryFrame(self.projects, sys.argv[1])
1668 else:
1669 frame = DocFactoryFrame(self.projects)
1670 self.SetTopWindow(frame)
1671 return 1
1672 else:
1673 return 0
1675 def init_projects(self):
1676 if os.path.exists(DATA):
1677 try:
1678 cfg = ConfigParser.ConfigParser()
1679 cfg.read(DATA)
1680 for section in cfg.sections():
1681 if section[:19] == 'docfactory_project:':
1682 project = DocProject()
1683 project.name = section.split(': ')[1]
1684 project.directory = cfg.get(section, 'outputdirectory')
1685 if cfg.has_option(section, 'files'):
1686 project.files = cfg.get(section, 'files').split(';')
1687 if project.files == ['']:
1688 project.files = []
1689 self.projects.append(project)
1690 except:
1691 f = open('error.txt', 'w')
1692 f.write('%s:\n%s\n%s' % sys.exc_info())
1693 f.close()
1694 return 0
1695 return 1
1697 #--------------------------------------------------------------------------
1699 def main():
1700 app = FactoryApp(0)
1701 app.MainLoop()
1703 #--------------------------------------------------------------------------
1705 if __name__ == '__main__':
1706 main()
1708 #--------------------------------------------------------------------------