~ubuntu-branches/debian/experimental/spyder/experimental

« back to all changes in this revision

Viewing changes to spyderlib/widgets/sourcecode/base.py

  • Committer: Package Import Robot
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2014-05-29 09:06:26 UTC
  • mfrom: (1.1.21) (18.1.6 sid)
  • Revision ID: package-import@ubuntu.com-20140529090626-f58t82g0n5iewaxu
Tags: 2.3.0~rc+dfsg-1~experimental2
* Add spyder-common binary package for all the python2,3 common files
* debian/path
  - 0001-fix-documentation-installation.patch (deleted)
  + 0001-fix-spyderlib-path.patch (new)

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
 
14
14
import os
15
15
import re
16
 
import string
 
16
import sys
17
17
 
18
18
from spyderlib.qt.QtGui import (QTextCursor, QColor, QFont, QApplication,
19
19
                                QTextEdit, QTextCharFormat, QToolTip,
20
20
                                QListWidget, QPlainTextEdit, QPalette,
21
 
                                QMainWindow, QTextOption, QMouseEvent)
22
 
from spyderlib.qt.QtCore import QPoint, SIGNAL, Qt, QEventLoop, QEvent
 
21
                                QMainWindow, QTextOption, QMouseEvent,
 
22
                                QTextFormat)
 
23
from spyderlib.qt.QtCore import SIGNAL, Qt, QEventLoop, QEvent, QPoint
 
24
from spyderlib.qt.compat import to_qvariant
23
25
 
24
26
 
25
27
# Local imports
26
28
from spyderlib.widgets.sourcecode.terminal import ANSIEscapeCodeHandler
27
29
from spyderlib.widgets.mixins import BaseEditMixin
 
30
from spyderlib.widgets.calltip import CallTipWidget
 
31
from spyderlib.py3compat import to_text_string, str_lower
 
32
 
 
33
CELL_SEPARATORS = ('#%%', '# %%', '# <codecell>', '# In[')
28
34
 
29
35
 
30
36
class CompletionWidget(QListWidget):
35
41
        self.textedit = parent
36
42
        self.completion_list = None
37
43
        self.case_sensitive = False
38
 
        self.show_single = None
39
44
        self.enter_select = None
40
45
        self.hide()
