Wiki

root/cobra/trunk/Samples/GtkSourceEditor.cobra

Revision 2618, 11.6 KB (checked in by Charles.Esterbrook, 9 months ago)

Added GtkSourceView? sample program.
credit:todd.a

  • Property svn:eol-style set to native
Line 
1"""
2GtkSourceEditor.cobra
3by Todd A.
4
5This sample program relies on GtkSourceView# which is only known
6to run on Linux, not Mac or Windows.
7
8pkg-config is commonly used with GTK libraries hence it is used
9for properly referencing gtk-sharp and gtksourceview.
10Install these packages using your system's package manager.
11"""
12
13@args sharpgtk-sharp-2.0,gtksourceview2-sharp
14
15use Gtk
16use GtkSourceView
17use Pango
18
19
20class Editor
21
22    var _untitledCount = 0
23    var _defaultTitle = 'Untitled'
24    var _filename = ''
25    var _modifiedBuffer = false
26    var _onDisk = false
27
28    var _config = {
29        'lineNumbers': true,
30        'tabWidth': 4,
31        'wrapLines': true,
32        'maxFontSize': 24,
33        'minFontSize': 6,
34        'fontFamily': 'monospace',
35    }
36
37    # The _ui is a dictionary of common parts of the editor that may need to be referenced
38    # throughout various parts of the code.
39    var _ui = {
40        'window': nil,
41        'sourceView': nil,
42    }
43
44    get modifiedBuffer from var
45        """
46        Keeps track of whether or not the current buffer was modified
47        and will need saving.
48        """
49
50    get onDisk from var
51        """
52        Indicates whether or not the current buffer was loaded from, or already saved to,
53        a file that is on some non-volatile storage.
54        """
55
56    get source as String?
57        sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
58        return sourceBuffer.text
59
60    def main 
61        Application.init
62
63        accelGroup = AccelGroup()
64        window = Window('')
65        listen window.deleteEvent, ref .exitHandler
66        window.setDefaultSize(800, 600)
67        window.addAccelGroup(accelGroup)
68        _ui['window'] = window
69        _ui['accelGroup'] = accelGroup
70       
71        menubar = _buildMenubar
72        editor = _buildEditor
73        status = _buildStatus
74
75        mainbox = VBox(false, 10)
76
77        mainbox.packStart(menubar, false, false, 0)
78        mainbox.packStart(editor, true, true, 0)
79        mainbox.packStart(status, false, false, 0)
80
81        window.add(mainbox)
82        window.showAll
83
84        .updateTitle(nil)
85
86        Application.run
87
88    def menuNewHandler(sender, e)
89        if .modifiedBuffer
90            responseType = .promptToSave
91            if responseType == ResponseType.Yes, .saveBuffer(.onDisk)
92            else if responseType == ResponseType.Cancel, return
93
94        .updateTitle(nil)
95        _onDisk = false
96        _modifiedBuffer = false
97        sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
98        sourceBuffer.clear
99
100    def menuOpenHandler(sender, e)
101        if .modifiedBuffer
102            responseType = .promptToSave
103            if responseType == ResponseType.Yes, .menuSaveHandler(sender, e)
104            else if responseType == ResponseType.Cancel, return
105           
106        fileChooser = FileChooserDialog('Select a file for opening', _ui['window'] to Window, _
107            FileChooserAction.Open, 'Cancel', ResponseType.Cancel, 'Open', ResponseType.Accept)
108           
109        if fileChooser.run == (ResponseType.Accept to int)
110            filename = fileChooser.filename
111            using reader = StreamReader(File.openRead(filename))
112                source = reader.readToEnd
113                sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
114                sourceBuffer.text = source
115
116                _filename = filename to !
117                _onDisk = true
118                .updateTitle(filename)
119
120                # Though the buffer is modified it is for a file that's just opened hence for
121                # editor purposes it is not modified.
122                _modifiedBuffer = false
123
124        fileChooser.destroy
125   
126    def saveBuffer(onDisk as bool)
127        saved = false
128        if onDisk
129            .writeBuffer(_filename, .source)
130            saved = true
131        else
132            filename = .saveToFile
133            if filename
134                .writeBuffer(filename, .source)
135                _onDisk = true
136                .updateTitle(filename)
137                saved = true
138
139        if saved, .updateStatus('File successfully saved')
140       
141
142    def menuSaveHandler(sender, e)
143        if not .modifiedBuffer, return
144        .saveBuffer(.onDisk)
145
146    def menuSaveAsHandler(sender, e)
147        .saveBuffer(false)
148
149    def menuPrintHandler(sender, e)
150        printDialog = PrintUnixDialog('Print the current file', _ui['window'] to Window)
151        printDialog.run
152        printDialog.destroy
153
154    def menuExitHandler(sender, e)
155        quit = true
156        if .modifiedBuffer
157            responseType = .promptToSave
158            if responseType == ResponseType.Cancel, quit = false
159            else if responseType == ResponseType.Yes, .saveBuffer(.onDisk)
160        if quit, Application.quit
161   
162    def exitHandler(sender, e as DeleteEventArgs)
163        quit = true
164        if .modifiedBuffer
165            responseType = .promptToSave
166            if responseType == ResponseType.Cancel, quit = false
167            else if responseType == ResponseType.Yes, .saveBuffer(.onDisk)
168        e.retVal = not quit
169        if quit, Application.quit
170
171    def menuUndoHandler(sender, e)
172        sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
173        sourceBuffer.undo
174
175    def menuRedoHandler(sender, e)
176        sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
177        sourceBuffer.redo
178
179    def menuCutHandler(sender, e)
180        sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
181        clipboard = Clipboard.get(Gdk.Selection.clipboard)
182        sourceBuffer.cutClipboard(clipboard, true)
183
184    def menuCopyHandler(sender, e)
185        sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
186        clipboard = Clipboard.get(Gdk.Selection.clipboard)
187        sourceBuffer.copyClipboard(clipboard)
188
189    def menuPasteHandler(sender, e)
190        sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
191        clipboard = Clipboard.get(Gdk.Selection.clipboard)
192        sourceBuffer.pasteClipboard(clipboard)
193
194    def menuDeleteHandler(sender, e)
195        sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
196        sourceBuffer.deleteSelection(true, true)
197
198    def menuIncreaseFontSizeHandler(sender, e)
199        .changeFontSize(1)
200        .updateStatus('Increased font size')
201
202    def menuDecreaseFontSizeHandler(sender, e)
203        .changeFontSize(-1)
204        .updateStatus('Decreased font size')
205
206    def menuResetFontSizeHandler(sender, e)
207        sourceView = _ui['sourceView'] to SourceView
208        fontDesc = _ui['defaultFontDescription'] to FontDescription
209        sourceView.modifyFont(fontDesc)
210
211    def menuAboutHandler(sender, e)
212        aboutDialog = MessageDialog(_ui['window'] to Window, DialogFlags.DestroyWithParent, _
213            MessageType.Info, ButtonsType.Close, 'Copyright © 2010 Cobra Language. All rights reserved.')
214        aboutDialog.run
215        aboutDialog.destroy
216
217    def menuSelectAllHandler(sender, e)
218        sourceBuffer = _ui['sourceBuffer']
219        sourceBuffer.selectRange(sourceBuffer.startIter, sourceBuffer.endIter)
220   
221    def menuPassHandler(sender, e as EventArgs)
222        pass
223
224    def bufferModifiedHandler(sender, e as EventArgs)
225        if not _modifiedBuffer, .updateStatus('')
226        _modifiedBuffer = true
227        .refresh
228
229    def writeBuffer(filename as String?, contents as String?)
230        if filename and contents
231            using writer = StreamWriter(filename)
232                writer.write(contents)
233                _modifiedBuffer = false
234
235    def saveToFile as String?
236        fileChooser = FileChooserDialog('Save file', _ui['window'] to Window, _
237            FileChooserAction.Save, 'Cancel', ResponseType.Cancel, 'Save', ResponseType.Accept)
238        fileChooser.modal = true
239           
240        if fileChooser.run == (ResponseType.Accept to int)
241            filename = fileChooser.filename
242            fileChooser.destroy
243            return filename
244
245        fileChooser.destroy
246        return nil
247
248    def promptToSave as ResponseType
249        dialog = MessageDialog(_ui['window'] to Window, DialogFlags.Modal, MessageType.Question, _
250            ButtonsType.None, 'Would you like to save the changes in ')
251        dialog.addButton('Yes', ResponseType.Yes)
252        dialog.addButton('No', ResponseType.No)
253        dialog.addButton('Cancel', ResponseType.Cancel)
254        responseType = dialog.run
255        dialog.destroy
256        return responseType to ResponseType
257
258    def changeFontSize(delta as int)
259        sourceView = _ui['sourceView'] to SourceView
260        context = sourceView.pangoContext
261        fontDesc = context.fontDescription
262        scale = Pango.Scale.pangoScale
263        curSize = fontDesc.size / scale
264        curSize += delta
265        minSize = _config['minFontSize']
266        maxSize = _config['maxFontSize']
267        if curSize >= minSize and curSize <= maxSize
268            fontDesc.size = (curSize * scale) to int
269            sourceView.modifyFont(fontDesc)
270   
271    def refresh 
272        """
273        Update the state of UI elements based on the current editor state.
274        """
275        sourceBuffer = _ui['sourceBuffer'] to SourceBuffer
276        redoItem = _ui['menu[Stock.redo]'] to MenuItem
277        undoItem = _ui['menu[Stock.undo]'] to MenuItem
278
279        redoItem.sensitive = sourceBuffer.canRedo
280        undoItem.sensitive = sourceBuffer.canUndo
281
282    def updateTitle(title as String?)
283        window = _ui['window'] to Window
284        if title and title.length
285            window.title = title
286        else
287            _untitledCount += 1
288            _filename = '[_defaultTitle] [_untitledCount]'
289            window.title = _filename
290
291    def updateStatus(message as String)
292        """
293        Removes any message previously displayed in the status bar then display
294        a new one.
295        """
296        statusBar = _ui['statusBar'] to Statusbar
297        statusBar.pop(1)
298        statusBar.push(1, message)
299
300    def _buildMenubar as Widget
301        container = MenuBar()
302
303        menuItems as Dictionary<of String, dynamic> = {
304            'File': [
305                [Stock.new, EventHandler(ref .menuNewHandler)],
306                [Stock.open, EventHandler(ref .menuOpenHandler)],
307                [Stock.save, EventHandler(ref .menuSaveHandler)],
308                [Stock.saveAs, EventHandler(ref .menuSaveAsHandler)],
309                ['----', nil],
310                [Stock.print, EventHandler(ref .menuPrintHandler)],
311                ['----', nil],
312                [Stock.quit, EventHandler(ref .menuExitHandler)]
313            ],
314
315            'Edit': [
316                [Stock.undo, EventHandler(ref .menuUndoHandler), StateType.Insensitive],
317                [Stock.redo, EventHandler(ref .menuRedoHandler), StateType.Insensitive],
318                ['----', nil],
319                [Stock.cut, EventHandler(ref .menuCutHandler)],
320                [Stock.copy, EventHandler(ref .menuCopyHandler)],
321                [Stock.paste, EventHandler(ref .menuPasteHandler)],
322                [Stock.delete, EventHandler(ref .menuDeleteHandler)],
323                ['----', nil],
324                [Stock.selectAll, EventHandler(ref .menuSelectAllHandler)],
325            ],
326
327            'Format': [
328                {
329                    'FontSize': [
330                        [Stock.zoomIn, EventHandler(ref .menuIncreaseFontSizeHandler)],
331                        [Stock.zoomOut, EventHandler(ref .menuDecreaseFontSizeHandler)],
332                        ['----', nil],
333                        [Stock.zoom100, EventHandler(ref .menuResetFontSizeHandler)],
334                    ] to System.Object,
335                }
336            ],
337           
338            'Help': [
339                [Stock.about, EventHandler(ref .menuAboutHandler)],
340            ],
341        }
342
343        _buildMenu(container, menuItems)
344
345        return container
346   
347    def _buildMenu(container as Container, items as Dictionary<of String, System.Object>)
348        accelGroup = _ui['accelGroup'] to AccelGroup
349        for label, item in items
350            menuItem = MenuItem(label)
351            menu = Menu()
352            for subItem in item to dynamic
353                if subItem inherits List<of System.Object>
354                    stockId, handler = subItem
355                    subMenuItem = ImageMenuItem(stockId to String, accelGroup)
356                    if subItem.count > 2
357                        for i in 2 : subItem.count
358                            if subItem[i] inherits StateType and subItem[i] to StateType == StateType.Insensitive, subMenuItem.sensitive = true
359
360                    _ui['menu[stockId]'] = subMenuItem
361
362                    menu.append(subMenuItem)
363
364                    if handler inherits EventHandler
365                        listen subMenuItem.activated, handler
366                else if subItem inherits List<of String>
367                    menu.append(SeparatorMenuItem())
368                else if subItem inherits Dictionary<of String, System.Object>
369                    _buildMenu(menu, subItem)
370            menuItem.submenu = menu
371            container.add(menuItem)
372
373    def _buildEditor as Widget
374        container = ScrolledWindow()
375        container.hscrollbarPolicy = PolicyType.Never
376        container.vscrollbarPolicy = PolicyType.Automatic
377
378        fontDesc = FontDescription()
379        fontDesc.family = 'monospace'
380        _ui['defaultFontDescription'] = fontDesc
381
382        languageManager = SourceLanguageManager.default
383        language = languageManager.getLanguage('cobra')
384        buffer = SourceBuffer(language)
385        listen buffer.changed, ref .bufferModifiedHandler
386        sourceView = SourceView(buffer)
387        sourceView.showLineNumbers = _config['lineNumbers'] to bool 
388        sourceView.wrapMode = Gtk.WrapMode.Word
389        sourceView.modifyFont(fontDesc)
390
391        _ui['sourceBuffer'] = buffer
392        _ui['sourceView'] = sourceView
393        _ui['fontDescription'] = fontDesc
394   
395        container.add(sourceView)
396
397        return container
398
399    def _buildStatus as Widget
400        container = Statusbar()
401        _ui['statusBar'] = container
402        return container
403
404    def _saveFileAs
405        fileChooser = FileChooserDialog('Save file as...', _ui['window'] to Window, FileChooserAction.Save, _
406        "Cancel", ResponseType.Cancel, "Save", ResponseType.Accept)
407
408        if fileChooser.run == (ResponseType.Accept to int)
409            # Save the file
410            pass
411        fileChooser.destroy
Note: See TracBrowser for help on using the browser.