Wiki

root/cobra/trunk/Samples/Notepad.cobra

Revision 2457, 13.0 KB (checked in by Chuck.Esterbrook, 19 months ago)

Added new "Cobra Notepad Sample" using Windows Presentation Foundation (WPF).
credit:todd.a,Chuck

  • Property svn:eol-style set to native
Line 
1"""
2Cobra Notepad Sample
3by Todd A., Charles Esterbrook
4
5This program is a basic text editor written with Cobra and the Windows Presentation Framework (WPF).
6"""
7
8@args lib:'C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0'
9@ref 'PresentationFramework'
10@ref 'PresentationCore'
11@ref 'WindowsBase'
12
13use System.ComponentModel
14use System.Windows
15use System.Windows.Controls
16use System.Windows.Controls.Primitives
17use System.Windows.Documents
18use System.Windows.Input
19use System.Windows.Media
20use System.Windows.Threading
21use Microsoft.Win32
22
23
24class CustomCommands
25
26    shared
27
28        var increaseFontSize = RoutedCommand('IncreaseFontSize', CustomCommands.getType, _
29            InputGestureCollection([KeyGesture(Key.OemPlus, ModifierKeys.Control, 'Ctrl++')]))
30
31        var decreaseFontSize = RoutedCommand('DecreaseFontSize', CustomCommands.getType, _
32            InputGestureCollection([KeyGesture(Key.OemMinus, ModifierKeys.Control, 'Ctrl+-')]))
33
34        var resetFontSize = RoutedCommand('ResetFontSize', CustomCommands.getType, _
35            InputGestureCollection([KeyGesture(Key.D0, ModifierKeys.Control)]))
36
37
38sig CanExecuteMethod(sender, e as CanExecuteRoutedEventArgs)
39
40sig ExecutedMethod(sender, e as ExecutedRoutedEventArgs)
41
42sig RoutedMethod(sender, e as RoutedEventArgs)
43
44
45class MainWindow inherits Window
46
47    var _path as String?
48    var _isModified as bool
49    var _textBox as TextBox
50    var _status as TextBlock
51    var _filters = 'All Files|*.*;*|Config|*.conf;*.config|Text|*.txt;*.text'
52
53    cue init
54        base.init
55        .buildLayout
56        .bindCommands
57        .path = nil
58
59        listen _textBox.textChanged, ref .textChangeHandler
60        listen .loaded, ref .loadedHandler
61        listen .closing, ref .closingHandler
62
63    get appName as String
64        return 'Cobra Notepad Sample'
65
66    get fileName as String?
67        return if(.path, Path.getFileName(.path), 'Untitled')
68
69    pro path as String?
70        get
71            return _path
72        set
73            _path = value
74            if _path
75                fileName = Path.getFileName(_path)
76                dirName = Path.getDirectoryName(_path)
77                .title = '[.appName] - [fileName] - [dirName]'
78            else
79                .title = '[.appName] - Untitled'
80
81    pro status as String
82        get
83            return _status.text ? ''
84        set
85            _status.text = '[value]  ([DateTime.now])'
86
87    def loadedHandler(sender, e as RoutedEventArgs)
88        _textBox.focus
89        _textBox.fontFamily = FontFamily('Consolas')
90
91    def closingHandler(sender, e as CancelEventArgs)
92        res = .promptUserToSave
93        if res == MessageBoxResult.Cancel
94            e.cancel = true
95            return
96        else if res == MessageBoxResult.Yes
97            .saveFile(_path to !, _textBox.text to !)
98
99    def showAbout(sender, e as RoutedEventArgs)
100        MessageBox.show('Copyright © 2010 Cobra Language. All rights reserved.',
101            'About [.appName]', MessageBoxButton.OK)
102
103    def buildLayout
104        menu = .push(Menu())
105
106        .pushMenuItem('_File')
107        if true
108            .addMenuItem('_New', ApplicationCommands.new)
109            .addMenuItem('_Open...', ApplicationCommands.open)
110            .addMenuItem('_Save', ApplicationCommands.save)
111            .addMenuItem('Save As...', ApplicationCommands.saveAs)
112            .addSeparator
113            .addMenuItem('_Print...', ApplicationCommands.print)
114            .addSeparator
115            .addMenuItem('_Exit', ApplicationCommands.close)
116        .popItem
117
118        .pushMenuItem('_Edit')
119        if true
120            .addMenuItem('Undo', ApplicationCommands.undo)
121            .addMenuItem('Redo', ApplicationCommands.redo)
122            .addSeparator
123            .addMenuItem('Cut', ApplicationCommands.cut)
124            .addMenuItem('Copy', ApplicationCommands.copy)
125            .addMenuItem('Paste', ApplicationCommands.paste)
126            .addMenuItem('Delete', ApplicationCommands.delete)
127            .addSeparator
128            .addMenuItem('Select All', ApplicationCommands.selectAll)
129        .popItem
130
131        .pushMenuItem('_Format')
132        if true
133            .pushMenuItem('Font Size')
134            if true
135                .addMenuItem('Increase', CustomCommands.increaseFontSize, ref .increaseFontHandler)
136                .addMenuItem('Decrease', CustomCommands.decreaseFontSize, ref .decreaseFontHandler)
137                .addSeparator
138                .addMenuItem('Reset', CustomCommands.resetFontSize, ref .resetFontHandler)
139            .popItem
140            # .addMenuItem('Font...', ... to-do
141        .popItem
142
143        .pushMenuItem('_Help')
144        if true
145            .addMenuItem('About', ref .showAbout)
146        .popItem
147
148        .popItem  # main menu
149
150        _textBox = TextBox(acceptsReturn=true, acceptsTab=true)
151        _textBox.verticalScrollBarVisibility = ScrollBarVisibility.Auto
152        _textBox.horizontalScrollBarVisibility = ScrollBarVisibility.Auto
153        listen _textBox.previewMouseWheel, ref .textMouseWheelHandler
154
155        statusBar = Grid()
156        _status = TextBlock(text='',
157            horizontalAlignment=HorizontalAlignment.Left,
158            margin=Thickness(5))
159        statusBar.children.add(_status)
160
161        grid = Grid()
162        grid.rowDefinitions.add(RowDefinition(height=GridLength.auto))
163        grid.rowDefinitions.add(RowDefinition())
164        grid.rowDefinitions.add(RowDefinition(height=GridLength.auto))
165
166        .content = grid
167
168        grid.children.add(menu)
169        grid.children.add(_textBox)
170        grid.children.add(statusBar)
171
172        Grid.setRow(menu, 0)
173        Grid.setRow(_textBox, 1)
174        Grid.setRow(statusBar, 2)
175
176    ## Building menus
177
178    var _itemsControlStack = Stack<of ItemsControl>()
179
180    def push(ic as ItemsControl) as ItemsControl
181        _itemsControlStack.push(ic)
182        return ic
183
184    def pushMenuItem(header as String) as MenuItem
185        mi = .addMenuItem(header, nil, nil)
186        .push(mi)
187        return mi
188
189    def addMenuItem(header as String, command as RoutedCommand?) as MenuItem
190        return .addMenuItem(header, command, nil)
191
192    def addMenuItem(header as String, handler as RoutedMethod?) as MenuItem
193        return .addMenuItem(header, nil, handler)
194
195    def addMenuItem(header as String, command as RoutedCommand?, handler as RoutedMethod?) as MenuItem
196        mi = MenuItem(header=header, command=command)
197        if handler, listen mi.click, RoutedEventHandler(handler)
198        .addControl(mi)
199        return mi
200
201    def addSeparator
202        .addControl(Separator())
203
204    def addControl(c as Control)
205        _itemsControlStack.peek.items.add(c)
206
207    def popItem as ItemsControl
208        return _itemsControlStack.pop
209
210    ## Bind commands
211
212    def bindCommands
213        .bindCommand(ApplicationCommands.new to !, ref .defaultCanExecute, ref .newExecuted)
214        .bindCommand(ApplicationCommands.open to !, ref .defaultCanExecute, ref .openExecuted)
215        .bindCommand(ApplicationCommands.save to !, ref .saveCanExecute, ref .saveExecuted)
216        .bindCommand(ApplicationCommands.saveAs to !, ref .defaultCanExecute, ref .saveAsExecuted)
217        .bindCommand(ApplicationCommands.print to !, ref .defaultCanExecute, ref .printExecuted)
218        .bindCommand(ApplicationCommands.close to !, ref .defaultCanExecute, ref .exitExecuted)
219
220        .bindCommand(ApplicationCommands.undo to !, ref .undoCanExecute, ref .undoExecuted)
221        .bindCommand(ApplicationCommands.redo to !, ref .defaultCanExecute, ref .redoExecuted)
222
223        .bindCommand(ApplicationCommands.cut to !, ref .defaultCanExecute, ref .cutExecuted)
224        .bindCommand(ApplicationCommands.copy to !, ref .defaultCanExecute, ref .copyExecuted)
225        .bindCommand(ApplicationCommands.paste to !, ref .defaultCanExecute, ref .pasteExecuted)
226
227        .bindCommand(ApplicationCommands.delete to !, ref .defaultCanExecute, ref .deleteExecuted)
228
229        .bindCommand(ApplicationCommands.selectAll to !, ref .defaultCanExecute, ref .selectAllExecuted)
230       
231        .bindCommand(CustomCommands.increaseFontSize, ref .defaultCanExecute, ref .increaseFontHandler)     
232        .bindCommand(CustomCommands.decreaseFontSize, ref .defaultCanExecute, ref .decreaseFontExecuted)
233        .bindCommand(CustomCommands.resetFontSize, ref .defaultCanExecute, ref .resetFontExecuted)
234
235    def bindCommand(command as ICommand, canExecute as CanExecuteMethod, executed as ExecutedMethod)
236        cb = CommandBinding(command)
237        listen cb.canExecute, CanExecuteRoutedEventHandler(canExecute)
238        listen cb.executed, ExecutedRoutedEventHandler(executed)
239        .commandBindings.add(cb)
240
241    def textChangeHandler(sender, e as TextChangedEventArgs)
242        if not _isModified
243            _isModified = true
244            .status = 'Modified'
245           
246    def increaseFontHandler(sender, e as RoutedEventArgs?)
247        _textBox.fontSize += 1f
248        if _textBox.fontSize > 24f, _textBox.fontSize = 24f
249        .status = 'Increased font size.'
250   
251    def decreaseFontHandler(sender, e as RoutedEventArgs?)
252        _textBox.fontSize -= 1f
253        if _textBox.fontSize < 6f, _textBox.fontSize = 6f
254        .status = 'Decreased font size.'
255
256    def resetFontHandler(sender, e as RoutedEventArgs?)
257        _textBox.fontSize = 12f
258        .status = 'Reset font size.'
259
260    def textMouseWheelHandler(sender, e as MouseWheelEventArgs)
261        if (Keyboard.modifiers & ModifierKeys.Control) to int > 0
262            if e.delta > 0
263                .increaseFontHandler(nil, nil)
264            else if e.delta < 0
265                .decreaseFontHandler(nil, nil)
266            e.handled = true
267
268    def defaultCanExecute(sender, e as CanExecuteRoutedEventArgs)
269        e.canExecute = true
270        e.handled = true
271
272    def saveCanExecute(sender, e as CanExecuteRoutedEventArgs)
273        e.canExecute = _isModified
274        e.handled = true
275
276    def undoCanExecute(sender, e as CanExecuteRoutedEventArgs)
277        e.canExecute = _textBox.canUndo
278        e.handled = true
279
280    def redoCanExecute(sender, e as CanExecuteRoutedEventArgs)
281        e.canExecute = _textBox.canRedo
282        e.handled = true
283
284    def newExecuted(sender, e as ExecutedRoutedEventArgs)
285        res = .promptUserToSave
286        if res == MessageBoxResult.Cancel
287            return
288        else
289            _textBox.text = ''
290            .path = nil
291            .revive
292            .status = 'New.'
293        e.handled = true
294
295    def openExecuted(sender, e as ExecutedRoutedEventArgs)
296        res = .promptUserToSave
297        if res == MessageBoxResult.Cancel
298            return
299        else if res == MessageBoxResult.Yes
300            .saveFile(_path, _textBox.text)
301        else
302            dialog = OpenFileDialog(filter=_filters)
303            if dialog.showDialog
304                .path = dialog.fileName to !
305                _textBox.text = File.readAllText(_path)
306                .revive
307                .status = 'Opened.'
308        e.handled = true
309
310    def saveExecuted(sender, e as ExecutedRoutedEventArgs)
311        # If a path is set for the currently edited file then save the text,
312        # else prompt the user for a filename then save to that location
313        if _path and _isModified
314            .saveFile(_path, _textBox.text)
315        else if _isModified
316            newPath = .promptSaveFileName
317            if newPath
318                .path = newPath
319                .saveFile(_path, _textBox.text)
320        e.handled = true
321
322    def saveAsExecuted(sender, e as ExecutedRoutedEventArgs)
323        newPath = .promptSaveFileName
324        if newPath
325            .path = newPath
326            .saveFile(_path, _textBox.text)
327   
328    def printExecuted(sender, e as ExecutedRoutedEventArgs)
329        dialog = PrintDialog()
330        if dialog.showDialog
331            .status = 'Printing...'
332            flowDoc = FlowDocument(columnWidth=700.0f, fontFamily=FontFamily('Consolas'))
333            lines = _textBox.text.split(c'\n')
334            for line in lines
335                para = Paragraph(margin=Thickness(0))
336                para.inlines.add(Run(line))
337                flowDoc.blocks.add(para)
338            paginator = (flowDoc to IDocumentPaginatorSource).documentPaginator
339            dialog.printDocument(paginator, _path)
340            .status = 'Printed.'
341       
342    def exitExecuted(sender, e as ExecutedRoutedEventArgs)
343        .close
344
345    def undoExecuted(sender, e as ExecutedRoutedEventArgs)
346        _textBox.undo
347        e.handled = true
348
349    def redoExecuted(sender, e as ExecutedRoutedEventArgs)
350        _textBox.redo
351        e.handled = true
352
353    def cutExecuted(sender, e as ExecutedRoutedEventArgs)
354        _textBox.cut
355        e.handled = true
356
357    def copyExecuted(sender, e as ExecutedRoutedEventArgs)
358        _textBox.copy
359        e.handled = true
360
361    def pasteExecuted(sender, e as ExecutedRoutedEventArgs)
362        _textBox.paste
363        e.handled = true
364
365    def deleteExecuted(sender, e as ExecutedRoutedEventArgs)
366        _textBox.selectedText = ''
367        e.handled = true
368
369    def selectAllExecuted(sender, e as ExecutedRoutedEventArgs)
370        _textBox.selectAll
371        e.handled = true
372       
373    def increaseFontExecuted(sender, e as ExecutedRoutedEventArgs)
374        .increaseFontHandler(nil, nil)
375        e.handled = true
376       
377    def decreaseFontExecuted(sender, e as ExecutedRoutedEventArgs)
378        .decreaseFontHandler(nil, nil)
379        e.handled = true
380       
381    def resetFontExecuted(sender, e as ExecutedRoutedEventArgs)
382        .resetFontHandler(nil, nil)
383        e.handled = true
384
385    def promptUserToSave as MessageBoxResult
386        """
387        Prompt's the user to commit unsaved changes.
388        Returns true if a save was performed, else false
389        """
390        if _isModified
391            res = MessageBox.show('Do you want to save changes to [.fileName]?',
392                    'Save changes', MessageBoxButton.YesNoCancel)
393            if res == MessageBoxResult.Yes
394                # If the current file is a new file that has never been written to disk then
395                # prompt the user to provide a name and save it.
396                if not _path
397                    newPath = .promptSaveFileName
398
399                    # If the user provides a filename/path then load this path, else if they cancel the
400                    # dialog box then return a 'No' result.
401                    if newPath
402                        .path = newPath
403                    else
404                        return MessageBoxResult.No
405
406                if _path and _path.trim <> ''
407                    .saveFile(_path, _textBox.text)
408            return res
409
410        return MessageBoxResult.No
411
412    def promptSaveFileName as String?
413        dialog = SaveFileDialog(filter=_filters)
414        dialog.fileName = .fileName
415        if dialog.showDialog
416            newPath = dialog.fileName to !
417            return newPath
418        return nil
419
420    def saveFile(path as String?, content as String?)
421        if path, File.writeAllText(path, content ? '')
422        .revive
423        .status = 'Saved.'
424
425    def revive
426        _isModified = false
427        .status = ''
428
429
430class Program
431
432    def main is shared has STAThread
433        Program().run
434
435    def run
436        app = Application()
437        listen app.dispatcherUnhandledException, ref .unhandledExceptionHandler
438        app.run(MainWindow())
439
440    def unhandledExceptionHandler(sender, args as DispatcherUnhandledExceptionEventArgs)
441        msg = 'Exception: ' + args.exception.message
442        MessageBox.show(msg, 'Unhandled Exception', MessageBoxButton.OK)
443        args.handled = true
Note: See TracBrowser for help on using the browser.