41
46
        self.connect(self, SIGNAL("itemActivated(QListWidgetItem*)"),
46
51
        self.setFont(font)
47
52
        
48
53
    def show_list(self, completion_list, automatic=True):
49
 
        if not self.show_single and len(completion_list) == 1 and not automatic:
 
54
        if len(completion_list) == 1 and not automatic:
50
55
            self.textedit.insert_completion(completion_list[0])
51
56
            return
52
57
        
104
109
            point = ancestor.mapFromGlobal(point)
105
110
        self.move(point)
106
111
        
107
 
        if unicode(self.textedit.completion_text):
 
112
        if to_text_string(self.textedit.completion_text):
108
113
            # When initialized, if completion text is not empty, we need 
109
114
            # to update the displayed list:
110
115
            self.update_current()
140
145
            QListWidget.keyPressEvent(self, event)
141
146
            
142
147
    def update_current(self):
143
 
        completion_text = unicode(self.textedit.completion_text)
 
148
        completion_text = to_text_string(self.textedit.completion_text)
144
149
        if completion_text:
145
150
            for row, completion in enumerate(self.completion_list):
146
151
                if not self.case_sensitive:
156
161
    
157
162
    def focusOutEvent(self, event):
158
163
        event.ignore()
159
 
        self.hide()
 
164
        # Don't hide it on Mac when main window loses focus because
 
165
        # keyboard input is lost
 
166
        # Fixes Issue 1318
 
167
        if sys.platform == "darwin":
 
168
            if event.reason() != Qt.ActiveWindowFocusReason:
 
169
                self.hide()
 
170
        else:
 
171
            self.hide()
160
172
        
161
173
    def item_selected(self, item=None):
162
174
        if item is None:
163
175
            item = self.currentItem()
164
 
        self.textedit.insert_completion( unicode(item.text()) )
 
176
        self.textedit.insert_completion( to_text_string(item.text()) )
165
177
        self.hide()
166
178
 
167
179
 
168
180
class TextEditBaseWidget(QPlainTextEdit, BaseEditMixin):
169
 
    """
170
 
    Text edit base widget
171
 
    """
 
181
    """Text edit base widget"""
172
182
    BRACE_MATCHING_SCOPE = ('sof', 'eof')
173
183
    
174
184
    def __init__(self, parent=None):
196
206
        self.completion_widget = CompletionWidget(self, parent)
197
207
        self.codecompletion_auto = False
198
208
        self.codecompletion_case = True
199
 
        self.codecompletion_single = False
200
209
        self.codecompletion_enter = False
201
210
        self.calltips = True
202
211
        self.calltip_position = None
203
 
        self.calltip_size = 600
204
 
        self.calltip_font = QFont()
 
212
        self.has_cell_separators = False        
205
213
        self.completion_text = ""
 
214
        self.calltip_widget = CallTipWidget(self, hide_timer_on=True)
 
215
        
 
216
        # The color values may be overridden by the syntax highlighter 
 
217
        # Highlight current line color
 
218
        self.currentline_color = QColor(Qt.red).lighter(190)
 
219
        self.currentcell_color = QColor(Qt.red).lighter(194)
206
220
 
207
221
        # Brace matching
208
222
        self.bracepos = None
209
223
        self.matched_p_color = QColor(Qt.green)
210
224
        self.unmatched_p_color = QColor(Qt.red)
211
225
        
212
 
    def setup_calltips(self, size=None, font=None):
213
 
        self.calltip_size = size
214
 
        self.calltip_font = font
215
 
    
216
226
    def setup_completion(self, size=None, font=None):
217
227
        self.completion_widget.setup_appearance(size, font)
218
228
        
228
238
        palette.setColor(QPalette.Base, background)
229
239
        palette.setColor(QPalette.Text, foreground)
230
240
        self.setPalette(palette)
231
 
        
232
 
    #------Line number area
233
 
    def get_linenumberarea_width(self):
234
 
        """Return line number area width"""
235
 
        # Implemented in CodeEditor, but needed here for completion widget
236
 
        return 0
237
 
        
 
241
 
 
242
 
238
243
    #------Extra selections
239
244
    def get_extra_selections(self, key):
240
245
        return self.extra_selections_dict.get(key, [])
244
249
        
245
250
    def update_extra_selections(self):
246
251
        extra_selections = []
247
 
        for _key, extra in self.extra_selections_dict.iteritems():
248
 
            extra_selections.extend(extra)
 
252
        for key, extra in list(self.extra_selections_dict.items()):
 
253
            if key == 'current_line' or key == 'current_cell':
 
254
                # Python 3 compatibility (weird): current line has to be 
 
255
                # highlighted first
 
256
                extra_selections = extra + extra_selections
 
257
            else:
 
258
                extra_selections += extra
249
259
        self.setExtraSelections(extra_selections)
250
260
        
251
261
    def clear_extra_selections(self, key):
259
269
                  self.document().isModified())
260
270
 
261
271
 
 
272
    #------Highlight current line
 
273
    def highlight_current_line(self):
 
274
        """Highlight current line"""
 
275
        selection = QTextEdit.ExtraSelection()
 
276
        selection.format.setProperty(QTextFormat.FullWidthSelection,
 
277
                                     to_qvariant(True))
 
278
        selection.format.setBackground(self.currentline_color)
 
279
        selection.cursor = self.textCursor()
 
280
        selection.cursor.clearSelection()
 
281
        self.set_extra_selections('current_line', [selection])
 
282
        self.update_extra_selections()
 
283
 
 
284
    def unhighlight_current_line(self):
 
285
        """Unhighlight current line"""
 
286
        self.clear_extra_selections('current_line')
 
287
 
 
288
    #------Highlight current cell
 
289
    def highlight_current_cell(self):
 
