Passed
Pull Request — master (#291)
by Marek
01:54
created

pygameui.Text.Text.deleteSelection()   C

Complexity

Conditions 9

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 20
nop 1
dl 0
loc 34
rs 6.6666
c 0
b 0
f 0
1
#
2
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
3
#
4
#  This file is part of Pygame.UI.
5
#
6
#  Pygame.UI is free software; you can redistribute it and/or modify
7
#  it under the terms of the Lesser GNU General Public License as published by
8
#  the Free Software Foundation; either version 2.1 of the License, or
9
#  (at your option) any later version.
10
#
11
#  Pygame.UI is distributed in the hope that it will be useful,
12
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
#  Lesser GNU General Public License for more details.
15
#
16
#  You should have received a copy of the Lesser GNU General Public License
17
#  along with Pygame.UI; if not, write to the Free Software
18
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
#
20
21
import Const
22
from WordUtils import *
23
from Widget import Widget, registerWidget
24
import pygame.key
25
26
# keys mapping
27
MAPPING = {
28
    pygame.K_KP0: '0', pygame.K_KP1: '1', pygame.K_KP2: '2', pygame.K_KP3: '3', pygame.K_KP4: '4',
29
    pygame.K_KP5: '5', pygame.K_KP6: '6', pygame.K_KP7: '7', pygame.K_KP8: '8', pygame.K_KP9: '9',
30
}
31
32
class Selection(object):
33
    """ Object to hold and pre-process information about selected area of the text.
34
    """
35
    def __init__(self):
36
        self._start = None
37
        self._end = None
38
39
    @property
40
    def first(self):
41
        """ Returns coordinates of the beginning of the selection """
42
        return min(self._start, self._end)
43
44
    @property
45
    def last(self):
46
        """ Returns coordinates of the ending of the selection """
47
        return max(self._start, self._end)
48
49
    def select(self, row, column):
50
        if self._start is None:
51
            self._start = (row, column)
52
        self._end = (row, column)
53
54
    def deselect(self):
55
        self._start = self._end = None
56
57
    def __nonzero__(self):
58
        return self._start is not None and self._end is not None
59
60
61
class Text(Widget):
62
    """Text edit widget."""
63
64
    def __init__(self, parent, **kwargs):
65
        Widget.__init__(self, parent)
66
        # data
67
        self.__dict__['text'] = [""]
68
        self.__dict__['offsetRow'] = 0
69
        self.__dict__['cursorRow'] = 0
70
        self.__dict__['cursorColumn'] = 0
71
        self.__dict__['action'] = None
72
        self.__dict__['editable'] = 1
73
        self.__dict__['vertScrollbar'] = None
74
        self.__dict__['selection'] = Selection()
75
        # flags
76
        self.processKWArguments(kwargs)
77
        parent.registerWidget(self)
78
79
    def draw(self, surface):
80
        self.theme.drawText(surface, self)
81
        return self.rect
82
83
    def attachVScrollbar(self, scrollbar):
84
        self.vertScrollbar = scrollbar
85
        scrollbar.subscribeAction("*", self)
86
        scrollbar.action = "onScroll"
87
        scrollbar.slider.min = 0
88
        scrollbar.slider.max = len(self.text) + 100
89
        scrollbar.slider.position = self.offsetRow
90
        scrollbar.slider.shown = 1
91
92
    def onScroll(self, widget, action, data):
93
        self.offsetRow = self.vertScrollbar.slider.position
94
95
    def onCursorChanged(self):
96
        # force redraw
97
        self.cursorColumn += 1
98
        self.cursorColumn -= 1
99
        # super
100
        Widget.onCursorChanged(self)
101
102
    def _deleteSelection(self):
103
        assert self.selection
104
105
        # one-line selection
106
        if self.selection.first[0] == self.selection.last[0]:
107
            textBefore = self.text[self.selection.last[0]][:self.selection.first[1]]
108
            textAfter = self.text[self.selection.last[0]][self.selection.last[1]:]
109
            self.text[self.selection.last[0]] = textBefore + textAfter
110
        else:
111
            # handle multi-line selection
112
            # delete end of selection
113
            self.text[self.selection.last[0]] = self.text[self.selection.last[0]][self.selection.last[1]:]
114
            # delete fully selected rows
115
            del self.text[self.selection.first[0] + 1 : self.selection.last[0]]
116
            # delete selection on first row
117
            self.text[self.selection.first[0]] = self.text[self.selection.first[0]][:self.selection.first[1]]
118
            # join the rows that are spanned
119
            self.text[self.selection.first[0]] = self.text[self.selection.first[0]] + self.text[self.selection.first[0] + 1]
120
            del self.text[self.selection.first[0]+1]
121
        # move cursor to selection begining
122
        self.cursorColumn = self.selection.first[1]
123
        self.cursorRow = self.selection.first[0]
124
        # clear selection
125
        self.selection.deselect()
126
127
    def _processBackspace(self, evt):
128
        if self.selection:
129
            self._deleteSelection()
130
        elif self.cursorColumn > 0:
131
            self.text[self.cursorRow] = self.text[self.cursorRow][:self.cursorColumn - 1] + self.text[self.cursorRow][self.cursorColumn:]
132
            self.cursorColumn -= 1
133
        elif self.cursorRow > 0:
134
            self.cursorColumn = len(self.text[self.cursorRow - 1])
135
            self.text[self.cursorRow - 1] = self.text[self.cursorRow - 1] + self.text[self.cursorRow]
136
            del self.text[self.cursorRow]
137
            self.cursorRow -= 1
138
139
    def _processDelete(self, evt):
140
        if self.selection:
141
            self._deleteSelection()
142
        elif self.cursorColumn < len(self.text[self.cursorRow]):
143
            self.text[self.cursorRow] = self.text[self.cursorRow][:self.cursorColumn] + self.text[self.cursorRow][self.cursorColumn + 1:]
144
        elif self.cursorRow < len(self.text) - 1:
145
            self.text[self.cursorRow] = self.text[self.cursorRow] + self.text[self.cursorRow + 1]
146
            del self.text[self.cursorRow + 1]
147
148
    def _processLeft(self, evt):
149
        if evt.mod & pygame.KMOD_CTRL:
150
            # move one word left
151
            # take words on line
152
            words = splitter(self.text[self.cursorRow][:self.cursorColumn])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable splitter does not seem to be defined.
Loading history...
153
            if len(words) == 0:
154
                if self.cursorRow == 0:
155
                    #we are on first line, so move cursor to begining of line
156
                    self.cursorColumn = 0
157
                else:
158
                    #move to previous line and try again
159
                    self.cursorRow -= 1
160
                    self.cursorColumn = len(self.text[self.cursorRow])
161
                    self._processLeft(evt)
162
                return
163
            # we must jump to begining of last word
164
            self.cursorColumn = words[-1][1]
165
        elif self.cursorColumn > 0:
166
            self.cursorColumn -= 1
167
        elif self.cursorRow > 0:
168
            self.cursorRow -= 1
169
            self.cursorColumn = len(self.text[self.cursorRow])
170
171
    def _processRight(self, evt):
172
        if evt.mod & pygame.KMOD_CTRL:
173
            # move one word right
174
            # take words on line
175
            words = splitter(self.text[self.cursorRow][self.cursorColumn:])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable splitter does not seem to be defined.
Loading history...
176
            if len(words) == 0:
177
                #move to next line
178
                if self.cursorRow == len(self.text) - 1:
179
                    #we are on last line, so move cursor to end of line
180
                    self.cursorColumn = len(self.text[self.cursorRow])
181
                else:
182
                    self.cursorRow += 1
183
                    self.cursorColumn = 0
184
                    self._processRight(evt)
185
                return
186
            # we jump to the end of the first word
187
            self.cursorColumn += words[0][2]
188
        elif self.cursorColumn < len(self.text[self.cursorRow]):
189
            self.cursorColumn += 1
190
        elif self.cursorRow < len(self.text):
191
            # move to the next row
192
            if self.cursorRow < len(self.text) - 1:
193
                self.cursorRow += 1
194
                self.cursorColumn = 0
195
196
    def _processUp(self, evt):
197
        if self.cursorRow > 0:
198
            self.cursorRow -= 1
199
            self.cursorColumn = min(self.cursorColumn, len(self.text[self.cursorRow]))
200
        else:
201
            self.cursorColumn = 0
202
203
        if self.vertScrollbar and self.cursorRow - self.offsetRow < 0:
204
            self.vertScrollbar.onButton1(self, "", "")
205
206
    def _processDown(self, evt):
207
        if self.cursorRow < len(self.text) - 1:
208
            self.cursorRow += 1
209
            self.cursorColumn = min(self.cursorColumn, len(self.text[self.cursorRow]))
210
        else:
211
            self.cursorColumn = len(self.text[self.cursorRow])
212
213
        if self.vertScrollbar and self.cursorRow - self.offsetRow >= self.theme.getTextDrawLines(self):
214
            self.vertScrollbar.onButton2(self, "", "")
215
216
    def _processHome(self, evt):
217
        self.cursorColumn = 0
218
219
    def _processEnd(self, evt):
220
        self.cursorColumn = len(self.text[self.cursorRow])
221
222
    def _processReturn(self, evt):
223
        text1 = self.text[self.cursorRow][self.cursorColumn:]
224
        text2 = self.text[self.cursorRow][:self.cursorColumn]
225
        self.text[self.cursorRow] = text1
226
        self.text.insert(self.cursorRow, text2)
227
        self.cursorRow += 1
228
        self.cursorColumn = 0
229
230
    def _processUnicode(self, evt):
231
        char = evt.unicode
232
        self.text[self.cursorRow] = self.text[self.cursorRow][:self.cursorColumn] + char + self.text[self.cursorRow][self.cursorColumn:]
233
        self.cursorColumn += 1
234
235
    def _processNumKeyboard(self, evt):
236
        self.text[self.cursorRow] = self.text[self.cursorRow][:self.cursorColumn] + MAPPING[evt.key] + self.text[self.cursorRow][self.cursorColumn:]
237
        self.cursorColumn += 1
238
239
    def wrapDeleteSelection(self, func, evt, deleteOnly=False):
240
        if self.selection:
241
            self._deleteSelection()
242
            if deleteOnly: return
243
        func(evt)
244
245
    def wrapSelect(self, func, evt):
246
        if evt.mod & pygame.KMOD_SHIFT:
247
            self.selection.select(self.cursorRow, self.cursorColumn)
248
            func(evt)
249
            self.selection.select(self.cursorRow, self.cursorColumn)
250
        else:
251
            func(evt)
252
253
    def processKeyUp(self, evt):
254
        # consume pygame.K_RETURN (acceptButton on Window will not work)
255
        # can be choosable on construction?
256
        if evt.key == pygame.K_RETURN:
257
            return Const.NoEvent
258
        else:
259
            return Widget.processKeyUp(self, evt)
260
261
    def processKeyDown(self, evt):
262
        if not self.editable:
263
            return Widget.processKeyDown(self, evt)
264
265
        # process keys
266
        if evt.key == pygame.K_BACKSPACE:
267
            self.wrapDeleteSelection(self._processBackspace, evt, deleteOnly=True)
268
269
        elif evt.key == pygame.K_DELETE:
270
            self.wrapDeleteSelection(self._processDelete, evt, deleteOnly=True)
271
272
        elif evt.key == pygame.K_ESCAPE:
273
            self.app.setFocus(None)
274
275
        elif evt.key == pygame.K_LEFT:
276
            self.wrapSelect(self._processLeft, evt)
277
278
        elif evt.key == pygame.K_RIGHT:
279
            self.wrapSelect(self._processRight, evt)
280
281
        elif evt.key == pygame.K_UP:
282
            self.wrapSelect(self._processUp, evt)
283
284
        elif evt.key == pygame.K_DOWN:
285
            self.wrapSelect(self._processDown, evt)
286
287
        elif evt.key == pygame.K_TAB:
288
            pass
289
290
        elif evt.key == pygame.K_HOME:
291
            self.wrapSelect(self._processHome, evt)
292
293
        elif evt.key == pygame.K_END:
294
            self.wrapSelect(self._processEnd, evt)
295
296
        elif evt.key == pygame.K_RETURN:
297
            self.wrapDeleteSelection(self._processReturn, evt)
298
299
        elif hasattr(evt, 'unicode') and evt.unicode:
300
            self.wrapDeleteSelection(self._processUnicode, evt)
301
302
        elif evt.key in MAPPING:
303
            self.wrapDeleteSelection(self._processNumKeyboard, evt)
304
305
        return Widget.processKeyDown(self, Const.NoEvent)
306
307
    def onFocusGained(self):
308
        Widget.onFocusGained(self)
309
        self.cursorRow = len(self.text) - 1
310
        self.cursorColumn = len(self.text[self.cursorRow])
311
312
    def onFocusLost(self):
313
        Widget.onFocusLost(self)
314
        self.processAction(self.action)
315
316
    # redirect mouse wheel events to the scollbar
317
    def processMWUp(self, evt):
318
        if self.vertScrollbar:
319
            return self.vertScrollbar.processMWUp(evt)
320
321
    def processMWDown(self, evt):
322
        if self.vertScrollbar:
323
            return self.vertScrollbar.processMWDown(evt)
324
325
326
registerWidget(Text, 'text')
327
328
329
import unittest
330
331
class TextTestCase(unittest.TestCase):
332
    def setUp(self):
333
        class Evt:
334
            def __init__(self, key, letter="", mod=0):
335
                self.key = key
336
                self.unicode = unicode(letter)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable unicode does not seem to be defined.
Loading history...
337
                self.mod = mod
338
339
        class App:
340
            def __init__(self):
341
                self.theme = "Test"
342
343
        class Parent:
344
            def __init__(self):
345
                self.app = App()
346
347
            def getApp(self):
348
                return self.app
349
350
            def redraw(self, *args, **kwargs):
351
                return
352
353
            def registerWidget(self, *args, **kwargs):
354
                return
355
356
        self.parent = Parent()
357
        self.txt = Text(self.parent)
358
        self.txt.text = ["Zero line", "One line", "Two line", "", "", "Five line"]
359
        self.txt.offsetRow = 0
360
        self.txt.cursorRow = 0
361
        self.txt.cursorColumn = 0
362
        self.txt.action = None
363
        self.txt.editable = 1
364
        self.txt.vertScrollbar = None
365
        self.txt.selection = Selection()
366
        self.Evt = Evt
367
368
    def tearDown(self):
369
        pass
370
371
    def test_dummy(self):
372
        self.txt.cursorColumn = 3
373
        self.txt.cursorRow = 2
374
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Two line", "", "", "Five line"])
375
        self.assertEqual(self.txt.cursorColumn, 3)
376
        self.assertEqual(self.txt.cursorRow, 2)
377
378
    def test_input(self):
379
        self.txt.cursorColumn = 3
380
        self.txt.cursorRow = 2
381
        for letter in " quite expanded":
382
            Text.processKeyDown(self.txt, self.Evt(None, letter=letter))
383
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Two quite expanded line", "", "", "Five line"])
384
        self.assertEqual(self.txt.cursorColumn, 18)
385
386
    def test_numpad_input(self):
387
        self.txt.cursorColumn = 3
388
        self.txt.cursorRow = 2
389
        for key in [pygame.K_KP0, pygame.K_KP4, pygame.K_KP4, pygame.K_KP8]:
390
            Text.processKeyDown(self.txt, self.Evt(key))
391
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Two0448 line", "", "", "Five line"])
392
        self.assertEqual(self.txt.cursorColumn, 7)
393
394
    def test_backspace(self):
395
        evt = self.Evt(pygame.K_BACKSPACE)
396
        self.txt.cursorColumn = 3
397
        self.txt.cursorRow = 2
398
        for x in range(4):
399
            Text.processKeyDown(self.txt, evt)
400
        self.assertEqual(self.txt.text, ["Zero line", "One line line", "", "", "Five line"])
401
        self.assertEqual(self.txt.cursorColumn, 8)
402
        self.assertEqual(self.txt.cursorRow, 1)
403
        # first character
404
        self.txt.cursorColumn = 0
405
        self.txt.cursorRow = 0
406
        Text.processKeyDown(self.txt, evt)
407
        self.assertEqual(self.txt.text, ["Zero line", "One line line", "", "", "Five line"])
408
        self.assertEqual(self.txt.cursorColumn, 0)
409
        self.assertEqual(self.txt.cursorRow, 0)
410
411
    def test_delete(self):
412
        evt = self.Evt(pygame.K_DELETE)
413
        self.txt.cursorColumn = 3
414
        self.txt.cursorRow = 2
415
        for x in range(6):
416
            Text.processKeyDown(self.txt, evt)
417
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Two", "", "Five line"])
418
        self.assertEqual(self.txt.cursorColumn, 3)
419
        self.assertEqual(self.txt.cursorRow, 2)
420
        for x in range(3):
421
            Text.processKeyDown(self.txt, evt)
422
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Twoive line"])
423
        self.assertEqual(self.txt.cursorColumn, 3)
424
        self.assertEqual(self.txt.cursorRow, 2)
425
        # last character
426
        self.txt.cursorColumn = 11
427
        Text.processKeyDown(self.txt, evt)
428
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Twoive line"])
429
        self.assertEqual(self.txt.cursorColumn, 11)
430
        self.assertEqual(self.txt.cursorRow, 2)
431
432
    def test_return(self):
433
        evt = self.Evt(pygame.K_RETURN)
434
        self.txt.cursorColumn = 3
435
        self.txt.cursorRow = 2
436
        Text.processKeyDown(self.txt, evt)
437
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Two", " line", "", "", "Five line"])
438
        self.assertEqual(self.txt.cursorColumn, 0)
439
        self.assertEqual(self.txt.cursorRow, 3)
440
        Text.processKeyDown(self.txt, evt)
441
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Two", "", " line", "", "", "Five line"])
442
        self.assertEqual(self.txt.cursorColumn, 0)
443
        self.assertEqual(self.txt.cursorRow, 4)
444
445
    def test_up(self):
446
        evt = self.Evt(pygame.K_UP)
447
        self.txt.cursorColumn = 3
448
        self.txt.cursorRow = 5
449
        for x in range(3):
450
            Text.processKeyDown(self.txt, evt)
451
        self.assertEqual(self.txt.cursorColumn, 0)
452
        self.assertEqual(self.txt.cursorRow, 2)
453
        self.txt.cursorColumn = 3
454
        Text.processKeyDown(self.txt, evt)
455
        self.assertEqual(self.txt.cursorColumn, 3)
456
457
    def test_down(self):
458
        evt = self.Evt(pygame.K_DOWN)
459
        self.txt.cursorColumn = 3
460
        self.txt.cursorRow = 1
461
        Text.processKeyDown(self.txt, evt)
462
        self.assertEqual(self.txt.cursorColumn, 3)
463
        for x in range(3):
464
            Text.processKeyDown(self.txt, evt)
465
        self.assertEqual(self.txt.cursorColumn, 0)
466
        self.assertEqual(self.txt.cursorRow, 5)
467
468
    def test_left(self):
469
        evt = self.Evt(pygame.K_LEFT)
470
        self.txt.cursorColumn = 3
471
        self.txt.cursorRow = 2
472
        Text.processKeyDown(self.txt, evt)
473
        self.assertEqual(self.txt.cursorColumn, 2)
474
        for x in range(12):
475
            Text.processKeyDown(self.txt, evt)
476
        self.assertEqual(self.txt.cursorColumn, 9)
477
        self.assertEqual(self.txt.cursorRow, 0)
478
479
    def test_right(self):
480
        evt = self.Evt(pygame.K_RIGHT)
481
        self.txt.cursorColumn = 3
482
        self.txt.cursorRow = 2
483
        Text.processKeyDown(self.txt, evt)
484
        self.assertEqual(self.txt.cursorColumn, 4)
485
        for x in range(12):
486
            Text.processKeyDown(self.txt, evt)
487
        self.assertEqual(self.txt.cursorColumn, 5)
488
        self.assertEqual(self.txt.cursorRow, 5)
489
490
    def test_selection_movement(self):
491
        evt = self.Evt(pygame.K_RIGHT, mod=pygame.KMOD_RSHIFT)
492
        self.txt.cursorColumn = 3
493
        self.txt.cursorRow = 2
494
        for x in range(10):
495
            Text.processKeyDown(self.txt, evt)
496
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Two line", "", "", "Five line"])
497
        self.assertEqual(self.txt.selection.first, (2, 3))
498
        self.assertEqual(self.txt.selection.last, (5, 2))
499
500
        evt = self.Evt(pygame.K_LEFT, mod=pygame.KMOD_LSHIFT)
501
        for x in range(3):
502
            Text.processKeyDown(self.txt, evt)
503
        self.assertEqual(self.txt.selection.first, (2, 3))
504
        self.assertEqual(self.txt.selection.last, (4, 0))
505
506
        evt = self.Evt(pygame.K_UP, mod=pygame.KMOD_LSHIFT)
507
        for x in range(3):
508
            Text.processKeyDown(self.txt, evt)
509
        self.assertEqual(self.txt.selection.first, (1, 0))
510
        self.assertEqual(self.txt.selection.last, (2, 3))
511
512
    def test_selection_delete(self):
513
        evt = self.Evt(pygame.K_DOWN, mod=pygame.KMOD_LSHIFT)
514
        self.txt.cursorColumn = 3
515
        self.txt.cursorRow = 0
516
        Text.processKeyDown(self.txt, evt)
517
        Text.processKeyDown(self.txt, self.Evt(pygame.K_DELETE))
518
        self.assertEqual(self.txt.text, ["Zer line", "Two line", "", "", "Five line"])
519
        self.assertIs(self.txt.selection.first, None)
520
        self.assertIs(self.txt.selection.last, None)
521
522
        evt = self.Evt(pygame.K_RIGHT, mod=pygame.KMOD_LSHIFT)
523
        for x in range(3):
524
            Text.processKeyDown(self.txt, evt)
525
        Text.processKeyDown(self.txt, self.Evt(pygame.K_DELETE))
526
        self.assertEqual(self.txt.text, ["Zerne", "Two line", "", "", "Five line"])
527
528
        evt = self.Evt(pygame.K_DOWN, mod=pygame.KMOD_LSHIFT)
529
        for x in range(4):
530
            Text.processKeyDown(self.txt, evt)
531
        Text.processKeyDown(self.txt, self.Evt(pygame.K_DELETE))
532
        self.assertEqual(self.txt.text, ["ZerFive line"])
533
534
    def test_selection_backspace(self):
535
        evt = self.Evt(pygame.K_DOWN, mod=pygame.KMOD_LSHIFT)
536
        self.txt.cursorColumn = 3
537
        self.txt.cursorRow = 0
538
        Text.processKeyDown(self.txt, evt)
539
        Text.processKeyDown(self.txt, self.Evt(pygame.K_BACKSPACE))
540
        self.assertEqual(self.txt.text, ["Zer line", "Two line", "", "", "Five line"])
541
        self.assertIs(self.txt.selection.first, None)
542
        self.assertIs(self.txt.selection.last, None)
543
544
    def test_selection_return(self):
545
        evt = self.Evt(pygame.K_DOWN, mod=pygame.KMOD_LSHIFT)
546
        self.txt.cursorColumn = 3
547
        self.txt.cursorRow = 0
548
        Text.processKeyDown(self.txt, evt)
549
        Text.processKeyDown(self.txt, self.Evt(pygame.K_RETURN))
550
        self.assertEqual(self.txt.text, ["Zer", " line", "Two line", "", "", "Five line"])
551
        self.assertIs(self.txt.selection.first, None)
552
        self.assertIs(self.txt.selection.last, None)
553
554
    def test_move_word_left(self):
555
        evt = self.Evt(pygame.K_LEFT, mod=pygame.KMOD_LCTRL)
556
        self.txt.cursorColumn = 3
557
        self.txt.cursorRow = 5
558
        for x in range(2):
559
            Text.processKeyDown(self.txt, evt)
560
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Two line", "", "", "Five line"])
561
        self.assertEqual(self.txt.cursorColumn, 4)
562
        self.assertEqual(self.txt.cursorRow, 2)
563
        Text.processKeyDown(self.txt, evt)
564
        self.assertEqual(self.txt.cursorColumn, 0)
565
        self.assertEqual(self.txt.cursorRow, 2)
566
567
    def test_move_word_right(self):
568
        evt = self.Evt(pygame.K_RIGHT, mod=pygame.KMOD_RCTRL)
569
        self.txt.cursorColumn = 1
570
        self.txt.cursorRow = 0
571
        Text.processKeyDown(self.txt, evt)
572
        self.assertEqual(self.txt.text, ["Zero line", "One line", "Two line", "", "", "Five line"])
573
        self.assertEqual(self.txt.cursorColumn, 4)
574
        self.assertEqual(self.txt.cursorRow, 0)
575
        for x in range(4):
576
            Text.processKeyDown(self.txt, evt)
577
        self.assertEqual(self.txt.cursorColumn, 3)
578
        self.assertEqual(self.txt.cursorRow, 2)
579
        for x in range(2):
580
            Text.processKeyDown(self.txt, evt)
581
        self.assertEqual(self.txt.cursorColumn, 4)
582
        self.assertEqual(self.txt.cursorRow, 5)
583