]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/Main.py
Add py2.4 to the pacakge name so they will be unique and both 2.3 and
[wxWidgets.git] / wxPython / demo / Main.py
CommitLineData
cf694132
RD
1#!/bin/env python
2#----------------------------------------------------------------------------
3# Name: Main.py
4# Purpose: Testing lots of stuff, controls, window types, etc.
5#
f6bcfd97 6# Author: Robin Dunn
cf694132 7#
f6bcfd97 8# Created: A long time ago, in a galaxy far, far away...
cf694132
RD
9# RCS-ID: $Id$
10# Copyright: (c) 1999 by Total Control Software
11# Licence: wxWindows license
12#----------------------------------------------------------------------------
13
08ecc920
RD
14# FIXME List:
15# * Problems with flickering related to ERASE_BACKGROUND
16# and the splitters. Might be a problem with this 2.5 beta...?
17# UPDATE: can't see on 2.5.2 GTK - maybe just a faster machine :)
18# * Demo Code menu?
19# * Annoying switching between tabs and resulting flicker
20# how to replace a page in the notebook without deleting/adding?
21# Where is SetPage!? tried freeze...tried reparent of dummy panel....
22
23# TODO List:
24# * UI design more prefessional
25# * save file positions (new field in demoModules) (@ LoadDemoSource)
26# * Update main overview
27
28# * Why don't we move _treeList into a separate module
29
30import sys, os, time, traceback, types
cf694132 31
1fded56b
RD
32import wx # This module uses the new wx namespace
33import wx.html
606d919c 34
96bfd053
RD
35import images
36
d14a1e28
RD
37# For debugging
38##wx.Trap();
ef8b9c3e 39##print "wx.VERSION_STRING = ", wx.VERSION_STRING
4276dc52 40##print os.getpid();
ef8b9c3e 41##raw_input("Press Enter...")
6fa68946 42
1e4a197e 43
cf694132
RD
44#---------------------------------------------------------------------------
45
46
47_treeList = [
9c67cbec 48 # new stuff
77d4f443 49 ('Recent Additions/Updates', [
42f5333f 50 'FoldPanelBar',
9c67cbec
RD
51 ]),
52
1e4a197e 53 # managed windows == things with a (optional) caption you can close
03a604a6 54 ('Frames and Dialogs', [
299647ac
RD
55 'Dialog',
56 'Frame',
57 'MDIWindows',
58 'MiniFrame',
59 'Wizard',
9c67cbec
RD
60 ]),
61
62 # the common dialogs
63 ('Common Dialogs', [
299647ac
RD
64 'ColourDialog',
65 'DirDialog',
66 'FileDialog',
299647ac
RD
67 'FindReplaceDialog',
68 'FontDialog',
69 'MessageDialog',
89eb18ec 70 'MultiChoiceDialog',
299647ac
RD
71 'PageSetupDialog',
72 'PrintDialog',
73 'ProgressDialog',
74 'SingleChoiceDialog',
75 'TextEntryDialog',
9c67cbec
RD
76 ]),
77
af83019e 78 # dialogs from libraries
9c67cbec 79 ('More Dialogs', [
9c67cbec 80 'ImageBrowser',
299647ac
RD
81 'MultipleChoiceDialog',
82 'ScrolledMessageDialog',
9c67cbec
RD
83 ]),
84
85 # core controls
86 ('Core Windows/Controls', [
8b7fa2d9 87 'BitmapButton',
299647ac
RD
88 'Button',
89 'CheckBox',
90 'CheckListBox',
91 'Choice',
9694b21a 92 'Choicebook',
299647ac
RD
93 'ComboBox',
94 'Gauge',
95 'Grid',
96 'Grid_MegaExample',
97 'ListBox',
98 'ListCtrl',
99 'ListCtrl_virtual',
15513a80 100 'ListCtrl_edit',
299647ac
RD
101 'Listbook',
102 'Menu',
103 'Notebook',
1fded56b 104 'PopupMenu',
299647ac
RD
105 'PopupWindow',
106 'RadioBox',
107 'RadioButton',
108 'SashWindow',
109 'ScrolledWindow',
110 'Slider',
111 'SpinButton',
112 'SpinCtrl',
113 'SplitterWindow',
114 'StaticBitmap',
e79c8a7c 115 'StaticBox',
299647ac
RD
116 'StaticText',
117 'StatusBar',
f2b4a4ea 118 'StockButtons',
299647ac
RD
119 'TextCtrl',
120 'ToggleButton',
121 'ToolBar',
122 'TreeCtrl',
123 'Validator',
9c67cbec
RD
124 ]),
125
d14a1e28
RD
126 ('Custom Controls', [
127 'AnalogClockWindow',
128 'ColourSelect',
299647ac 129 'Editor',
d14a1e28 130 'GenericButtons',
299647ac
RD
131 'GenericDirCtrl',
132 'LEDNumberCtrl',
133 'MultiSash',
134 'PopupControl',
135 'PyColourChooser',
136 'TreeListCtrl',
d14a1e28
RD
137 ]),
138
8b9a4190 139 # controls coming from other libraries
9c67cbec 140 ('More Windows/Controls', [
b7c75283
RD
141 'ActiveX_FlashWindow',
142 'ActiveX_IEHtmlWindow',
143 'ActiveX_PDFWindow',
299647ac
RD
144 #'RightTextCtrl', deprecated as we have wxTE_RIGHT now.
145 'Calendar',
146 'CalendarCtrl',
9c67cbec 147 'ContextHelp',
91334b48 148 'DatePickerCtrl',
299647ac
RD
149 'DynamicSashWindow',
150 'EditableListBox',
9c67cbec
RD
151 'FancyText',
152 'FileBrowseButton',
299647ac
RD
153 'FloatBar',
154 'FloatCanvas',
42f5333f 155 'FoldPanelBar',
299647ac 156 'HtmlWindow',
299647ac 157 'IntCtrl',
256c055e 158 'MediaCtrl',
299647ac 159 'MVCTree',
1fded56b 160 'MaskedEditControls',
299647ac
RD
161 'MaskedNumCtrl',
162 'MimeTypesManager',
9c67cbec 163 'PyCrust',
80b27b4e 164 'PyPlot',
299647ac
RD
165 'PyShell',
166 'ScrolledPanel',
9c67cbec 167 'SplitTree',
299647ac
RD
168 'StyledTextCtrl_1',
169 'StyledTextCtrl_2',
9c67cbec 170 'TablePrint',
1e4a197e 171 'Throbber',
cccce1a6 172 'Ticker',
299647ac
RD
173 'TimeCtrl',
174 'VListBox',
9c67cbec
RD
175 ]),
176
177 # How to lay out the controls in a frame/dialog
178 ('Window Layout', [
299647ac 179 'GridBagSizer',
9c67cbec 180 'LayoutAnchors',
299647ac 181 'LayoutConstraints',
9c67cbec
RD
182 'Layoutf',
183 'RowColSizer',
299647ac 184 'ScrolledPanel',
9c67cbec 185 'Sizers',
299647ac
RD
186 'XmlResource',
187 'XmlResourceHandler',
188 'XmlResourceSubclass',
9c67cbec
RD
189 ]),
190
191 # ditto
192 ('Process and Events', [
1e4a197e 193 'EventManager',
299647ac 194 'KeyEvents',
299647ac 195 'Process',
9c67cbec
RD
196 'PythonEvents',
197 'Threads',
299647ac 198 'Timer',
c163da42 199 ##'infoframe', # needs better explanation and some fixing
9c67cbec
RD
200 ]),
201
202 # Clipboard and DnD
203 ('Clipboard and DnD', [
204 'CustomDragAndDrop',
205 'DragAndDrop',
206 'URLDragAndDrop',
207 ]),
208
209 # Images
1e4a197e 210 ('Using Images', [
299647ac 211 'ArtProvider',
76343679 212 'Cursor',
299647ac
RD
213 'DragImage',
214 'Image',
8a88769e 215 'ImageAlpha',
299647ac
RD
216 'ImageFromStream',
217 'Mask',
1e4a197e 218 'Throbber',
9c67cbec
RD
219 ]),
220
221 # Other stuff
222 ('Miscellaneous', [
223 'ColourDB',
c163da42 224 ##'DialogUnits', # needs more explanations
9c67cbec 225 'DrawXXXList',
299647ac 226 'FileHistory',
9c67cbec 227 'FontEnumerator',
299647ac 228 'Joystick',
51c5e1f2 229 'MouseGestures',
299647ac 230 'OGL',
9c67cbec 231 'PrintFramework',
3628e088 232 'ShapedWindow',
78862f24 233 'Sound',
62038e59 234 'StandardPaths',
9c67cbec 235 'Unicode',
9c67cbec
RD
236 ]),
237
238 # need libs not coming with the demo
d7403ad2 239 ('Samples using an external library', [
299647ac 240 'GLCanvas',
9c67cbec
RD
241 ]),
242
70a357c2 243
9c67cbec
RD
244 ('Check out the samples dir too', [
245 ]),
246
9c67cbec
RD
247]
248
249
cf694132
RD
250
251#---------------------------------------------------------------------------
8b9a4190 252# Show how to derive a custom wxLog class
cf694132 253
1fded56b 254class MyLog(wx.PyLog):
76bfdc78 255 def __init__(self, textCtrl, logTime=0):
1fded56b 256 wx.PyLog.__init__(self)
76bfdc78
RD
257 self.tc = textCtrl
258 self.logTime = logTime
259
260 def DoLogString(self, message, timeStamp):
62038e59
RD
261 #print message, timeStamp
262 #if self.logTime:
263 # message = time.strftime("%X", time.localtime(timeStamp)) + \
264 # ": " + message
1e4a197e
RD
265 if self.tc:
266 self.tc.AppendText(message + '\n')
76bfdc78
RD
267
268
1fded56b 269class MyTP(wx.PyTipProvider):
861a0196
RD
270 def GetTip(self):
271 return "This is my tip"
272
c4ef95da
RD
273#---------------------------------------------------------------------------
274# A class to be used to simply display a message in the demo pane
275# rather than running the sample itself.
276
277class MessagePanel(wx.Panel):
278 def __init__(self, parent, message, caption='', flags=0):
279 wx.Panel.__init__(self, parent)
280
281 # Make widgets
282 if flags:
283 artid = None
284 if flags & wx.ICON_EXCLAMATION:
285 artid = wx.ART_WARNING
286 elif flags & wx.ICON_ERROR:
287 artid = wx.ART_ERROR
288 elif flags & wx.ICON_QUESTION:
289 artid = wx.ART_QUESTION
290 elif flags & wx.ICON_INFORMATION:
291 artid = wx.ART_INFORMATION
292
293 if artid is not None:
294 bmp = wx.ArtProvider.GetBitmap(artid, wx.ART_MESSAGE_BOX, (32,32))
295 icon = wx.StaticBitmap(self, -1, bmp)
296 else:
297 icon = (32,32) # make a spacer instead
298
299 if caption:
300 caption = wx.StaticText(self, -1, caption)
301 caption.SetFont(wx.Font(28, wx.SWISS, wx.NORMAL, wx.BOLD))
302
303 message = wx.StaticText(self, -1, message)
304
305 # add to sizers for layout
306 tbox = wx.BoxSizer(wx.VERTICAL)
307 if caption:
308 tbox.Add(caption)
309 tbox.Add((10,10))
310 tbox.Add(message)
311
312 hbox = wx.BoxSizer(wx.HORIZONTAL)
313 hbox.Add((10,10), 1)
314 hbox.Add(icon)
315 hbox.Add((10,10))
316 hbox.Add(tbox)
317 hbox.Add((10,10), 1)
318
319 box = wx.BoxSizer(wx.VERTICAL)
320 box.Add((10,10), 1)
321 box.Add(hbox, 0, wx.EXPAND)
322 box.Add((10,10), 2)
323
324 self.SetSizer(box)
a7593631 325 self.Fit()
c4ef95da 326
08ecc920 327
1fded56b
RD
328#---------------------------------------------------------------------------
329# A class to be used to display source code in the demo. Try using the
299647ac 330# wxSTC in the StyledTextCtrl_2 sample first, fall back to wxTextCtrl
1fded56b 331# if there is an error, such as the stc module not being present.
8b9a4190 332#
1fded56b
RD
333
334try:
4638eaea 335 ##raise ImportError # for testing the alternate implementation
1fded56b 336 from wx import stc
299647ac 337 from StyledTextCtrl_2 import PythonSTC
08ecc920
RD
338
339 class DemoCodeEditor(PythonSTC):
340 def __init__(self, parent):
2e839e96 341 PythonSTC.__init__(self, parent, -1, style=wx.BORDER_NONE)
80b27b4e 342 self.SetUpEditor()
1fded56b
RD
343
344 # Some methods to make it compatible with how the wxTextCtrl is used
345 def SetValue(self, value):
6c7d1792
RD
346 if wx.USE_UNICODE:
347 value = value.decode('iso8859_1')
1fded56b 348 self.SetText(value)
08ecc920
RD
349 self.EmptyUndoBuffer()
350 self.SetSavePoint()
351
352 def IsModified(self):
353 return self.GetModify()
1fded56b
RD
354
355 def Clear(self):
356 self.ClearAll()
357
358 def SetInsertionPoint(self, pos):
359 self.SetCurrentPos(pos)
08ecc920 360 self.SetAnchor(pos)
1fded56b
RD
361
362 def ShowPosition(self, pos):
08ecc920 363 line = self.LineFromPosition(pos)
887a1bd9
RD
364 #self.EnsureVisible(line)
365 self.GotoLine(line)
1fded56b
RD
366
367 def GetLastPosition(self):
368 return self.GetLength()
369
08ecc920
RD
370 def GetPositionFromLine(self, line):
371 return self.PositionFromLine(line)
372
1fded56b
RD
373 def GetRange(self, start, end):
374 return self.GetTextRange(start, end)
375
376 def GetSelection(self):
377 return self.GetAnchor(), self.GetCurrentPos()
378
379 def SetSelection(self, start, end):
380 self.SetSelectionStart(start)
381 self.SetSelectionEnd(end)
382
08ecc920
RD
383 def SelectLine(self, line):
384 start = self.PositionFromLine(line)
385 end = self.GetLineEndPosition(line)
386 self.SetSelection(start, end)
387
80b27b4e
RD
388 def SetUpEditor(self):
389 """
390 This method carries out the work of setting up the demo editor.
391 It's seperate so as not to clutter up the init code.
392 """
393 import keyword
394
395 self.SetLexer(stc.STC_LEX_PYTHON)
396 self.SetKeyWords(0, " ".join(keyword.kwlist))
397
398 # Enable folding
399 self.SetProperty("fold", "1" )
400
401 # Highlight tab/space mixing (shouldn't be any)
402 self.SetProperty("tab.timmy.whinge.level", "1")
403
404 # Set left and right margins
405 self.SetMargins(2,2)
406
407 # Set up the numbers in the margin for margin #1
408 self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
409 # Reasonable value for, say, 4-5 digits using a mono font (40 pix)
410 self.SetMarginWidth(1, 40)
411
412 # Indentation and tab stuff
413 self.SetIndent(4) # Proscribed indent size for wx
414 self.SetIndentationGuides(True) # Show indent guides
415 self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space
416 self.SetTabIndents(True) # Tab key indents
417 self.SetTabWidth(4) # Proscribed tab size for wx
418 self.SetUseTabs(False) # Use spaces rather than tabs, or
419 # TabTimmy will complain!
420 # White space
421 self.SetViewWhiteSpace(False) # Don't view white space
422
887a1bd9
RD
423 # EOL: Since we are loading/saving ourselves, and the
424 # strings will always have \n's in them, set the STC to
425 # edit them that way.
426 self.SetEOLMode(wx.stc.STC_EOL_LF)
427 self.SetViewEOL(False)
428
80b27b4e
RD
429 # No right-edge mode indicator
430 self.SetEdgeMode(stc.STC_EDGE_NONE)
431
432 # Setup a margin to hold fold markers
433 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
434 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
435 self.SetMarginSensitive(2, True)
436 self.SetMarginWidth(2, 12)
437
438 # and now set up the fold markers
439 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "black")
440 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "black")
441 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "black")
442 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "black")
443 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "black")
444 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "black")
445 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "black")
446
447 # Global default style
448 if wx.Platform == '__WXMSW__':
449 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
450 'fore:#000000,back:#FFFFFF,face:Courier New,size:9')
451 else:
452 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
453 'fore:#000000,back:#FFFFFF,face:Courier,size:12')
454
455 # Clear styles and revert to default.
456 self.StyleClearAll()
457
458 # Following style specs only indicate differences from default.
459 # The rest remains unchanged.
460
461 # Line numbers in margin
e44b5196 462 self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2')
80b27b4e
RD
463 # Highlighted brace
464 self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
465 # Unmatched brace
466 self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
467 # Indentation guide
468 self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")
469
470 # Python styles
471 self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
472 # Comments
a253aa20
RD
473 self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0')
474 self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
80b27b4e
RD
475 # Numbers
476 self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080')
477 # Strings and characters
478 self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#800080')
479 self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#800080')
480 # Keywords
481 self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
482 # Triple quotes
483 self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
484 self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
485 # Class names
486 self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
487 # Function names
488 self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
489 # Operators
490 self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold')
491 # Identifiers. I leave this as not bold because everything seems
492 # to be an identifier if it doesn't match the above criterae
493 self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000')
494
495 # Caret color
496 self.SetCaretForeground("BLUE")
497 # Selection background
498 self.SetSelBackground(1, '#66CCFF')
499
500 self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
501 self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
502
08ecc920
RD
503 def RegisterModifiedEvent(self, eventHandler):
504 self.Bind(wx.stc.EVT_STC_CHANGE, eventHandler)
505
1fded56b
RD
506
507except ImportError:
08ecc920
RD
508 class DemoCodeEditor(wx.TextCtrl):
509 def __init__(self, parent):
2e839e96
RD
510 wx.TextCtrl.__init__(self, parent, -1, style =
511 wx.TE_MULTILINE | wx.HSCROLL | wx.TE_RICH2 | wx.TE_NOHIDESEL)
1fded56b 512
08ecc920
RD
513 def RegisterModifiedEvent(self, eventHandler):
514 self.Bind(wx.EVT_TEXT, eventHandler)
515
516 def SetReadOnly(self, flag):
517 self.SetEditable(not flag)
518 # NOTE: STC already has this method
519
520 def GetText(self):
521 return self.GetValue()
522
b6176ab7 523 def GetPositionFromLine(self, line):
08ecc920
RD
524 return self.XYToPosition(0,line)
525
526 def GotoLine(self, line):
b6176ab7
RD
527 pos = self.GetPositionFromLine(line)
528 self.SetInsertionPoint(pos)
529 self.ShowPosition(pos)
08ecc920
RD
530
531 def SelectLine(self, line):
532 start = self.GetPositionFromLine(line)
533 end = start + self.GetLineLength(line)
534 self.SetSelection(start, end)
535
536
537#---------------------------------------------------------------------------
538# Constants for module versions
539
540modOriginal = 0
541modModified = 1
542modDefault = modOriginal
543
544#---------------------------------------------------------------------------
545
546class DemoCodePanel(wx.Panel):
547 """Panel for the 'Demo Code' tab"""
548 def __init__(self, parent, mainFrame):
2e839e96 549 wx.Panel.__init__(self, parent, size=(1,1))
e0465cbd
RD
550 if 'wxMSW' in wx.PlatformInfo:
551 self.Hide()
08ecc920
RD
552 self.mainFrame = mainFrame
553 self.editor = DemoCodeEditor(self)
554 self.editor.RegisterModifiedEvent(self.OnCodeModified)
555
556 self.btnSave = wx.Button(self, -1, "Save Changes")
557 self.btnRestore = wx.Button(self, -1, "Delete Modified")
558 self.btnSave.Enable(False)
559 self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
560 self.btnRestore.Bind(wx.EVT_BUTTON, self.OnRestore)
561
562 self.radioButtons = { modOriginal: wx.RadioButton(self, -1, "Original", style = wx.RB_GROUP),
563 modModified: wx.RadioButton(self, -1, "Modified") }
564
565 self.controlBox = wx.BoxSizer(wx.HORIZONTAL)
566 self.controlBox.Add(wx.StaticText(self, -1, "Active Version:"), 0,
567 wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
568 for modID, radioButton in self.radioButtons.items():
569 self.controlBox.Add(radioButton, 0, wx.EXPAND | wx.RIGHT, 5)
570 radioButton.modID = modID # makes it easier for the event handler
571 radioButton.Bind(wx.EVT_RADIOBUTTON, self.OnRadioButton)
572
573 self.controlBox.Add(self.btnSave, 0, wx.RIGHT, 5)
574 self.controlBox.Add(self.btnRestore, 0)
575
576 self.box = wx.BoxSizer(wx.VERTICAL)
577 self.box.Add(self.controlBox, 0, wx.EXPAND)
887a1bd9 578 self.box.Add(wx.StaticLine(self), 0, wx.EXPAND)
08ecc920
RD
579 self.box.Add(self.editor, 1, wx.EXPAND)
580
581 self.box.Fit(self)
582 self.SetSizer(self.box)
583
584
585 # Loads a demo from a DemoModules object
586 def LoadDemo(self, demoModules):
587 self.demoModules = demoModules
588 if (modDefault == modModified) and demoModules.Exists(modModified):
589 demoModules.SetActive(modModified)
590 else:
591 demoModules.SetActive(modOriginal)
592 self.radioButtons[demoModules.GetActiveID()].Enable(True)
593 self.ActiveModuleChanged()
594
595
596 def ActiveModuleChanged(self):
597 self.LoadDemoSource(self.demoModules.GetSource())
598 self.UpdateControlState()
a6780fa2 599 self.ReloadDemo()
08ecc920
RD
600
601
602 def LoadDemoSource(self, source):
603 self.editor.Clear()
604 self.editor.SetValue(source)
605 self.JumpToLine(0)
606 self.btnSave.Enable(False)
607
608
609 def JumpToLine(self, line, highlight=False):
610 self.editor.GotoLine(line)
611 self.editor.SetFocus()
612 if highlight:
613 self.editor.SelectLine(line)
614
615
616 def UpdateControlState(self):
617 active = self.demoModules.GetActiveID()
618 # Update the radio/restore buttons
619 for moduleID in self.radioButtons:
620 btn = self.radioButtons[moduleID]
621 if moduleID == active:
622 btn.SetValue(True)
623 else:
624 btn.SetValue(False)
625
626 if self.demoModules.Exists(moduleID):
627 btn.Enable(True)
628 if moduleID == modModified:
629 self.btnRestore.Enable(True)
630 else:
631 btn.Enable(False)
632 if moduleID == modModified:
633 self.btnRestore.Enable(False)
634
635
636 def OnRadioButton(self, event):
637 radioSelected = event.GetEventObject()
638 modSelected = radioSelected.modID
639 if modSelected != self.demoModules.GetActiveID():
640 busy = wx.BusyInfo("Reloading demo module...")
641 self.demoModules.SetActive(modSelected)
642 self.ActiveModuleChanged()
643
644
645 def ReloadDemo(self):
646 if self.demoModules.name != __name__:
a6780fa2 647 self.mainFrame.RunModule()
08ecc920
RD
648
649
650 def OnCodeModified(self, event):
651 self.btnSave.Enable(self.editor.IsModified())
652
653
654 def OnSave(self, event):
655 if self.demoModules.Exists(modModified):
656 if self.demoModules.GetActiveID() == modOriginal:
657 overwriteMsg = "You are about to overwrite an already existing modified copy\n" + \
658 "Do you want to continue?"
659 dlg = wx.MessageDialog(self, overwriteMsg, "wxPython Demo",
660 wx.YES_NO | wx.NO_DEFAULT| wx.ICON_EXCLAMATION)
661 result = dlg.ShowModal()
662 if result == wx.ID_NO:
663 return
664 dlg.Destroy()
665
666 self.demoModules.SetActive(modModified)
667 modifiedFilename = GetModifiedFilename(self.demoModules.name)
668
669 # Create the demo directory if one doesn't already exist
670 if not os.path.exists(GetModifiedDirectory()):
671 try:
672 os.makedirs(GetModifiedDirectory())
673 if not os.path.exists(GetModifiedDirectory()):
c163da42 674 wx.LogMessage("BUG: Created demo directory but it still doesn't exist")
08ecc920
RD
675 raise AssetionError
676 except:
677 wx.LogMessage("Error creating demo directory: %s" % GetModifiedDirectory())
678 return
679 else:
680 wx.LogMessage("Created directory for modified demos: %s" % GetModifiedDirectory())
681
682 # Save
887a1bd9 683 f = open(modifiedFilename, "wt")
08ecc920
RD
684 source = self.editor.GetText()
685 try:
686 f.write(source)
687 finally:
688 f.close()
689
690 busy = wx.BusyInfo("Reloading demo module...")
691 self.demoModules.LoadFromFile(modModified, modifiedFilename)
692 self.ActiveModuleChanged()
693
694
695 def OnRestore(self, event): # Handles the "Delete Modified" button
696 modifiedFilename = GetModifiedFilename(self.demoModules.name)
697 self.demoModules.Delete(modModified)
698 os.unlink(modifiedFilename) # Delete the modified copy
699 busy = wx.BusyInfo("Reloading demo module...")
700 self.ActiveModuleChanged()
701
1fded56b 702
6c5ae2d2
RD
703#---------------------------------------------------------------------------
704
705def opj(path):
706 """Convert paths to the platform-specific separator"""
08ecc920
RD
707 str = apply(os.path.join, tuple(path.split('/')))
708 # HACK: on Linux, a leading / gets lost...
709 if path.startswith('/'):
710 str = '/' + str
711 return str
712
713
714def GetModifiedDirectory():
715 """
716 Returns the directory where modified versions of the demo files
717 are stored
718 """
719 return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
720
721
722def GetModifiedFilename(name):
723 """
724 Returns the filename of the modified version of the specified demo
725 """
726 if not name.endswith(".py"):
727 name = name + ".py"
728 return GetModifiedDirectory() + name
729
730
731def GetOriginalFilename(name):
732 """
733 Returns the filename of the original version of the specified demo
734 """
735 if not name.endswith(".py"):
736 name = name + ".py"
737 return name
738
739
740def DoesModifiedExist(name):
741 """Returns whether the specified demo has a modified copy"""
742 if os.path.exists(GetModifiedFilename(name)):
743 return True
744 else:
745 return False
746
747
748#---------------------------------------------------------------------------
749
750class ModuleDictWrapper:
751 """Emulates a module with a dynamically compiled __dict__"""
752 def __init__(self, dict):
753 self.dict = dict
754
755 def __getattr__(self, name):
756 if name in self.dict:
757 return self.dict[name]
758 else:
759 raise AttributeError
760
761class DemoModules:
762 """
763 Dynamically manages the original/modified versions of a demo
764 module
765 """
766 def __init__(self, name):
767 self.modActive = -1
768 self.name = name
769
770 # (dict , source , filename , description , error information )
771 # ( 0 , 1 , 2 , 3 , 4 )
772 self.modules = [[None, "" , "" , "<original>" , None],
773 [None, "" , "" , "<modified>" , None]]
774
775 # load original module
776 self.LoadFromFile(modOriginal, GetOriginalFilename(name))
317a64e5 777 self.SetActive(modOriginal)
08ecc920
RD
778
779 # load modified module (if one exists)
780 if DoesModifiedExist(name):
781 self.LoadFromFile(modModified, GetModifiedFilename(name))
782
783
784 def LoadFromFile(self, modID, filename):
785 self.modules[modID][2] = filename
887a1bd9 786 file = open(filename, "rt")
08ecc920
RD
787 self.LoadFromSource(modID, file.read())
788 file.close()
789
790
791 def LoadFromSource(self, modID, source):
792 self.modules[modID][1] = source
793 self.LoadDict(modID)
794
795
796 def LoadDict(self, modID):
797 if self.name != __name__:
798 source = self.modules[modID][1]
799 description = self.modules[modID][3]
800
801 try:
802 self.modules[modID][0] = {}
803 code = compile(source, description, "exec")
804 exec code in self.modules[modID][0]
805 except:
806 self.modules[modID][4] = DemoError(sys.exc_info())
807 self.modules[modID][0] = None
808 else:
809 self.modules[modID][4] = None
810
811
812 def SetActive(self, modID):
813 if modID != modOriginal and modID != modModified:
814 raise LookupError
815 else:
816 self.modActive = modID
817
818
819 def GetActive(self):
820 dict = self.modules[self.modActive][0]
821 if dict is None:
822 return None
823 else:
824 return ModuleDictWrapper(dict)
825
826
827 def GetActiveID(self):
828 return self.modActive
829
830
831 def GetSource(self, modID = None):
832 if modID is None:
833 modID = self.modActive
834 return self.modules[modID][1]
835
836
837 def GetFilename(self, modID = None):
838 if modID is None:
839 modID = self.modActive
840 return self.modules[self.modActive][2]
841
842
843 def GetErrorInfo(self, modID = None):
844 if modID is None:
845 modID = self.modActive
846 return self.modules[self.modActive][4]
847
848
849 def Exists(self, modID):
850 return self.modules[modID][1] != ""
851
852
853 def UpdateFile(self, modID = None):
854 """Updates the file from which a module was loaded
855 with (possibly updated) source"""
856 if modID is None:
857 modID = self.modActive
858
859 source = self.modules[modID][1]
860 filename = self.modules[modID][2]
861
862 try:
887a1bd9 863 file = open(filename, "wt")
08ecc920
RD
864 file.write(source)
865 finally:
866 file.close()
867
868
869 def Delete(self, modID):
870 if self.modActive == modID:
871 self.SetActive(0)
872
873 self.modules[modID][0] = None
874 self.modules[modID][1] = ""
875 self.modules[modID][2] = ""
876
877
08ecc920
RD
878#---------------------------------------------------------------------------
879
880class DemoError:
881 """Wraps and stores information about the current exception"""
882 def __init__(self, exc_info):
883 import copy
884
885 excType, excValue = exc_info[:2]
886 # traceback list entries: (filename, line number, function name, text)
887 self.traceback = traceback.extract_tb(exc_info[2])
888
889 # --Based on traceback.py::format_exception_only()--
890 if type(excType) == types.ClassType:
891 self.exception_type = excType.__name__
892 else:
893 self.exception_type = excType
894
895 # If it's a syntax error, extra information needs
896 # to be added to the traceback
897 if excType is SyntaxError:
898 try:
899 msg, (filename, lineno, self.offset, line) = excValue
900 except:
901 pass
902 else:
903 if not filename:
904 filename = "<string>"
905 line = line.strip()
906 self.traceback.append( (filename, lineno, "", line) )
907 excValue = msg
908 try:
909 self.exception_details = str(excValue)
910 except:
911 self.exception_details = "<unprintable %s object>" & type(excValue).__name__
912
913 del exc_info
914
915 def __str__(self):
916 ret = "Type %s \n \
917 Traceback: %s \n \
918 Details : %s" % ( str(self.exception_type), str(self.traceback), self.exception_details )
919 return ret
920
921#---------------------------------------------------------------------------
922
923class DemoErrorPanel(wx.Panel):
924 """Panel put into the demo tab when the demo fails to run due to errors"""
925
926 def __init__(self, parent, codePanel, demoError, log):
927 wx.Panel.__init__(self, parent, -1)#, style=wx.NO_FULL_REPAINT_ON_RESIZE)
928 self.codePanel = codePanel
929 self.nb = parent
930 self.log = log
931
932 self.box = wx.BoxSizer(wx.VERTICAL)
933
934 # Main Label
935 self.box.Add(wx.StaticText(self, -1, "An error has occured while trying to run the demo")
936 , 0, wx.ALIGN_CENTER | wx.TOP, 10)
937
938 # Exception Information
939 boxInfo = wx.StaticBox(self, -1, "Exception Info" )
940 boxInfoSizer = wx.StaticBoxSizer(boxInfo, wx.VERTICAL ) # Used to center the grid within the box
941 boxInfoGrid = wx.FlexGridSizer(0, 2, 0, 0)
942 textFlags = wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.TOP
943 boxInfoGrid.Add(wx.StaticText(self, -1, "Type: "), 0, textFlags, 5 )
944 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_type) , 0, textFlags, 5 )
945 boxInfoGrid.Add(wx.StaticText(self, -1, "Details: ") , 0, textFlags, 5 )
946 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_details) , 0, textFlags, 5 )
947 boxInfoSizer.Add(boxInfoGrid, 0, wx.ALIGN_CENTRE | wx.ALL, 5 )
948 self.box.Add(boxInfoSizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
949
950 # Set up the traceback list
951 # This one automatically resizes last column to take up remaining space
952 from ListCtrl import TestListCtrl
953 self.list = TestListCtrl(self, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
954 self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
955 self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
956 self.list.InsertColumn(0, "Filename")
957 self.list.InsertColumn(1, "Line", wx.LIST_FORMAT_RIGHT)
958 self.list.InsertColumn(2, "Function")
959 self.list.InsertColumn(3, "Code")
960 self.InsertTraceback(self.list, demoError.traceback)
961 self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
962 self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
963 self.box.Add(wx.StaticText(self, -1, "Traceback:")
964 , 0, wx.ALIGN_CENTER | wx.TOP, 5)
965 self.box.Add(self.list, 1, wx.GROW | wx.ALIGN_CENTER | wx.ALL, 5)
966 self.box.Add(wx.StaticText(self, -1, "Entries from the demo module are shown in blue\n"
967 + "Double-click on them to go to the offending line")
968 , 0, wx.ALIGN_CENTER | wx.BOTTOM, 5)
969
970 self.box.Fit(self)
971 self.SetSizer(self.box)
972
973
974 def InsertTraceback(self, list, traceback):
975 #Add the traceback data
976 for x in range(len(traceback)):
977 data = traceback[x]
978 list.InsertStringItem(x, os.path.basename(data[0])) # Filename
979 list.SetStringItem(x, 1, str(data[1])) # Line
980 list.SetStringItem(x, 2, str(data[2])) # Function
981 list.SetStringItem(x, 3, str(data[3])) # Code
982
983 # Check whether this entry is from the demo module
984 if data[0] == "<original>" or data[0] == "<modified>": # FIXME: make more generalised
985 self.list.SetItemData(x, int(data[1])) # Store line number for easy access
986 # Give it a blue colour
987 item = self.list.GetItem(x)
988 item.SetTextColour(wx.BLUE)
989 self.list.SetItem(item)
990 else:
991 self.list.SetItemData(x, -1) # Editor can't jump into this one's code
992
993
994 def OnItemSelected(self, event):
995 # This occurs before OnDoubleClick and can be used to set the
996 # currentItem. OnDoubleClick doesn't get a wxListEvent....
997 self.currentItem = event.m_itemIndex
998 event.Skip()
999
1000
1001 def OnDoubleClick(self, event):
1002 # If double-clicking on a demo's entry, jump to the line number
1003 line = self.list.GetItemData(self.currentItem)
1004 if line != -1:
1005 self.nb.SetSelection(1) # Switch to the code viewer tab
1006 wx.CallAfter(self.codePanel.JumpToLine, line-1, True)
1007 event.Skip()
1008
6c5ae2d2 1009
76bfdc78
RD
1010#---------------------------------------------------------------------------
1011
1cc18e15
RD
1012class DemoTaskBarIcon(wx.TaskBarIcon):
1013 TBMENU_RESTORE = wx.NewId()
1014 TBMENU_CLOSE = wx.NewId()
1015 TBMENU_CHANGE = wx.NewId()
1016 TBMENU_REMOVE = wx.NewId()
1017
1018 def __init__(self, frame):
1019 wx.TaskBarIcon.__init__(self)
1020 self.frame = frame
1021
1022 # Set the image
8596dc0b 1023 icon = self.MakeIcon(images.getWXPdemoImage())
1cc18e15 1024 self.SetIcon(icon, "wxPython Demo")
8596dc0b 1025 self.imgidx = 1
1cc18e15
RD
1026
1027 # bind some events
1028 self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
1029 self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
1030 self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
1031 self.Bind(wx.EVT_MENU, self.OnTaskBarChange, id=self.TBMENU_CHANGE)
1032 self.Bind(wx.EVT_MENU, self.OnTaskBarRemove, id=self.TBMENU_REMOVE)
1033
1034
1035 def CreatePopupMenu(self):
1036 """
1037 This method is called by the base class when it needs to popup
1038 the menu for the default EVT_RIGHT_DOWN event. Just create
1039 the menu how you want it and return it from this function,
1040 the base class takes care of the rest.
1041 """
1042 menu = wx.Menu()
1043 menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
1044 menu.Append(self.TBMENU_CLOSE, "Close wxPython Demo")
1045 menu.AppendSeparator()
1046 menu.Append(self.TBMENU_CHANGE, "Change the TB Icon")
1047 menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
1048 return menu
1049
1050
1051 def MakeIcon(self, img):
1052 """
1053 The various platforms have different requirements for the
1054 icon size...
1055 """
1056 if "wxMSW" in wx.PlatformInfo:
8596dc0b 1057 img = img.Scale(16, 16)
1cc18e15 1058 elif "wxGTK" in wx.PlatformInfo:
8596dc0b
RD
1059 img = img.Scale(22, 22)
1060 # wxMac can be any size upto 128x128, so leave the source img alone....
1cc18e15
RD
1061 icon = wx.IconFromBitmap(img.ConvertToBitmap() )
1062 return icon
1063
1064
1065 def OnTaskBarActivate(self, evt):
1066 if self.frame.IsIconized():
1067 self.frame.Iconize(False)
1068 if not self.frame.IsShown():
1069 self.frame.Show(True)
1070 self.frame.Raise()
1071
1072
1073 def OnTaskBarClose(self, evt):
1074 self.frame.Close()
1075
1cc18e15
RD
1076
1077 def OnTaskBarChange(self, evt):
8596dc0b
RD
1078 names = [ "WXPdemo", "WXP", "Mondrian", "Test2m",
1079 "Blom08m", "Blom10m", "Blom15m" ]
1080 name = names[self.imgidx]
1081
1082 getFunc = getattr(images, "get%sImage" % name)
1083 self.imgidx += 1
1084 if self.imgidx >= len(names):
1085 self.imgidx = 0
1086
1087 icon = self.MakeIcon(getFunc())
1088 self.SetIcon(icon, "This is a new icon: " + name)
1cc18e15
RD
1089
1090
1091 def OnTaskBarRemove(self, evt):
1092 self.RemoveIcon()
1093
1094
1095#---------------------------------------------------------------------------
1fded56b 1096class wxPythonDemo(wx.Frame):
e9159fe8 1097 overviewText = "wxPython Overview"
c368d904 1098
08ecc920 1099 def __init__(self, parent, title):
8ee202f3 1100 wx.Frame.__init__(self, parent, -1, title, size = (950, 720),
08ecc920 1101 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
2f90df85 1102
f290d1c7
RD
1103 self.SetMinSize((640,480))
1104
08ecc920 1105 self.loaded = False
694759cf 1106 self.cwd = os.getcwd()
3ca6a5f0 1107 self.curOverview = ""
08ecc920
RD
1108 self.demoPage = None
1109 self.codePage = None
887a1bd9 1110 self.shell = None
a6780fa2 1111 self.firstTime = True
df5204e0 1112 self.finddlg = None
694759cf 1113
8596dc0b 1114 icon = images.getWXPdemoIcon()
96bfd053 1115 self.SetIcon(icon)
c368d904 1116
1cc18e15 1117 self.tbicon = DemoTaskBarIcon(self)
c368d904 1118
1fded56b 1119 wx.CallAfter(self.ShowTip)
cf694132
RD
1120
1121 self.otherWin = None
80b27b4e
RD
1122 self.Bind(wx.EVT_IDLE, self.OnIdle)
1123 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
1124 self.Bind(wx.EVT_ICONIZE, self.OnIconfiy)
1125 self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
cf694132 1126
1fded56b
RD
1127 self.Centre(wx.BOTH)
1128 self.CreateStatusBar(1, wx.ST_SIZEGRIP)
5a7823f5 1129
0c8f2860
RD
1130 splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
1131 splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
5a7823f5 1132
d56cebe7 1133 def EmptyHandler(evt): pass
80b27b4e
RD
1134 #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1135 #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
cf694132 1136
1e4a197e
RD
1137 # Prevent TreeCtrl from displaying all items after destruction when True
1138 self.dying = False
cf694132 1139
08ecc920
RD
1140 # Create a Notebook
1141 self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
1142
cf694132 1143 # Make a File menu
1fded56b
RD
1144 self.mainmenu = wx.MenuBar()
1145 menu = wx.Menu()
330af869
RD
1146 item = menu.Append(-1, '&Redirect Output',
1147 'Redirect print statements to a window',
1148 wx.ITEM_CHECK)
1149 self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
08ecc920 1150
c8000995
RD
1151 item = menu.Append(-1, 'E&xit\tAlt-X', 'Get the heck outta here!')
1152 self.Bind(wx.EVT_MENU, self.OnFileExit, item)
05871e13 1153 wx.App.SetMacExitMenuItemId(item.GetId())
cf694132
RD
1154 self.mainmenu.Append(menu, '&File')
1155
ec3e670f 1156 # Make a Demo menu
1fded56b 1157 menu = wx.Menu()
ec3e670f 1158 for item in _treeList:
1fded56b 1159 submenu = wx.Menu()
ec3e670f 1160 for childItem in item[1]:
c8000995
RD
1161 mi = submenu.Append(-1, childItem)
1162 self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
1fded56b 1163 menu.AppendMenu(wx.NewId(), item[0], submenu)
ec3e670f
RD
1164 self.mainmenu.Append(menu, '&Demo')
1165
08ecc920
RD
1166 # Make a Demo Code menu
1167 #TODO: Add new menu items
1168 # Like the option-enabled entries to select the
1169 # active module
1170 #TODO: should we bother?
1171
1172 #menu = wx.Menu()
1173 #saveID = wx.NewId()
1174 #restoreID = wx.NewId()
1175 #
1176 #menu.Append(saveID, '&Save\tCtrl-S', 'Save edited demo')
1177 #menu.Append(restoreID, '&Delete Modified\tCtrl-R', 'Delete modified copy')
1178 #self.Bind(wx.EVT_MENU, self.codePage.OnSave, id=saveID)
1179 #self.Bind(wx.EVT_MENU, self.codePage.OnRestore, id=restoreID)
1180 #self.mainmenu.Append(menu, 'Demo &Code')
1181 #
ec3e670f 1182
cf694132 1183 # Make a Help menu
1fded56b 1184 menu = wx.Menu()
c8000995
RD
1185 findItem = menu.Append(-1, '&Find\tCtrl-F', 'Find in the Demo Code')
1186 findnextItem = menu.Append(-1, 'Find &Next\tF3', 'Find Next')
1e4a197e 1187 menu.AppendSeparator()
887a1bd9
RD
1188
1189 shellItem = menu.Append(-1, 'Open Py&Shell Window\tF5',
1190 'An interactive interpreter window with the demo app and frame objects in the namesapce')
1191 menu.AppendSeparator()
c8000995 1192 helpItem = menu.Append(-1, '&About\tCtrl-H', 'wxPython RULES!!!')
05871e13 1193 wx.App.SetMacAboutMenuItemId(helpItem.GetId())
887a1bd9
RD
1194
1195 self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
c8000995
RD
1196 self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
1197 self.Bind(wx.EVT_MENU, self.OnHelpFind, findItem)
1198 self.Bind(wx.EVT_MENU, self.OnFindNext, findnextItem)
df5204e0
RD
1199 self.Bind(wx.EVT_FIND, self.OnFind)
1200 self.Bind(wx.EVT_FIND_NEXT, self.OnFind)
1201 self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
1202 self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findItem)
1203 self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findnextItem)
cf694132
RD
1204 self.mainmenu.Append(menu, '&Help')
1205 self.SetMenuBar(self.mainmenu)
1206
1fded56b 1207 self.finddata = wx.FindReplaceData()
1e4a197e
RD
1208
1209 if 0:
1210 # This is another way to set Accelerators, in addition to
1211 # using the '\t<key>' syntax in the menu items.
1fded56b
RD
1212 aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitID),
1213 (wx.ACCEL_CTRL, ord('H'), helpID),
1214 (wx.ACCEL_CTRL, ord('F'), findID),
1215 (wx.ACCEL_NORMAL, WXK_F3, findnextID)
1216 ])
1e4a197e 1217 self.SetAcceleratorTable(aTable)
2f90df85 1218
bb0054cd 1219
cf694132 1220 # Create a TreeCtrl
1fded56b 1221 tID = wx.NewId()
f6bcfd97 1222 self.treeMap = {}
3628e088
RD
1223 self.tree = wx.TreeCtrl(splitter, tID, style =
1224 wx.TR_DEFAULT_STYLE #| wx.TR_HAS_VARIABLE_ROW_HEIGHT
eb0f373c 1225 )
afb810d9 1226
e9159fe8 1227 root = self.tree.AddRoot("wxPython Overview")
f6bcfd97
BP
1228 firstChild = None
1229 for item in _treeList:
1230 child = self.tree.AppendItem(root, item[0])
1231 if not firstChild: firstChild = child
1232 for childItem in item[1]:
1233 theDemo = self.tree.AppendItem(child, childItem)
1234 self.treeMap[childItem] = theDemo
1235
1236 self.tree.Expand(root)
1237 self.tree.Expand(firstChild)
80b27b4e
RD
1238 self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
1239 self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
1240 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
1241 self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
cf694132 1242
1fded56b 1243 # Set up a wx.html.HtmlWindow on the Overview Notebook page
0bdca46d
RD
1244 # we put it in a panel first because there seems to be a
1245 # refresh bug of some sort (wxGTK) when it is directly in
1246 # the notebook...
1247 if 0: # the old way
1fded56b 1248 self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400))
0bdca46d
RD
1249 self.nb.AddPage(self.ovr, self.overviewText)
1250
1fded56b
RD
1251 else: # hopefully I can remove this hacky code soon, see SF bug #216861
1252 panel = wx.Panel(self.nb, -1, style=wx.CLIP_CHILDREN)
1253 self.ovr = wx.html.HtmlWindow(panel, -1, size=(400, 400))
0bdca46d
RD
1254 self.nb.AddPage(panel, self.overviewText)
1255
1256 def OnOvrSize(evt, ovr=self.ovr):
1257 ovr.SetSize(evt.GetSize())
80b27b4e
RD
1258 panel.Bind(wx.EVT_SIZE, OnOvrSize)
1259 panel.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
0bdca46d 1260
08ecc920 1261 if "gtk2" in wx.PlatformInfo:
385721a8 1262 self.ovr.SetStandardFonts()
08ecc920 1263 self.SetOverview(self.overviewText, mainOverview)
c368d904 1264
cf694132 1265
08ecc920
RD
1266 # Set up a log window
1267 self.log = wx.TextCtrl(splitter2, -1,
1268 style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
1269
1270 # Set the wxWindows log target to be this textctrl
1271 #wx.Log_SetActiveTarget(wx.LogTextCtrl(self.log))
cf694132 1272
08ecc920
RD
1273 # But instead of the above we want to show how to use our own wx.Log class
1274 wx.Log_SetActiveTarget(MyLog(self.log))
1275
1276 # for serious debugging
1277 #wx.Log_SetActiveTarget(wx.LogStderr())
1278 #wx.Log_SetTraceMask(wx.TraceMessages)
cf694132
RD
1279
1280
e44b5196
RD
1281 self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
1282 wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate)
1283
f6bcfd97 1284 # add the windows to the splitter and split it.
08ecc920
RD
1285 splitter2.SplitHorizontally(self.nb, self.log, -160)
1286 splitter.SplitVertically(self.tree, splitter2, 200)
afb810d9 1287
f290d1c7
RD
1288 splitter.SetMinimumPaneSize(120)
1289 splitter2.SetMinimumPaneSize(60)
afb810d9 1290
11926a78 1291 # Make the splitter on the right expand the top window when resized
2f0f3b0f
RD
1292 def SplitterOnSize(evt):
1293 splitter = evt.GetEventObject()
1294 sz = splitter.GetSize()
08ecc920 1295 splitter.SetSashPosition(sz.height - 160, False)
2f0f3b0f 1296 evt.Skip()
80b27b4e
RD
1297
1298 splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
2f0f3b0f 1299
bb0054cd
RD
1300 # select initial items
1301 self.nb.SetSelection(0)
f6bcfd97 1302 self.tree.SelectItem(root)
ec3e670f 1303
08ecc920
RD
1304 # Load 'Main' module
1305 self.LoadDemo(self.overviewText)
1306 self.loaded = True
1307
1308 # select some other initial module?
1309 if len(sys.argv) > 1:
1310 arg = sys.argv[1]
1311 if arg.endswith('.py'):
1312 arg = arg[:-3]
1313 selectedDemo = self.treeMap.get(arg, None)
f6bcfd97 1314 if selectedDemo:
ec3e670f
RD
1315 self.tree.SelectItem(selectedDemo)
1316 self.tree.EnsureVisible(selectedDemo)
1317
bb0054cd 1318
cf694132
RD
1319 #---------------------------------------------
1320 def WriteText(self, text):
f6bcfd97
BP
1321 if text[-1:] == '\n':
1322 text = text[:-1]
1fded56b 1323 wx.LogMessage(text)
f6bcfd97 1324
cf694132
RD
1325 def write(self, txt):
1326 self.WriteText(txt)
1327
1328 #---------------------------------------------
1329 def OnItemExpanded(self, event):
1330 item = event.GetItem()
1fded56b 1331 wx.LogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item))
c368d904 1332 event.Skip()
cf694132
RD
1333
1334 #---------------------------------------------
1335 def OnItemCollapsed(self, event):
1336 item = event.GetItem()
1fded56b 1337 wx.LogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item))
c368d904 1338 event.Skip()
f6bcfd97
BP
1339
1340 #---------------------------------------------
1341 def OnTreeLeftDown(self, event):
08ecc920 1342 # reset the overview text if the tree item is clicked on again
f6bcfd97
BP
1343 pt = event.GetPosition();
1344 item, flags = self.tree.HitTest(pt)
1345 if item == self.tree.GetSelection():
d975da9b 1346 self.SetOverview(self.tree.GetItemText(item)+" Overview", self.curOverview)
185d7c3e 1347 event.Skip()
cf694132
RD
1348
1349 #---------------------------------------------
1350 def OnSelChanged(self, event):
08ecc920 1351 if self.dying or not self.loaded:
cf694132
RD
1352 return
1353
5a7823f5
RD
1354 item = event.GetItem()
1355 itemText = self.tree.GetItemText(item)
08ecc920 1356 self.LoadDemo(itemText)
5a7823f5
RD
1357
1358 #---------------------------------------------
08ecc920
RD
1359 def LoadDemo(self, demoName):
1360 try:
1361 wx.BeginBusyCursor()
1362
1363 os.chdir(self.cwd)
1364 self.ShutdownDemoModule()
1365
1366 if demoName == self.overviewText:
1367 # User selected the "wxPython Overview" node
1368 # ie: _this_ module
1369 # Changing the main window at runtime not yet supported...
1370 self.demoModules = DemoModules(__name__)
1371 self.SetOverview(self.overviewText, mainOverview)
1372 self.LoadDemoSource()
1373 self.UpdateNotebook(0)
1374 else:
1375 if os.path.exists(GetOriginalFilename(demoName)):
1376 wx.LogMessage("Loading demo %s.py..." % demoName)
1377 self.demoModules = DemoModules(demoName)
a6780fa2 1378 self.LoadDemoSource()
08ecc920
RD
1379 self.tree.Refresh()
1380 else:
1381 self.SetOverview("wxPython", mainOverview)
1382 self.codePage = None
1383 self.UpdateNotebook(0)
1384 finally:
1385 wx.EndBusyCursor()
cf694132 1386
08ecc920
RD
1387 #---------------------------------------------
1388 def LoadDemoSource(self):
1389 self.codePage = None
1390 self.codePage = DemoCodePanel(self.nb, self)
1391 self.codePage.LoadDemo(self.demoModules)
1392
1393 #---------------------------------------------
a6780fa2 1394 def RunModule(self):
08ecc920 1395 """Runs the active module"""
cf694132 1396
08ecc920
RD
1397 module = self.demoModules.GetActive()
1398 self.ShutdownDemoModule()
1399 overviewText = ""
1400
8412feb0
RD
1401 # o The RunTest() for all samples must now return a window that can
1402 # be palced in a tab in the main notebook.
08ecc920
RD
1403 # o If an error occurs (or has occured before) an error tab is created.
1404
1405 if module is not None:
1406 wx.LogMessage("Running demo module...")
1407 if hasattr(module, "overview"):
1408 overviewText = module.overview
cf694132 1409
08ecc920
RD
1410 try:
1411 self.demoPage = module.runTest(self, self.nb, self)
08ecc920 1412 except:
317a64e5 1413 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
7d0f8766
RD
1414 DemoError(sys.exc_info()), self)
1415
1416 assert self.demoPage is not None, "runTest must return a window!"
1417
08ecc920
RD
1418 else:
1419 # There was a previous error in compiling or exec-ing
317a64e5
RD
1420 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
1421 self.demoModules.GetErrorInfo(), self)
a6780fa2 1422
08ecc920 1423 self.SetOverview(self.demoModules.name + " Overview", overviewText)
a6780fa2
RD
1424
1425 if self.firstTime:
1426 # cahnge to the demo page the first time a module is run
1427 self.UpdateNotebook(2)
1428 self.firstTime = False
1429 else:
1430 # otherwise just stay on the same tab in case the user has changed to another one
1431 self.UpdateNotebook()
cf694132
RD
1432
1433 #---------------------------------------------
08ecc920
RD
1434 def ShutdownDemoModule(self):
1435 if self.demoPage:
1436 # inform the window that it's time to quit if it cares
1437 if hasattr(self.demoPage, "ShutdownDemo"):
1438 self.demoPage.ShutdownDemo()
1439 wx.YieldIfNeeded() # in case the page has pending events
1440 self.demoPage = None
1441
1442 #---------------------------------------------
1443 def UpdateNotebook(self, select = -1):
1444 nb = self.nb
1445 debug = False
1446
1447 def UpdatePage(page, pageText):
1448 pageExists = False
1449 pagePos = -1
1450 for i in range(nb.GetPageCount()):
1451 if nb.GetPageText(i) == pageText:
1452 pageExists = True
1453 pagePos = i
1454 break
1455
1456 if page:
1457 if not pageExists:
1458 # Add a new page
08ecc920
RD
1459 nb.AddPage(page, pageText)
1460 if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
1461 else:
08ecc920
RD
1462 if nb.GetPage(pagePos) != page:
1463 # Reload an existing page
1464 nb.Freeze()
08ecc920
RD
1465 nb.DeletePage(pagePos)
1466 nb.InsertPage(pagePos, page, pageText)
1467 nb.Thaw()
1468 if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
1469 else:
1470 # Excellent! No redraw/flicker
1471 if debug: wx.LogMessage("DBG: SAVED from reloading %s" % pageText)
1472 elif pageExists:
1473 # Delete a page
1474 nb.DeletePage(pagePos)
1475 if debug: wx.LogMessage("DBG: DELETED %s" % pageText)
1476 else:
1477 if debug: wx.LogMessage("DBG: STILL GONE - %s" % pageText)
1478
1479 if select == -1:
1480 select = nb.GetSelection()
cf694132 1481
08ecc920
RD
1482 UpdatePage(self.codePage, "Demo Code")
1483 UpdatePage(self.demoPage, "Demo")
cf694132 1484
887a1bd9 1485 if select >= 0 and select < nb.GetPageCount():
08ecc920
RD
1486 nb.SetSelection(select)
1487
cf694132
RD
1488 #---------------------------------------------
1489 def SetOverview(self, name, text):
f6bcfd97
BP
1490 self.curOverview = text
1491 lead = text[:6]
1492 if lead != '<html>' and lead != '<HTML>':
1e4a197e 1493 text = '<br>'.join(text.split('\n'))
6c7d1792
RD
1494 if wx.USE_UNICODE:
1495 text = text.decode('iso8859_1')
f6bcfd97 1496 self.ovr.SetPage(text)
cf694132 1497 self.nb.SetPageText(0, name)
cf694132
RD
1498
1499 #---------------------------------------------
1500 # Menu methods
c368d904 1501 def OnFileExit(self, *event):
cf694132
RD
1502 self.Close()
1503
330af869
RD
1504 def OnToggleRedirect(self, event):
1505 app = wx.GetApp()
1506 if event.Checked():
1507 app.RedirectStdio()
1508 print "Print statements and other standard output will now be directed to this window."
1509 else:
1510 app.RestoreStdio()
1511 print "Print statements and other standard output will now be sent to the usual location."
08ecc920 1512
cf694132 1513 def OnHelpAbout(self, event):
e166644c 1514 from About import MyAboutBox
ec3e670f 1515 about = MyAboutBox(self)
cf694132
RD
1516 about.ShowModal()
1517 about.Destroy()
1518
1e4a197e 1519 def OnHelpFind(self, event):
df5204e0
RD
1520 if self.finddlg != None:
1521 return
1522
1e4a197e 1523 self.nb.SetSelection(1)
1fded56b
RD
1524 self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find",
1525 wx.FR_NOUPDOWN |
1526 wx.FR_NOMATCHCASE |
1527 wx.FR_NOWHOLEWORD)
1e4a197e
RD
1528 self.finddlg.Show(True)
1529
df5204e0
RD
1530
1531 def OnUpdateFindItems(self, evt):
1532 evt.Enable(self.finddlg == None)
1533
1534
1e4a197e 1535 def OnFind(self, event):
08ecc920 1536 editor = self.codePage.editor
1e4a197e 1537 self.nb.SetSelection(1)
08ecc920
RD
1538 end = editor.GetLastPosition()
1539 textstring = editor.GetRange(0, end).lower()
1540 start = editor.GetSelection()[1]
1e4a197e
RD
1541 findstring = self.finddata.GetFindString().lower()
1542 loc = textstring.find(findstring, start)
1543 if loc == -1 and start != 0:
1544 # string not found, start at beginning
1545 start = 0
1546 loc = textstring.find(findstring, start)
1547 if loc == -1:
1fded56b 1548 dlg = wx.MessageDialog(self, 'Find String Not Found',
1e4a197e 1549 'Find String Not Found in Demo File',
1fded56b 1550 wx.OK | wx.ICON_INFORMATION)
1e4a197e
RD
1551 dlg.ShowModal()
1552 dlg.Destroy()
1553 if self.finddlg:
1554 if loc == -1:
1555 self.finddlg.SetFocus()
1556 return
1557 else:
1558 self.finddlg.Destroy()
df5204e0 1559 self.finddlg = None
08ecc920
RD
1560 editor.ShowPosition(loc)
1561 editor.SetSelection(loc, loc + len(findstring))
1e4a197e
RD
1562
1563
1564
1565 def OnFindNext(self, event):
1566 if self.finddata.GetFindString():
1567 self.OnFind(event)
1568 else:
1569 self.OnHelpFind(event)
1570
1571 def OnFindClose(self, event):
1572 event.GetDialog().Destroy()
df5204e0 1573 self.finddlg = None
1e4a197e 1574
cf694132 1575
887a1bd9
RD
1576 def OnOpenShellWindow(self, evt):
1577 if self.shell:
1578 # if it already exists then just make sure it's visible
1579 s = self.shell
1580 if s.IsIconized():
1581 s.Iconize(False)
1582 s.Raise()
1583 else:
1584 # Make a PyShell window
1585 from wx import py
1586 namespace = { 'wx' : wx,
1587 'app' : wx.GetApp(),
1588 'frame' : self,
1589 }
1590 self.shell = py.shell.ShellFrame(None, locals=namespace)
1591 self.shell.SetSize((640,480))
1592 self.shell.Show()
1593
1594 # Hook the close event of the main frame window so that we
1595 # close the shell at the same time if it still exists
1596 def CloseShell(evt):
1597 if self.shell:
1598 self.shell.Close()
1599 evt.Skip()
1600 self.Bind(wx.EVT_CLOSE, CloseShell)
1601
1602
cf694132
RD
1603 #---------------------------------------------
1604 def OnCloseWindow(self, event):
1e4a197e 1605 self.dying = True
08ecc920
RD
1606 self.demoPage = None
1607 self.codePage = None
26197023 1608 self.mainmenu = None
1cc18e15 1609 self.tbicon.Destroy()
cf694132
RD
1610 self.Destroy()
1611
c368d904 1612
cf694132
RD
1613 #---------------------------------------------
1614 def OnIdle(self, event):
1615 if self.otherWin:
1616 self.otherWin.Raise()
08ecc920 1617 self.demoPage = self.otherWin
cf694132
RD
1618 self.otherWin = None
1619
ccf691a4
RD
1620
1621 #---------------------------------------------
1622 def ShowTip(self):
1623 try:
1624 showTipText = open(opj("data/showTips")).read()
1625 showTip, index = eval(showTipText)
1626 except IOError:
1627 showTip, index = (1, 0)
1628 if showTip:
1fded56b 1629 tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
861a0196 1630 ##tp = MyTP(0)
1fded56b 1631 showTip = wx.ShowTip(self, tp)
ccf691a4
RD
1632 index = tp.GetCurrentTip()
1633 open(opj("data/showTips"), "w").write(str( (showTip, index) ))
1634
1635
ec3e670f
RD
1636 #---------------------------------------------
1637 def OnDemoMenu(self, event):
f6bcfd97
BP
1638 try:
1639 selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())]
1640 except:
1641 selectedDemo = None
1642 if selectedDemo:
1643 self.tree.SelectItem(selectedDemo)
1644 self.tree.EnsureVisible(selectedDemo)
ec3e670f 1645
c368d904 1646
c368d904 1647
f3d9dc1d
RD
1648 #---------------------------------------------
1649 def OnIconfiy(self, evt):
8412feb0 1650 wx.LogMessage("OnIconfiy: %s" % evt.Iconized())
f3d9dc1d
RD
1651 evt.Skip()
1652
1653 #---------------------------------------------
1654 def OnMaximize(self, evt):
1fded56b 1655 wx.LogMessage("OnMaximize")
f3d9dc1d
RD
1656 evt.Skip()
1657
8412feb0
RD
1658 #---------------------------------------------
1659 def OnActivate(self, evt):
1660 wx.LogMessage("OnActivate: %s" % evt.GetActive())
1661 evt.Skip()
f3d9dc1d 1662
8412feb0
RD
1663 #---------------------------------------------
1664 def OnAppActivate(self, evt):
1665 wx.LogMessage("OnAppActivate: %s" % evt.GetActive())
1666 evt.Skip()
f3d9dc1d 1667
cf694132
RD
1668#---------------------------------------------------------------------------
1669#---------------------------------------------------------------------------
1670
1fded56b 1671class MySplashScreen(wx.SplashScreen):
b5a5d647 1672 def __init__(self):
1fded56b
RD
1673 bmp = wx.Image(opj("bitmaps/splash.gif")).ConvertToBitmap()
1674 wx.SplashScreen.__init__(self, bmp,
a253aa20
RD
1675 wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
1676 3000, None, -1)
80b27b4e 1677 self.Bind(wx.EVT_CLOSE, self.OnClose)
b5a5d647
RD
1678
1679 def OnClose(self, evt):
8eca4fef 1680 self.Hide()
08ecc920 1681 frame = wxPythonDemo(None, "wxPython: (A Demonstration)")
1e4a197e 1682 frame.Show()
ccf691a4 1683 evt.Skip() # Make sure the default handler runs too...
cf694132 1684
b5a5d647 1685
1fded56b 1686class MyApp(wx.App):
b5a5d647
RD
1687 def OnInit(self):
1688 """
68320e40 1689 Create and show the splash screen. It will then create and show
b5a5d647
RD
1690 the main frame when it is time to do so.
1691 """
1e4a197e 1692
7b85d630
RD
1693 wx.SystemOptions.SetOptionInt("mac.window-plain-transition", 1)
1694
35ee3288
RD
1695 # For debugging
1696 #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
1697
a253aa20
RD
1698 # Normally when using a SplashScreen you would create it, show
1699 # it and then continue on with the applicaiton's
1700 # initialization, finally creating and showing the main
1701 # application window(s). In this case we have nothing else to
1702 # do so we'll delay showing the main frame until later (see
b7aef858 1703 # OnClose above) so the users can see the SplashScreen effect.
b5a5d647
RD
1704 splash = MySplashScreen()
1705 splash.Show()
a253aa20 1706
1e4a197e 1707 return True
b5a5d647
RD
1708
1709
1710
cf694132
RD
1711#---------------------------------------------------------------------------
1712
1713def main():
e02c03a4 1714 try:
d56cebe7 1715 demoPath = os.path.dirname(__file__)
e02c03a4
RD
1716 os.chdir(demoPath)
1717 except:
1718 pass
03a604a6 1719 app = MyApp(False)
cf694132
RD
1720 app.MainLoop()
1721
cf694132
RD
1722#---------------------------------------------------------------------------
1723
1724
08ecc920 1725mainOverview = """<html><body>
1fded56b
RD
1726<h2>wxPython</h2>
1727
f6063c19
RD
1728<p> wxPython is a <b>GUI toolkit</b> for the Python programming
1729language. It allows Python programmers to create programs with a
1730robust, highly functional graphical user interface, simply and easily.
1731It is implemented as a Python extension module (native code) that
1732wraps the popular wxWindows cross platform GUI library, which is
1733written in C++.
1fded56b
RD
1734
1735<p> Like Python and wxWindows, wxPython is <b>Open Source</b> which
1736means that it is free for anyone to use and the source code is
1737available for anyone to look at and modify. Or anyone can contribute
8b9a4190 1738fixes or enhancements to the project.
1fded56b
RD
1739
1740<p> wxPython is a <b>cross-platform</b> toolkit. This means that the
1741same program will run on multiple platforms without modification.
1742Currently supported platforms are 32-bit Microsoft Windows, most Unix
1743or unix-like systems, and Macintosh OS X. Since the language is
1744Python, wxPython programs are <b>simple, easy</b> to write and easy to
1745understand.
1746
1747<p> <b>This demo</b> is not only a collection of test cases for
1748wxPython, but is also designed to help you learn about and how to use
1749wxPython. Each sample is listed in the tree control on the left.
1750When a sample is selected in the tree then a module is loaded and run
1751(usually in a tab of this notebook,) and the source code of the module
1752is loaded in another tab for you to browse and learn from.
1753
1754"""
cf694132
RD
1755
1756
1757#----------------------------------------------------------------------------
1758#----------------------------------------------------------------------------
1759
1760if __name__ == '__main__':
08ecc920 1761 __name__ = 'Main'
cf694132
RD
1762 main()
1763
1764#----------------------------------------------------------------------------
1765
1766
1767
1768
1769
1770
1771