290
        """Highlight current cell"""
 
291
        selection = QTextEdit.ExtraSelection()
 
292
        selection.format.setProperty(QTextFormat.FullWidthSelection,
 
293
                                     to_qvariant(True))
 
294
        selection.format.setBackground(self.currentcell_color)
 
295
        selection.cursor, whole_file_selected, whole_screen_selected =\
 
296
            self.select_current_cell_in_visible_portion()
 
297
        if whole_file_selected: 
 
298
            self.clear_extra_selections('current_cell')
 
299
        elif whole_screen_selected:
 
300
            if self.has_cell_separators:
 
301
                self.set_extra_selections('current_cell', [selection])
 
302
                self.update_extra_selections()
 
303
            else:
 
304
                self.clear_extra_selections('current_cell')
 
305
        else:
 
306
            self.set_extra_selections('current_cell', [selection])
 
307
            self.update_extra_selections()
 
308
 
 
309
    def unhighlight_current_cell(self):
 
310
        """Unhighlight current cell"""
 
311
        self.clear_extra_selections('current_cell')
 
312
 
262
313
    #------Brace matching
263
314
    def find_brace_match(self, position, brace, forward):
264
315
        start_pos, end_pos = self.BRACE_MATCHING_SCOPE
329
380
            return
330
381
        cursor.movePosition(QTextCursor.PreviousCharacter,
331
382
                            QTextCursor.KeepAnchor)
332
 
        text = unicode(cursor.selectedText())
 
383
        text = to_text_string(cursor.selectedText())
333
384
        pos1 = cursor.position()
334
385
        if text in (')', ']', '}'):
335
386
            pos2 = self.find_brace_match(pos1, text, forward=False)
355
406
        self.codecompletion_case = state
356
407
        self.completion_widget.case_sensitive = state
357
408
        
358
 
    def set_codecompletion_single(self, state):
359
 
        """Show single completion"""
360
 
        self.codecompletion_single = state
361
 
        self.completion_widget.show_single = state
362
 
        
363
409
    def set_codecompletion_enter(self, state):
364
410
        """Enable Enter key to select completion"""
365
411
        self.codecompletion_enter = state
393
439
        
394
440
 
395
441
    #------Text: get, set, ...
396
 
    def get_executable_text(self):
397
 
        """Return selected text or current line as an processed text,
 
442
    def get_selection_as_executable_code(self):
 
443
        """Return selected text as a processed text,
