Do not use <sup> and <sub> tags inside <pre> (parsed-literal blocks).
[docutils.git] / sandbox / dugui / dugui.py
blobaf238a5ab92a273323a753d3e39423203d48d84a
1 # -*- coding: UTF-8 -*-
4 # dugui.py
6 # Copyright (C) 2006 Facundo Batista <facundo@taniquetil.com.ar>
8 # This file is placed under the Python 2.3 license, see
9 # http://www.python.org/2.3/license.html
12 import wx
13 import wx.html as html
14 import user, codecs, os
15 import validator
16 from docutils.core import publish_file
19 # We must integrate this with gettext() later
20 def _(t): return t
23 # Menu IDs
24 # File
25 wxID_OPEN = 101
26 wxID_CLOSE = 102
27 wxID_EXIT = 103
28 #Actions
29 wxID_PROCESS = 201
30 wxID_CONFIGURATION = 202
31 #Help
32 wxID_USERGUIDE = 301
33 wxID_ABOUT = 302
35 # Formats
36 # Key: what you'll see in the radiobox
37 # Value: (writer name for docutils, file extension)
38 FORMATS = {"HTML": ("html", ".html"),
39 "LaTeX": ("latex", ".tex"),
40 "XML": ("xml", ".xml"),
43 class ControlPanel(wx.Panel):
44 """Panel with the files selections grid."""
46 def __init__(self, parent, vsProc):
47 wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS)
48 self.parent = parent
49 self.vsProc = vsProc
51 # the grid
52 gbs = wx.GridBagSizer(3, 4)
54 # source chooser
55 # static text
56 label = wx.StaticText(self, -1, _("Source file:"))
57 gbs.Add(label, (0,0))
58 # text entry
59 id = wx.NewId()
60 self.teSource = wx.TextCtrl(self, id, "", size=(200,-1), style=wx.TE_PROCESS_ENTER)
61 wx.EVT_TEXT_ENTER(self.teSource, id, self.enteredSourceText)
62 gbs.Add(self.teSource, (0,1), flag=wx.EXPAND)
63 self.vsProc.registerEditor(self.teSource, "")
64 # button
65 sBtn = wx.Button(self, wx.ID_OPEN)
66 wx.EVT_BUTTON(sBtn, wx.ID_OPEN, parent.onFileOpen)
67 gbs.Add(sBtn, (0,2))
69 # destination chooser
70 # static text
71 label = wx.StaticText(self, -1, _("Destination file:"))
72 gbs.Add(label, (1,0))
73 # text entry
74 id = wx.NewId()
75 self.teDestin = wx.TextCtrl(self, id, "", size=(200,-1), style=wx.TE_PROCESS_ENTER)
76 wx.EVT_TEXT_ENTER(self.teDestin, id, self.enteredDestinText)
77 gbs.Add(self.teDestin, (1,1), flag=wx.EXPAND)
78 self.vsProc.registerEditor(self.teDestin, "")
79 # button
80 dBtn = wx.Button(self, wx.ID_SAVE)
81 wx.EVT_BUTTON(dBtn, wx.ID_SAVE, parent.onDestChooser)
82 gbs.Add(dBtn, (1,2))
84 # format chooser
85 id = wx.NewId()
86 self.options = ["HTML", "LaTeX", "XML"]
87 self.rbFormat = wx.RadioBox(self, id, _("Destination format"), wx.DefaultPosition, wx.DefaultSize, self.options, 1, style=wx.RA_SPECIFY_COLS)
88 wx.EVT_RADIOBOX(self.rbFormat, id, self.rbSelection)
89 gbs.Add(self.rbFormat, (0,3), (3,1))
91 # process button
92 id = wx.NewId()
93 procBtn = wx.Button(self, id, _("Process"))
94 wx.EVT_BUTTON(procBtn, id, parent.goProcess)
95 vsProc.registerAction(procBtn)
96 gbs.Add(procBtn, (2,4))
98 self.SetSizer(gbs)
99 return
101 def enteredSourceText(self, event):
102 self.parent.onFileOpen(None, self.teSource.GetValue())
103 return
105 def enteredDestinText(self, event):
106 self.parent.onDestChooser(None, self.teDestin.GetValue())
107 return
109 def rbSelection(self, event):
110 # check what was chosen
111 chosen = event.GetSelection()
112 format = self.options[chosen]
114 # change the filename
115 filename = self.teDestin.GetValue()
116 for ext in [x[1] for x in FORMATS.values()]:
117 if filename.endswith(ext):
118 filename = filename[:-len(ext)]
119 filename += FORMATS[format][1]
120 self.teDestin.SetValue(filename)
121 self.enteredDestinText(None)
122 return
126 class TextsPanel(wx.Panel):
127 """Panel with the texts fields."""
129 def __init__(self, parent, vsProc):
130 wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS)
131 self.vsProc = vsProc
133 box = wx.BoxSizer(wx.HORIZONTAL)
135 # source text
136 self.oText = wx.TextCtrl(self, -1, size=(200, 100), style=wx.TE_MULTILINE|wx.TE_RICH2|wx.TE_READONLY)
137 box.Add(self.oText, 1, wx.EXPAND|wx.ALL, border=10)
138 self.vsProc.registerEditor(self.oText, "")
139 # put monospaced font
140 font = self.oText.GetFont()
141 font = wx.Font(font.GetPointSize(), wx.TELETYPE, font.GetStyle(), font.GetWeight(), font.GetUnderlined())
142 self.oText.SetFont(font)
144 # destination text
145 self.dText = html.HtmlWindow(self, -1, style=wx.NO_FULL_REPAINT_ON_RESIZE)
146 if "gtk2" in wx.PlatformInfo:
147 self.dText.SetStandardFonts()
148 box.Add(self.dText, 1, wx.EXPAND|wx.ALL, border=10)
150 self.SetSizer(box)
151 return
153 def setSourceText(self, text):
154 self.vsProc.setEditorState(self.oText, bool(text))
155 self.oText.SetValue(text)
156 return
158 def setDestinText(self, filename):
159 self.dText.LoadPage(filename)
161 class MenuEnabler(object):
162 '''Receives all the items that must be enabled or disabled according to if the there's an open project or not.'''
163 def __init__(self, menuBar, itemList):
164 self.menuBar = menuBar
165 self.itemList = itemList
167 def Enable(self, newState):
168 for item in self.itemList:
169 self.menuBar.Enable(item, newState)
170 return
173 class MyFrame(wx.Frame):
174 def __init__(self, parent, ID, title):
175 wx.Frame.__init__(self, parent, ID, title, pos=(-1,-1), size=wx.Size(600, 400))
177 self.CreateStatusBar()
178 self.previousMessage = ""
179 self.statusBar(_("Welcome to Docutils"))
180 menuBar = wx.MenuBar()
182 # Validator to check if the project is open and enable/disable the menu
183 self.vsSourceOpen = validator.ValidationSupervisor(self.statusBar, "originIsOpen")
184 enablerItemList = []
185 # For Process button
186 self.vsProc = validator.ValidationSupervisor(self.statusBar, "vsProcess")
188 #File
189 menu = wx.Menu() # 0
190 menu.Append(wxID_OPEN, _("&Open")+"\tCtrl-O", _("Open a source file to process"))
191 menu.Append(wxID_CLOSE, _("&Close")+"\tCtrl-W", _("Close the current source file"))
192 menu.Append(wxID_EXIT, _("&Exit")+"\tCtrl-Q", _("Exit Docutils"))
193 menuBar.Append(menu, _("&File"))
194 wx.EVT_MENU(self, wxID_OPEN, self.onFileOpen)
195 wx.EVT_MENU(self, wxID_CLOSE, self.onFileClose)
196 wx.EVT_MENU(self, wxID_EXIT, self.onFileExit)
197 enablerItemList.append(wxID_CLOSE)
198 #Actions
199 menu = wx.Menu() # 1
200 menu.Append(wxID_PROCESS, _("&Process"), _("Process the origin file"))
201 menu.Append(wxID_CONFIGURATION, _("&Configure"), _("Configure Distutils options"))
202 menuBar.Append(menu, _("&Actions"))
203 wx.EVT_MENU(self, wxID_PROCESS, self.onNotImplementedYet)
204 wx.EVT_MENU(self, wxID_CONFIGURATION, self.onNotImplementedYet)
205 enablerItemList.append(wxID_PROCESS)
206 #Help
207 menu = wx.Menu() # 2
208 menu.Append(wxID_USERGUIDE, _("&User guide")+"\tF1", _("Browse the user guide"))
209 menu.Append(wxID_ABOUT, _("&About Docutils"), _("Show information about Docutils"))
210 menuBar.Append(menu, _("&Help"))
211 wx.EVT_MENU(self, wxID_USERGUIDE, self.onNotImplementedYet)
212 wx.EVT_MENU(self, wxID_ABOUT, self.onNotImplementedYet)
214 # finish setting up the menu
215 self.SetMenuBar(menuBar)
217 # Enabling and disabling options
218 menuEnablerOpen = MenuEnabler(menuBar, enablerItemList)
219 self.vsSourceOpen.registerAction(menuEnablerOpen)
220 self.vsSourceOpen.registerEditor(self, "")
222 # Setup the WorkArea
223 self.cp = ControlPanel(self, self.vsProc)
224 self.tp = TextsPanel(self, self.vsProc)
225 # box
226 allBox = wx.BoxSizer(wx.VERTICAL)
227 allBox.Add(self.cp, 0, wx.EXPAND | wx.ALL, border=5)
228 allBox.Add(self.tp, 1, wx.EXPAND | wx.ALL, border=5)
229 self.SetSizer(allBox)
231 # Final go
232 wx.EVT_CLOSE(self, self.onFileExit)
233 self.Show(True)
234 return
237 def statusBar(self, message):
238 if message == self.previousMessage:
239 return
240 self.SetStatusText(message)
241 self.previousMessage = message
242 return
244 def onNotImplementedYet(self, event):
245 self.SetStatusText(_("ERROR: Menu option not yet implemented"))
246 return
249 #~ def OnAbout(self, event):
250 #~ dlg = wx.MessageDialog(self, "This sample program shows off\n"
251 #~ "frames, menus, statusbars, and this\n"
252 #~ "message dialog.",
253 #~ "About Me", wx.ICON_EXCLAMATION | wx.ICON_INFORMATION)
254 #~ dlg.ShowModal()
255 #~ dlg.Destroy()
257 def onFileOpen(self, event, filename=None):
258 if filename is not None:
259 if not os.access(filename, os.R_OK):
260 dlg = wx.MessageDialog(self, _('Such file does not exists!'), _('Error'), wx.OK | wx.ICON_ERROR)
261 dlg.ShowModal()
262 dlg.Destroy()
263 return
264 else:
265 # open a user selected file
266 wildcard = _("Text files (*.txt)") + "|*.txt|" + \
267 _("All files (*.*)") + "|*.*"
268 dlg = wx.FileDialog(self, _("Choose a source file to process"), user.home, wildcard=wildcard, style=wx.OPEN|wx.HIDE_READONLY)
270 if dlg.ShowModal() != wx.ID_OK:
271 dlg.Destroy()
272 self.statusBar(_("The file selection was cancelled"))
273 return
275 filename = dlg.GetPath()
276 dlg.Destroy()
278 # update the GUI
279 self.tp.setSourceText(codecs.open(filename).read())
280 self.cp.teSource.SetValue(filename)
281 self.vsSourceOpen.setEditorState(self, True)
282 self.vsProc.setEditorState(self.cp.teSource, True)
283 self.statusBar(_("A new source file was opened"))
284 return
286 def onFileClose(self, event):
287 # widgets
288 self.tp.setSourceText("")
289 self.cp.teSource.SetValue("")
290 self.cp.teDestin.SetValue("")
291 # validators
292 self.vsProc.setEditorState(self.cp.teSource, False)
293 self.vsProc.setEditorState(self.cp.teDestin, False)
294 self.vsSourceOpen.setEditorState(self, False)
295 return True
297 def onFileExit(self, event):
298 self.Destroy()
300 def onDestChooser(self, event, filename=None):
301 if filename is None:
302 # let's ask the project filename
303 wildcard = _("Text files (*.txt)") + "|*.txt|" + \
304 _("All files (*.*)") + "|*.*"
305 dlg = wx.FileDialog(self, _("Choose a file to save the result"), user.home, wildcard=wildcard, style=wx.SAVE)
306 if dlg.ShowModal() != wx.ID_OK:
307 dlg.Destroy()
308 self.statusBar(_("The file selection was cancelled"))
309 return
311 filename = dlg.GetPath()
312 dlg.Destroy()
314 # append the correct extension if we need to
315 format = self.cp.rbFormat.GetStringSelection()
316 ext = FORMATS[format][1]
317 if not filename.endswith(ext):
318 filename += ext
320 # check file permission
321 if os.access(filename, os.F_OK)and not os.access(filename, os.W_OK):
322 dlg = wx.MessageDialog(self, _('Such name is not valid for a writable file!'), _('Error'), wx.OK | wx.ICON_ERROR)
323 dlg.ShowModal()
324 dlg.Destroy()
325 return
327 # update the GUI
328 self.cp.teDestin.SetValue(filename)
329 self.vsProc.setEditorState(self.cp.teDestin, True)
330 self.statusBar(_("A destination file was selected"))
331 return
333 def goProcess(self, event):
334 # gather the data
335 inp = self.cp.teSource.GetValue()
336 out = self.cp.teDestin.GetValue()
337 format = self.cp.rbFormat.GetStringSelection()
339 # process and show
340 wn = FORMATS[format][0]
341 publish_file(source_path=inp, destination_path=out, writer_name=wn)
342 self.tp.setDestinText(out)
343 return
346 class MyApp(wx.App):
347 def OnInit(self):
348 frame = MyFrame(None, -1, "Docutils")
349 frame.Show(True)
350 self.SetTopWindow(frame)
351 return True
353 if __name__ == "__main__":
354 app = MyApp(0)
355 app.MainLoop()