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