398
444
        to be executable in a Python/IPython interpreter"""
399
 
        no_selection = not self.has_selected_text()
400
 
        if no_selection:
401
 
            self.select_current_block()
402
 
        
403
445
        ls = self.get_line_separator()
404
446
        
405
447
        _indent = lambda line: len(line)-len(line.lstrip())
406
448
        
407
449
        line_from, line_to = self.get_selection_bounds()
408
450
        text = self.get_selected_text()
 
451
        if not text:
 
452
            return
409
453
 
410
454
        lines = text.split(ls)
411
455
        if len(lines) > 1:
412
456
            # Multiline selection -> eventually fixing indentation
413
457
            original_indent = _indent(self.get_text_line(line_from))
414
458
            text = (" "*(original_indent-_indent(lines[0])))+text
415
 
        
416
 
        # If there is a common indent to all lines, remove it
 
459
            
 
460
        # If there is a common indent to all lines, find it.
 
461
        # Moving from bottom line to top line ensures that blank
 
462
        # lines inherit the indent of the line *below* it,
 
463
        # which is the desired behavior.
417
464
        min_indent = 999
418
 
        for line in text.split(ls):
 
465
        current_indent = 0
 
466
        lines = text.split(ls)
 
467
        for i in range(len(lines)-1, -1, -1):
 
468
            line = lines[i]
419
469
            if line.strip():
420
 
                min_indent = min(_indent(line), min_indent)
 
470
                current_indent = _indent(line)
 
471
                min_indent = min(current_indent, min_indent)
 
472
            else:
 
473
                lines[i] = ' ' * current_indent
421
474
        if min_indent:
422
 
            text = ls.join([line[min_indent:] for line in text.split(ls)])
 
475
            lines = [line[min_indent:] for line in lines]
 
476
 
 
477
        # Remove any leading whitespace or comment lines
 
478
        # since they confuse the reserved word detector that follows below
 
479
        while lines:
 
480
            first_line = lines[0].lstrip()
 
481
            if first_line == '' or first_line[0] == '#':
 
482
                lines.pop(0)
 
483
            else:
 
484
                break
423
485
        
424
 
        # Add an EOL character if a block stars with various Python
425
 
        # reserved words, so that it gets evaluated automatically
 
486
        # Add an EOL character after indentation blocks that start with some 
 
487
        # Python reserved words, so that it gets evaluated automatically
426
488
        # by the console
427
 
        first_line = lines[0].lstrip()
428
 
        last_line = self.get_text_line(line_to).strip()
429
 
        words = ['def', 'for', 'if', 'while', 'try', 'with', 'class']
430
 
        if any([first_line.startswith(w) for w in words]):
431
 
            text += ls
432
 
            if last_line != '':
433
 
                text += ls
 
489
        varname = re.compile('[a-zA-Z0-9_]*') # matches valid variable names
 
490
        maybe = False
 
491
        nextexcept = ()
 
492
        for n, line in enumerate(lines):
 
493
            if not _indent(line):
 
494
                word = varname.match(line).group()
 
495
                if maybe and word not in nextexcept:
 
496
                    lines[n-1] += ls
 
497
                    maybe = False
 
498
                if word:
 
499
                    if word in ('def', 'for', 'while', 'with', 'class'):
 
500
                        maybe = True
 
501
                        nextexcept = ()
 
502
                    elif word == 'if':
 
503
                        maybe = True
 
504
                        nextexcept = ('elif', 'else')
 
505
                    elif word == 'try':
 
506
                        maybe = True
 
507
                        nextexcept = ('except', 'finally')
 
508
        if maybe:
 
509
            if lines[-1].strip() == '':
 
510
                lines[-1] += ls
 
511
            else:
 
512
                lines.append(ls)
 
513
        
 
514
        return ls.join(lines)
434
515
 
435
 
        if no_selection:
436
 
            self.setFocus()
437
 
            self.clear_selection()
438
 
        
 
516
    def get_cell_as_executable_code(self):
 
517
        """Return cell contents as executable code"""
 
518
        start_pos, end_pos = self.__save_selection()
 
519
        cursor, whole_file_selected = self.select_current_cell()
 
520
        if not whole_file_selected:
 
521
            self.setTextCursor(cursor)
 
522
        text = self.get_selection_as_executable_code()
 
523
        self.__restore_selection(start_pos, end_pos)
439
524
        return text
 
525
 
 
526
    def is_cell_separator(self, cursor=None, block=None):
 
527
        """Return True if cursor (or text block) is on a block separator"""
 
528
        assert cursor is not None or block is not None
 
529
        if cursor is not None:
 
530
            cursor0 = QTextCursor(cursor)
 
531
            cursor0.select(QTextCursor.BlockUnderCursor)
 
532
            text = to_text_string(cursor0.selectedText())
 
533
        else:
 
534
            text = to_text_string(block.text())
 
535
        return text.lstrip().startswith(CELL_SEPARATORS)
 
536
 
 
537
    def select_current_cell(self):
 
538
        """Select cell under cursor
 
539
        cell = group of lines separated by CELL_SEPARATORS
 
540
        returns the textCursor and a boolean indicating if the
 
541
        entire file is selected"""
 
542
        cursor = self.textCursor()
 
543
        cursor.movePosition(QTextCursor.StartOfBlock)
 
544
        cur_pos = prev_pos = cursor.position()
 
545
 
 
546
        # Moving to the next line that is not a separator, if we are
 
547
        # exactly at one of them
 
548
        while self.is_cell_separator(cursor):
 
549
            cursor.movePosition(QTextCursor.NextBlock)
 
550
            prev_pos = cur_pos
 
551
            cur_pos = cursor.position()
 
552
            if cur_pos == prev_pos:
 
553
                return cursor, False
 
554
        prev_pos = cur_pos
 
555
        # If not, move backwards to find the previous separator
 
556
        while not self.is_cell_separator(cursor):
 
557
            cursor.movePosition(QTextCursor.PreviousBlock)
 
558
            prev_pos = cur_pos
 
559
            cur_pos = cursor.position()
 
560
            if cur_pos == prev_pos:
 
561
                if self.is_cell_separator(cursor):
 
562
                    return cursor, False
 
563
                else:
 
564
                    break
 
565
        cursor.setPosition(prev_pos)
 
566
        cell_at_file_start = cursor.atStart()
 
567
        # Once we find it (or reach the beginning of the file)
 
568
        # move to the next separator (or the end of the file)
 
569
        # so we can grab the cell contents
 
570
        while not self.is_cell_separator(cursor):
 
571
            cursor.movePosition(QTextCursor.NextBlock,
 
572
                                QTextCursor.KeepAnchor)
 
573
            cur_pos = cursor.position()
 
574
            if cur_pos == prev_pos:
 
575
                cursor.movePosition(QTextCursor.EndOfBlock,
 
576
                                    QTextCursor.KeepAnchor)
 
577
                break
 
578
            prev_pos = cur_pos
 
579
        cell_at_file_end = cursor.atEnd()
 
580
        return cursor, cell_at_file_start and cell_at_file_end
440
581
    
 
582
    def select_current_cell_in_visible_portion(self):
 
583
        """Select cell under cursor in the visible portion of the file
 
584
        cell = group of lines separated by CELL_SEPARATORS
 
585
        returns 
 
586
         -the textCursor
 
587
         -a boolean indicating if the entire file is selected
 
588
         -a boolean indicating if the entire visible portion of the file is selected"""
 
589
        cursor = self.textCursor()
 
590
        cursor.movePosition(QTextCursor.StartOfBlock)
 
591
        cur_pos = prev_pos = cursor.position()
 
592
 
 
593
        beg_pos = self.cursorForPosition(QPoint(0, 0)).position()
 
594
        bottom_right = QPoint(self.viewport().width() - 1, 
 
595
                              self.viewport().height() - 1)
 
596
        end_pos = self.cursorForPosition(bottom_right).position()
 
597
 
 
598
        # Moving to the next line that is not a separator, if we are
 
599
        # exactly at one of them
 
600
        while self.is_cell_separator(cursor):
 
601
            cursor.movePosition(QTextCursor.NextBlock)
 
602
            prev_pos = cur_pos
 
603
            cur_pos = cursor.position()
 
604
            if cur_pos == prev_pos:
 
605
                return cursor, False, False
 
606
        prev_pos = cur_pos
 
607
        # If not, move backwards to find the previous separator
 
608
        while not self.is_cell_separator(cursor)\
 
609
          and cursor.position() >= beg_pos:
 
610
            cursor.movePosition(QTextCursor.PreviousBlock)
 
611
            prev_pos = cur_pos
 
612
            cur_pos = cursor.position()
 
613
            if cur_pos == prev_pos:
 
614
                if self.is_cell_separator(cursor):
 
615
                    return cursor, False, False
 
616
                else:
 
617
                    break
 
618
        cell_at_screen_start = cursor.position() <= beg_pos
 
619
        cursor.setPosition(prev_pos)
 
620
        cell_at_file_start = cursor.atStart()
 
621
        # Selecting cell header
 
622
        if not cell_at_file_start:
 
623
            cursor.movePosition(QTextCursor.PreviousBlock)
 
624
            cursor.movePosition(QTextCursor.NextBlock,
 
625
                                QTextCursor.KeepAnchor)
 
626
        # Once we find it (or reach the beginning of the file)
 
627
        # move to the next separator (or the end of the file)
 
628
        # so we can grab the cell contents
 
629
        while not self.is_cell_separator(cursor)\
 
630
          and cursor.position() <= end_pos:
 
631
            cursor.movePosition(QTextCursor.NextBlock,
 
632
                                QTextCursor.KeepAnchor)
 
633
            cur_pos = cursor.position()
 
634
            if cur_pos == prev_pos:
 
635
                cursor.movePosition(QTextCursor.EndOfBlock,
 
636
                                    QTextCursor.KeepAnchor)
 
637
                break
 
638
            prev_pos = cur_pos
 
639
        cell_at_file_end = cursor.atEnd()
 
640
        cell_at_screen_end = cursor.position() >= end_pos
 
641
        return cursor,\
 
642
               cell_at_file_start and cell_at_file_end,\
 
643
               cell_at_screen_start and cell_at_screen_end
 
644
 
 
645
    def go_to_next_cell(self):
 
646
        """Go to the next cell of lines"""
 
647
        cursor = self.textCursor()
 
648
        cursor.movePosition(QTextCursor.NextBlock)
 
649
        cur_pos = prev_pos = cursor.position()
 
650
        while not self.is_cell_separator(cursor):
 
651
            # Moving to the next code cell
 
652
            cursor.movePosition(QTextCursor.NextBlock)
 
653
            prev_pos = cur_pos
 
654
            cur_pos = cursor.position()
 
655
            if cur_pos == prev_pos:
 
656
                return
 
657
        self.setTextCursor(cursor)
 
658
 
441
659
    def get_line_count(self):
442
660
        """Return document total line number"""
443
661
        return self.blockCount()
444
 
    
 
662
 
 
663
    def __save_selection(self):
 
664
        """Save current cursor selection and return position bounds"""
 
665
        cursor = self.textCursor()
 
666
        return cursor.selectionStart(), cursor.selectionEnd()
 
667
 
 
668
    def __restore_selection(self, start_pos, end_pos):
 
669
        """Restore cursor selection from position bounds"""
 
670
        cursor = self.textCursor()
 
671
        cursor.setPosition(start_pos)
 
672
        cursor.setPosition(end_pos, QTextCursor.KeepAnchor)
 
673
        self.setTextCursor(cursor)
 
674
 
445
675
    def __duplicate_line_or_selection(self, after_current_line=True):
446
676
        """Duplicate current line or selected text"""
447
677
        cursor = self.textCursor()
448
678
        cursor.beginEditBlock()
449
 
        orig_sel = start_pos, end_pos = (cursor.selectionStart(),
450
 
                                         cursor.selectionEnd())
451
 
        if unicode(cursor.selectedText()):
 
679
        start_pos, end_pos = self.__save_selection()
 
680
        if to_text_string(cursor.selectedText()):
452
681
            cursor.setPosition(end_pos)
453
682
            # Check if end_pos is at the start of a block: if so, starting
454
683
            # changes from the previous block
455
684
            cursor.movePosition(QTextCursor.StartOfBlock,
456
685
                                QTextCursor.KeepAnchor)
457
 
            if not unicode(cursor.selectedText()):
 
686
            if not to_text_string(cursor.selectedText()):
458
687
                cursor.movePosition(QTextCursor.PreviousBlock)
459
688
                end_pos = cursor.position()
460
689
            
475
704
            # Moving cursor before current line/selected text
476
705
            cursor.setPosition(start_pos)
477
706
            cursor.movePosition(QTextCursor.StartOfBlock)
478
 
            orig_sel = (orig_sel[0]+len(text), orig_sel[1]+len(text))
 
707
            start_pos += len(text)
 
708
            end_pos += len(text)
479
709
        
480
710
        cursor.insertText(text)
481
711
        cursor.endEditBlock()
482
 
        cursor.setPosition(orig_sel[0])
483
 
        cursor.setPosition(orig_sel[1], QTextCursor.KeepAnchor)
484
712
        self.setTextCursor(cursor)
 
713
        self.__restore_selection(start_pos, end_pos)
485
714
    
486
715
    def duplicate_line(self):
487
716
        """
501
730
        """Move current line or selected text"""
502
731
        cursor = self.textCursor()
503
732
        cursor.beginEditBlock()
504
 
        orig_sel = start_pos, end_pos = (cursor.selectionStart(),
505
 
                                         cursor.selectionEnd())
506
 
        if unicode(cursor.selectedText()):
 
733
        start_pos, end_pos = self.__save_selection()
 
734
        if to_text_string(cursor.selectedText()):
507
735
            # Check if start_pos is at the start of a block
508
736
            cursor.setPosition(start_pos)
509
737
            cursor.movePosition(QTextCursor.StartOfBlock)
514
742
            # changes from the previous block
515
743
            cursor.movePosition(QTextCursor.StartOfBlock,
516
744
                                QTextCursor.KeepAnchor)
517
 
            if unicode(cursor.selectedText()):
 
745
            if to_text_string(cursor.selectedText()):
518
746
                cursor.movePosition(QTextCursor.NextBlock)
519
747
                end_pos = cursor.position()
520
748
        else:
525
753
        cursor.setPosition(start_pos)
526
754
        cursor.setPosition(end_pos, QTextCursor.KeepAnchor)
527
755
        
528
 
        sel_text = unicode(cursor.selectedText())
 
756
        sel_text = to_text_string(cursor.selectedText())
529
757
        cursor.removeSelectedText()
530
758
        
531
759
        if after_current_line:
532
 
            text = unicode(cursor.block().text())
533
 
            orig_sel = (orig_sel[0]+len(text)+1, orig_sel[1]+len(text)+1)
 
760
            text = to_text_string(cursor.block().text())
 
761
            start_pos += len(text)+1
 
762
            end_pos += len(text)+1
534
763
            cursor.movePosition(QTextCursor.NextBlock)
535
764
        else:
536
765
            cursor.movePosition(QTextCursor.PreviousBlock)
537
 
            text = unicode(cursor.block().text())
538
 
            orig_sel = (orig_sel[0]-len(text)-1, orig_sel[1]-len(text)-1)
 
766
            text = to_text_string(cursor.block().text())
 
767
            start_pos -= len(text)+1
 
768
            end_pos -= len(text)+1
539
769
        cursor.insertText(sel_text)
540
770
 
541
771
        cursor.endEditBlock()
542
 
 
543
 
        cursor.setPosition(orig_sel[0])
544
 
        cursor.setPosition(orig_sel[1], QTextCursor.KeepAnchor)
545
772
        self.setTextCursor(cursor)
 
773
        self.__restore_selection(start_pos, end_pos)
546
774
    
547
775
    def move_line_up(self):
548
776
        """Move up current line or selected text"""
588
816
 
589
817
 
590
818
    #------Code completion / Calltips
591
 
    def show_calltip(self, title, text, color='#2D62FF',
592
 
                     at_line=None, at_position=None):
593
 
        """Show calltip"""
594
 
        if text is None or len(text) == 0:
595
 
            return
596
 
        # Saving cursor position:
597
 
        if at_position is None:
598
 
            at_position = self.get_position('cursor')
599
 
        self.calltip_position = at_position
600
 
        # Preparing text:
601
 
        weight = 'bold' if self.calltip_font.bold() else 'normal'
602
 
        size = self.calltip_font.pointSize()
603
 
        family = self.calltip_font.family()
604
 
        format1 = '<div style=\'font-size: %spt; color: %s\'>' % (size, color)
605
 
        format2 = '<hr><div style=\'font-family: "%s"; font-size: %spt; font-weight: %s\'>' % (family, size, weight)
606
 
        if isinstance(text, list):
607
 
            text = "\n    ".join(text)
608
 
        text = text.replace('\n', '<br>')
609
 
        if len(text) > self.calltip_size:
610
 
            text = text[:self.calltip_size] + " ..."
611
 
        tiptext = format1 + ('<b>%s</b></div>' % title) \
612
 
                  + format2 + text + "</div>"
613
 
        # Showing tooltip at cursor position:
614
 
        cx, cy = self.get_coordinates('cursor')
615
 
        if at_line is not None:
616
 
            cx = 5
617
 
            cursor = QTextCursor(self.document().findBlockByNumber(at_line-1))
618
 
            cy = self.cursorRect(cursor).top()
619
 
        point = self.mapToGlobal(QPoint(cx, cy))
620
 
        point.setX(point.x()+self.get_linenumberarea_width())
621
 
        QToolTip.showText(point, tiptext)
622
 
 
623
819
    def hide_tooltip_if_necessary(self, key):
624
820
        """Hide calltip when necessary"""
625
821
        try:
627
823
            before = self.is_cursor_before(self.calltip_position,
628
824
                                           char_offset=1)
629
825
            other = key in (Qt.Key_ParenRight, Qt.Key_Period, Qt.Key_Tab)
630
 
            if calltip_char not in ('?','(') or before or other:
 
826
            if calltip_char not in ('?', '(') or before or other:
631
827
                QToolTip.hideText()
632
828
        except (IndexError, TypeError):
633
829
            QToolTip.hideText()
648
844
        self.completion_text = completion_text
649
845
        # Sorting completion list (entries starting with underscore are 
650
846
        # put at the end of the list):
651
 
        underscore = set([comp for comp in completions if comp.startswith('_')])
652
 
        completions = sorted(set(completions)-underscore, key=string.lower)+\
653
 
                      sorted(underscore, key=string.lower)
 
847
        underscore = set([comp for comp in completions
 
848
                          if comp.startswith('_')])
 
849
        completions = sorted(set(completions)-underscore, key=str_lower)+\
 
850
                      sorted(underscore, key=str_lower)
654
851
        self.show_completion_widget(completions, automatic=automatic)
655
852
        
656
853
    def select_completion_list(self):
736
933
    def mousePressEvent(self, event):
737
934
        """Reimplement Qt method"""
738
935
        if os.name != 'posix' and event.button() == Qt.MidButton:
 
936
            self.calltip_widget.hide()
739
937
            self.setFocus()
740
938
            event = QMouseEvent(QEvent.MouseButtonPress, event.pos(),
741
939
                                Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
743
941
            QPlainTextEdit.mouseReleaseEvent(self, event)
744
942
            self.paste()
745
943
        else:
 
944
            self.calltip_widget.hide()
746
945
            QPlainTextEdit.mousePressEvent(self, event)
747
946
 
748
947
    def focusInEvent(self, event):
758
957
    
759
958
    def wheelEvent(self, event):
760
959
        """Reimplemented to emit zoom in/out signals when Ctrl is pressed"""
761
 
        if event.modifiers() & Qt.ControlModifier:
762
 
            if event.delta() < 0:
763
 
                self.emit(SIGNAL("zoom_out()"))
764
 
            elif event.delta() > 0:
765
 
                self.emit(SIGNAL("zoom_in()"))
766
 
            return
 
960
        # This feature is disabled on MacOS, see Issue 1510:
 
961
        # http://code.google.com/p/spyderlib/issues/detail?id=1510
 
962
        if sys.platform != 'darwin':
 
963
            if event.modifiers() & Qt.ControlModifier:
 
964
                if event.delta() < 0:
 
965
                    self.emit(SIGNAL("zoom_out()"))
 
966
                elif event.delta() > 0:
 
967
                    self.emit(SIGNAL("zoom_in()"))
 
968
                return
767
969
        QPlainTextEdit.wheelEvent(self, event)
768
970
 
 
971
    def paintEvent(self, event):
 
972
        """Reimplemented to recompute cell highlighting"""
 
973
        self.highlight_current_cell()
 
974
        QPlainTextEdit.paintEvent(self, event)
 
975
 
769
976
 
770
977
class QtANSIEscapeCodeHandler(ANSIEscapeCodeHandler):
771
978
    def __init__(self):
1019
1226
                              light_background=self.light_background,
1020
1227
                              is_default=style is self.default_style)
1021
1228
        self.ansi_handler.set_base_format(self.default_style.format)
1022
 
        
 
 
b'\\ No newline at end of file'
 
1229