Exemplo n.º 1
0
 def __init__(self, *args, **kwargs):
     loader = QuietLoaders()
     default_theme = loader.load_default_theme()
     settings = loader.load_settings_data()
     settings['menu_active_bg'] = default_theme['menu_bg_active']
     settings['menu_active_fg'] = default_theme['menu_fg_active']
     settings['menu_fg'] = default_theme['comment_color']
     settings['menu_bg'] = default_theme['bg_color']
     super().__init__(bg=settings["menu_bg"],
                      fg=settings['menu_fg'],
                      activeforeground=settings['menu_active_fg'],
                      activebackground=settings['menu_active_bg'],
                      activeborderwidth=0,
                      bd=0,
                      *args, **kwargs)
Exemplo n.º 2
0
class QuietText(tk.Frame):
    def __init__(self, *args, **kwargs):
        """The main class for bringing the whole shebang together"""
        tk.Frame.__init__(self, *args, **kwargs)
        master.title('untitled - Quiet Text')
        # defined size of the editer window
        master.geometry('700x785')
        self.configure(bg='black')
        self.loader = QuietLoaders()
        self.operating_system = system()
        self.quiet_icon_path = self.loader.resource_path(os.path.join('data', 'q.png'))
        self.icon = tk.PhotoImage(file = self.quiet_icon_path)
        master.iconphoto(False, self.icon)

        # start editor according to defined settings in settings.yaml

        self.settings = self.loader.load_settings_data()

        self.browser = self.settings['web_browser']
        self.font_family = self.settings['font_family']
        self.bg_color = self.settings['textarea_background_color']
        self.font_color = self.settings['font_color']
        self.tab_size = self.settings['tab_size']
        self.font_size = int(self.settings['font_size'])
        self.top_spacing = self.settings['text_top_lineheight']
        self.bottom_spacing = self.settings['text_bottom_lineheight']
        self.padding_x = self.settings['textarea_padding_x']
        self.padding_y = self.settings['textarea_padding_y']
        self.insertion_blink = 300 if self.settings['insertion_blink'] else 0
        self.insertion_color = self.settings['insertion_color']
        self.tab_size_spaces = self.settings['tab_size']
        self.text_wrap = self.settings['text_wrap']
        self.autoclose_parentheses = self.settings['autoclose_parentheses']
        self.autoclose_curlybraces = self.settings['autoclose_curlybraces']
        self.autoclose_squarebrackets = self.settings['autoclose_squarebrackets']
        self.autoclose_singlequotes = self.settings['autoclose_singlequotes']
        self.autoclose_doublequotes = self.settings['autoclose_doublequotes']
        self.border = self.settings['textarea_border']
        self.text_selection_bg = self.settings['text_selection_bg']
        self.scrollx_clr = self.settings['horizontal_scrollbar_color']
        self.troughx_clr = self.settings['horizontal_scrollbar_trough_color']
        self.scrollx_width = self.settings['horizontal_scrollbar_width']
        self.scrollx_active_bg = self.settings['horizontal_scrollbar_active_bg']
        self.scrolly_clr = self.settings['vertical_scrollbar_color']
        self.troughy_clr = self.settings['vertical_scrollbar_trough_color']
        self.scrolly_width = self.settings['vertical_scrollbar_width']
        self.scrolly_active_bg = self.settings['vertical_scrollbar_active_bg']
        self.menubar_active_fg = self.settings['menubar_active_fg']
        self.menubar_active_bg = self.settings['menubar_active_bg']
        self.menu_fg = self.settings['menu_fg']
        self.menu_bg = self.settings['menu_bg']
        self.current_line_symbol = self.settings['current_line_indicator_symbol']
        self.current_line_indicator = self.settings['current_line_indicator']

        #configuration of the file dialog text colors.
        self.font_style = tk_font.Font(family=self.font_family,
                                       size=self.font_size)

        self.italics = tk_font.Font(family=self.font_family, slant='italic', size=self.font_size)
        self.bold = tk_font.Font(family=self.font_family, weight='bold', size=self.font_size)
        self.header1 = tk_font.Font(family=self.font_family, weight='bold', size=self.font_size + 15)
        self.header2 = tk_font.Font(family=self.font_family, weight='bold', size=self.font_size + 7)

        self.master = master
        self.filename = os.getcwd()
        self.dirname = os.getcwd()

        self.previous_file = None
        self.textarea = CustomText(self)

        self.scrolly = tk.Scrollbar(
            master,
            command=self.textarea.yview,
            bg=self.scrolly_clr,
            troughcolor=self.troughy_clr,
            bd=0,
            width=self.scrolly_width,
            highlightthickness=0,
            activebackground=self.scrolly_active_bg,
            orient='vertical')

        self.scrollx = tk.Scrollbar(
            master,
            command=self.textarea.xview,
            bg=self.scrollx_clr,
            troughcolor=self.troughx_clr,
            bd=0,
            width=self.scrollx_width,
            highlightthickness=0,
            activebackground=self.scrollx_active_bg,
            orient='horizontal')

        self.textarea.configure(
            yscrollcommand=self.scrolly.set,
            xscrollcommand=self.scrollx.set,
            bg=self.bg_color,
            fg=self.font_color,
            wrap= self.text_wrap,
            spacing1=self.top_spacing, 
            spacing3=self.bottom_spacing,
            selectbackground= self.text_selection_bg,
            insertbackground=self.insertion_color,
            insertofftime=self.insertion_blink,
            bd=self.border,
            highlightthickness=self.border,
            highlightbackground='black',
            font=self.font_style,
            undo=True,
            autoseparators=True,
            maxundo=-1,
            padx=self.padding_x,
            pady=self.padding_y)

        self.initial_content = self.textarea.get("1.0", tk.END)

        #retrieving the font from the text area and setting a tab width
        self._font = tk_font.Font(font=self.textarea['font'])
        self._tab_width = self._font.measure(' ' * self.tab_size_spaces)
        self.textarea.config(tabs=(self._tab_width,))

        self.context_menu = ContextMenu(self)
        self.linenumbers = TextLineNumbers(self)
        self.syntax_highlighter = SyntaxHighlighting(self, self.textarea, self.initial_content)
        self.menubar = Menubar(self)
        self.statusbar = Statusbar(self)
        self.syntax_highlighter.syntax_and_themes.load_default()

        self.linenumbers.attach(self.textarea)
        self.scrolly.pack(side=tk.RIGHT, fill=tk.Y)
        self.scrollx.pack(side=tk.BOTTOM, fill=tk.X)
        self.linenumbers.pack(side=tk.LEFT, fill=tk.Y)
        self.textarea.pack(side=tk.RIGHT, fill='both', expand=True)
        
        self.textarea.find_match_index = None
        self.textarea.find_search_starting_index = 1.0

        #calling function to bind hotkeys.
        self.bind_shortcuts()
        self.control_key = False
        self.menu_hidden = False
        self.first_word = True

    def clear_and_replace_textarea(self):
        self.textarea.delete(1.0, tk.END)
        try:
            if self.filename:
                with open(self.filename, 'r') as f:
                    self.textarea.insert(1.0, f.read())
            self.syntax_highlighter.initial_highlight()
        except TypeError as e:
            print(e)

    #reconfigure the tab_width depending on changes.
    def set_new_tab_width(self, tab_spaces = 'default'):
        if tab_spaces == 'default':
            space_count = self.tab_size_spaces
        else:
            space_count = tab_spaces
        _font = tk_font.Font(font=self.textarea['font'])
        _tab_width = _font.measure(' ' * int(space_count))
        self.textarea.config(tabs=(_tab_width,))

    # editor basic settings can be altered here
    #function used to reload settings after the user changes in settings.yaml
    def reconfigure_settings(self, overwrite_with_default=False):
            if overwrite_with_default:
                _settings = self.loader.load_settings_data(default=True)
            else:
                _settings = self.loader.load_settings_data()
            font_family = _settings['font_family']
            bg_color = _settings['textarea_background_color']
            font_color = _settings['font_color']
            top_spacing = _settings['text_top_lineheight']
            bottom_spacing = _settings['text_bottom_lineheight']
            insertion_blink = 300 if _settings['insertion_blink'] else 0
            insertion_color = _settings['insertion_color']
            tab_size_spaces = _settings['tab_size']
            padding_x = _settings['textarea_padding_x']
            padding_y = _settings['textarea_padding_y']
            text_wrap = _settings['text_wrap']
            border = _settings['textarea_border']
            text_selection_bg = _settings['text_selection_bg']
            scrollx_clr = _settings['horizontal_scrollbar_color']
            troughx_clr = _settings['horizontal_scrollbar_trough_color']
            scrollx_width = _settings['horizontal_scrollbar_width']
            scrollx_active_bg = _settings['horizontal_scrollbar_active_bg']
            scrolly_clr = _settings['vertical_scrollbar_color']
            troughy_clr = _settings['vertical_scrollbar_trough_color']
            scrolly_width = _settings['vertical_scrollbar_width']
            scrolly_active_bg = _settings['vertical_scrollbar_active_bg']
            menu_fg = _settings['menu_fg']
            menu_bg = _settings['menu_bg']
            self.autoclose_parentheses = _settings['autoclose_parentheses']
            self.autoclose_curlybraces = _settings['autoclose_curlybraces']
            self.autoclose_squarebrackets = _settings['autoclose_squarebrackets']
            self.autoclose_singlequotes = _settings['autoclose_singlequotes']
            self.autoclose_doublequotes = _settings['autoclose_doublequotes']
            self.linenumbers.current_line_symbol = _settings['current_line_indicator_symbol']
            self.linenumbers.indicator_on = _settings['current_line_indicator']
            self.browser = _settings['web_browser']
            self.text_selection_bg = text_selection_bg
            self.textarea.reload_text_settings()
            self.set_new_tab_width(tab_size_spaces)
            self.menubar.reconfigure_settings()
            self.bg_color = bg_color
            self.menu_fg = menu_fg
            self.menubar_active_bg = _settings['menubar_active_bg']
            self.menubar_active_fg = _settings['menubar_active_fg']
            self.linenumbers.font_color = menu_fg
            self.linenumbers.bg_color = bg_color
            self.linenumbers._text_font = font_family
            self.linenumbers.redraw()

            font_style = tk_font.Font(family=font_family,
                                      size=_settings['font_size'])

            self.menubar._menubar.configure(
                          fg=menu_fg,
                          bg=menu_bg,
                          activeforeground=self.menubar_active_fg,
                          activebackground=self.menubar_active_bg,
                          activeborderwidth=0,
                          bd=0)

            self.context_menu.right_click_menu.configure(
                                font=font_family,
                                fg=menu_fg,
                                bg=bg_color,
                                activebackground=self.menubar_active_bg,
                                activeforeground=self.menubar_active_fg,
                                bd=0,
                                tearoff=0)
            
            self.scrolly.configure(
                bg=scrolly_clr,
                troughcolor=troughy_clr,
                width=scrolly_width,
                activebackground=scrolly_active_bg)

            self.scrollx.configure(
                bg=scrollx_clr,
                troughcolor=troughx_clr,
                width=scrollx_width,
                activebackground=scrollx_active_bg)

            self.textarea.configure(
                font=font_style,
                bg=bg_color,
                pady=padding_y,
                padx=padding_x,
                fg=font_color,
                spacing1=top_spacing,
                spacing3=bottom_spacing,
                insertbackground=insertion_color,
                selectbackground= text_selection_bg,
                insertofftime=insertion_blink,
                bd=border,
                highlightthickness=border,
                wrap=text_wrap)


            if overwrite_with_default:
                MsgBox = tk.messagebox.askquestion(
                    'Reset Settings?',
                    'Are you sure you want to reset the editor settings to their default value?',
                    icon='warning')
                if MsgBox == 'yes':
                    self.loader.store_settings_data(_settings)
                else:
                    if self.filename == self.loader.settings_path: 
                        self.save(self.loader.settings_path)
                    self.reconfigure_settings()

    # editor quiet mode calling which removes status bar and menu bar
    def enter_quiet_mode(self, *args):
        self.statusbar.hide_status_bar()
        self.menubar.hide_menu()
        self.scrollx.configure(width=0)
        self.scrolly.configure(width=0)
        self.statusbar.update_status('quiet')

    # editor leaving quite enu to bring back status bar and menu bar
    def leave_quiet_mode(self, *args):
        self.statusbar.show_status_bar()
        self.menubar.show_menu()
        self.scrollx.configure(width=8)
        self.scrolly.configure(width=8)
        self.statusbar.update_status('hide')

    #hide status bar for text class so it can be used in menu class
    def hide_status_bar(self, *args):
        self.statusbar.hide_status_bar()

    # toggle the visibility of line numbers
    def toggle_linenumbers(self, *args):
        self.linenumbers.visible = not self.linenumbers.visible

    # setting up the editor title
    #Renames the window title bar to the name of the current file.
    def set_window_title(self, name=None):
        if name:
            self.master.title(f'{name} - Quiet Text')
        else:
            self.master.title('untitled - Quiet Text')


    def load_previous_file(self, *args):
        if self.previous_file:
            try:
                previous = self.filename
                self.filename = self.previous_file
                self.previous_file = previous
                self.set_window_title(name=self.filename)
                self.initialize_syntax()
                self.clear_and_replace_textarea()
            except PermissionError as e:
                print(e)

    # new file creating in the editor feature
    #Deletes all of the text in the current area and sets window title to default.
    def new_file(self, *args):
        self.textarea.delete(1.0, tk.END)
        try:
            new_file = filedialog.asksaveasfilename(
                parent=self.master,
                title='New',
                initialdir=self.dirname,
                initialfile='untitled',
                filetypes=[('All Files', '*.*'),
                           ('Text Files', '*.txt'),
                           ('Python Scripts', '*.py'),
                           ('Markdown Documents', '*.md'),
                           ('JavaScript Files', '*.js'),
                           ('Java Files', '*.java'),
                           ('Haskell Files', '*.hs'),
                           ('HTML Documents', '*.js'),
                           ('CSS Documents', '*.css'),
                           ('C Files', '*.c'),
                           ('C++ Files', '*.cpp'),
                           ('CoffeeScript Files', '*.coffee'),
                           ('Dart Files', '*.dart'),
                           ('Go Files', '*.go'),
                           ('Rust Files', '*.rs'),
                           ('Swift Files', '*.swift')])
            self.previous_file = self.filename
            self.filename = new_file
            textarea_content = self.textarea.get(1.0, tk.END)
            self.set_window_title('untitled')
            with open(new_file, 'w') as f:
                f.write(textarea_content)
            self.set_window_title(self.filename)
            self.statusbar.update_status('created')
            self.initialize_syntax()
        except Exception as e:
            print(e)

    def initialize_syntax(self):
        if self.filename:
            self.clear_and_replace_textarea()
            if self.filename[-4:] == '.txt' or self.filename[-3:] == '.md':
                self.syntax_highlighter.syntax_and_themes.load_markdown_syntax()
            elif self.filename[-2:] == '.c':
                self.syntax_highlighter.syntax_and_themes.load_c_syntax()
            elif self.filename[-2:] == '.coffee':
                self.syntax_highlighter.syntax_and_themes.load_coffeescript_syntax()
            elif self.filename[-5:] == '.dart':
                self.syntax_highlighter.syntax_and_themes.load_dart_syntax()
            elif self.filename[-3:] == '.py':
                self.syntax_highlighter.syntax_and_themes.load_python3_syntax()
            elif self.filename[-3:] == '.js':
                self.syntax_highlighter.syntax_and_themes.load_javascript_syntax()
            elif self.filename[-5:] == '.java':
                self.syntax_highlighter.syntax_and_themes.load_java_syntax()
            elif self.filename[-3:] == '.hs':
                self.syntax_highlighter.syntax_and_themes.load_haskell_syntax()
            elif self.filename[-5:] == '.html':
                self.syntax_highlighter.syntax_and_themes.load_html_syntax()
            elif self.filename[-4:] == '.css':
                self.syntax_highlighter.syntax_and_themes.load_css_syntax()
            elif self.filename[-4:] == '.cpp':
                self.syntax_highlighter.syntax_and_themes.load_cpp_syntax()
            elif self.filename[-3:] == '.go':
                self.syntax_highlighter.syntax_and_themes.load_go_syntax()
            elif self.filename[-3:] == '.rs':
                self.syntax_highlighter.syntax_and_themes.load_rust_syntax()
            elif self.filename[-4:] == '.sql':
                self.syntax_highlighter.syntax_and_themes.load_sql_syntax()
            elif self.filename[-5:] == '.yaml':
                self.syntax_highlighter.syntax_and_themes.load_yaml_syntax()
            elif self.filename[-6:] == '.swift':
                self.syntax_highlighter.syntax_and_themes.load_swift_syntax()
            elif self.filename[-10:] == 'Dockerfile':
                self.syntax_highlighter.syntax_and_themes.load_docker_syntax()
            elif self.filename[-4:] == '.nim':
                self.syntax_highlighter.syntax_and_themes.load_nim_syntax()

    # opening an existing file in the editor
    def open_file(self, *args):
        # various file types that editor can support
        self.previous_file = self.filename
        try:
            self.filename = filedialog.askopenfilename(
                parent=self.master,
                initialdir=self.dirname,
                filetypes=[('All Files', '*.*'),
                           ('Text Files', '*.txt'),
                           ('Python Scripts', '*.py'),
                           ('Markdown Documents', '*.md'),
                           ('JavaScript Files', '*.js'),
                           ('Java Files', '*.java'),
                           ('Haskell Files', '*.hs'),
                           ('HTML Documents', '*.html'),
                           ('CSS Documents', '*.css'),
                           ('C Files', '*.c'),
                           ('C++ Files', '*.cpp'),
                           ('CoffeeScript Files', '*.coffee'),
                           ('Dart Files', '*.dart'),
                           ('Go Files', '*.go'),
                           ('Rust Files', '*.rs'),
                           ('Sql Files', '*.sql'),
                           ('Swift Files', '*.swift')])

            self.initialize_syntax()
            if not self.filename:
                self.filename = self.previous_file
            self.set_window_title(name=self.filename)
        except Exception as e:
            print(e)

    def open_dir(self):
        try:
            self.dirname = filedialog.askdirectory(
                parent=self.master)
            self.filename = self.dirname
            if self.dirname:
                self.set_window_title(name=self.filename)
                FileTree(self)
        except Exception:
            pass

    def show_file_tree(self, *args):
        self.control_key = False
        self.textarea.isControlPressed = False
        FileTree(self)
        return 'break'

    # saving changes made in the file
    def save(self,*args):
        if self.filename:
            try:
                textarea_content = self.textarea.get(1.0, tk.END)
                with open(self.filename, 'w') as f:
                    f.write(textarea_content)
                self.statusbar.update_status('saved')
                if self.filename == self.loader.settings_path:
                    self.reconfigure_settings()
                    self.menubar.reconfigure_settings()
            except Exception as e:
                pass
        else:
            self.save_as()

    # saving file as a particular name
    def save_as(self, *args):
        try:
            self.filename = filedialog.asksaveasfilename(
                parent=self.master,
                initialdir=self.dirname,
                filetypes=[('All Files', '*.*'),
                           ('Text Files', '*.txt'),
                           ('Python Scripts', '*.py'),
                           ('Markdown Documents', '*.md'),
                           ('Javascript Files', '*.js'),
                           ('Java Files', '*.java'),
                           ('Haskell Files', '*.hs'),
                           ('HTML Documents', '*.html'),
                           ('CSS Documents', '*.css'),
                           ('C Files', '*.c'),
                           ('C++ Files', '*.cpp'),
                           ('CoffeeScript Files', '*.coffee'),
                           ('Dart Files', '*.dart'),
                           ('Go Files', '*.go'),
                           ('Rust Files', '*.rs'),
                           ('Sql Files', '*.sql'),
                           ('Swift Files', '*.swift')])

            textarea_content = self.textarea.get(1.0, tk.END)
            with open(self.filename, 'w') as f:
                f.write(textarea_content)
            self.filename = new_file
            self.set_window_title(self.filename)
            self.statusbar.update_status('saved')
            self.initialize_syntax()
        except Exception as e:
            pass
            
    #On exiting the Program
    def quit_save(self):
        try:
            os.path.isfile(self.filename)
            self.save()
        except Exception:
            self.save_as()
        sys.exit()

    def on_closing(self):
        message = tk.messagebox.askyesnocancel("Save On Close", "Do you want to save the changes before closing?")
        if message == True:
            self.quit_save()
        elif message == False:
            sys.exit()
        else:
            return

    # opens the main setting file of the editor
    def open_settings_file(self):
        self.syntax_highlighter.syntax_and_themes.load_yaml_syntax()
        self.previous_file = self.filename
        self.filename = self.loader.settings_path
        self.textarea.delete(1.0, tk.END)
        with open(self.filename, 'r') as f:
            self.textarea.insert(1.0, f.read())
        self.syntax_highlighter.initial_highlight()
        self.set_window_title(name=self.filename)

    # reset the settings set by the user to the default settings
    def reset_settings_file(self):
        self.reconfigure_settings(overwrite_with_default=True)
        self.syntax_highlighter.syntax_and_themes.load_material()
        try:
            self.clear_and_replace_textarea()
        except IsADirectoryError:
            pass

    # select all written text in the editor
    def select_all_text(self, *args):
        self.textarea.tag_add(tk.SEL, '1.0', tk.END)
        self.textarea.mark_set(tk.INSERT, '1.0')
        self.textarea.see(tk.INSERT)
        return 'break'

    # give hex colors to the file content for better understanding
    def apply_hex_color(self, key_event):
        new_color = self.menubar.open_color_picker()
        try:
            sel_start = self.textarea.index(tk.SEL_FIRST)
            sel_end = self.textarea.index(tk.SEL_LAST)
            self.textarea.delete(sel_start, sel_end)
            self.textarea.insert(sel_start, new_color)
        except tk.TclError:
            pass

    def _on_change(self, key_event):
        self.linenumbers.redraw()

    def _on_mousewheel(self, event):
        if self.control_key:
            self.change_font_size(1 if event.delta > 0 else -1)

    def _on_linux_scroll_up(self, _):
        if self.control_key:
            self.change_font_size(1)
            if self.filename == self.loader.settings_path:
                self.syntax_highlighter.initial_highlight()

    def _on_linux_scroll_down(self, _):
        if self.control_key:
            self.change_font_size(-1)
            if self.filename == self.loader.settings_path:
                self.syntax_highlighter.initial_highlight()

    def change_font_size(self, delta):
        self.font_size = self.font_size + delta
        min_font_size = 6
        self.font_size = min_font_size if self.font_size < min_font_size else self.font_size

        self.font_style = tk_font.Font(family=self.font_family,
                                       size=self.font_size)

        self.italics = tk_font.Font(family=self.font_family,
                                    size=self.font_size,
                                    slant='italic')

        self.bold = tk_font.Font(family=self.font_family,
                                 size = self.font_size,
                                 weight='bold')

        self.header1 = tk_font.Font(family=self.font_family,
                                    size = self.font_size + 15,
                                    weight='bold')

        self.header2 = tk_font.Font(family=self.font_family,
                                    size = self.font_size + 7,
                                    weight='bold')

        self.textarea.configure(font=self.font_style)
        self.syntax_highlighter.text.tag_configure("Token.Name.Builtin.Pseudo",font=self.italics)
        self.syntax_highlighter.text.tag_configure("Token.Keyword.Type",font=self.italics)
        self.syntax_highlighter.text.tag_configure("Token.Keyword.Declaration",font=self.italics)
        self.syntax_highlighter.text.tag_configure("Token.Generic.Emph",font=self.italics)
        self.syntax_highlighter.text.tag_configure("Token.Generic.Strong",font=self.bold)
        self.syntax_highlighter.text.tag_configure("Token.Generic.Heading",font=self.header1)
        self.syntax_highlighter.text.tag_configure("Token.Generic.Subheading",font=self.header2)
        self.set_new_tab_width()
        
        _settings = self.loader.load_settings_data()
        _settings['font_size'] = self.font_size
        self.loader.store_settings_data(_settings)

        if self.filename == self.loader.settings_path:
            self.clear_and_replace_textarea()


    # control_l = 37
    # control_r = 109
    # mac_control = 262401 #control key in mac keyboard
    # mac_control_l = 270336 #tk.LEFT control key in mac os with normal keyboard
    # mac_control_r = 262145 #tk.RIGHT control key in mac os with normal keyboard
    def _on_keydown(self, event):
        if event.keycode in [17, 37, 109, 262401, 270336, 262145]:
            self.control_key = True
            self.textarea.isControlPressed = True
        else:
            self.statusbar.update_status('hide')

    def syntax_highlight(self, *args):
        if self.first_word:
            self.syntax_highlighter.initial_highlight()
            self.first_word = not self.first_word
        self.syntax_highlighter.default_highlight()
        self.control_key = False
        self.textarea.isControlPressed = False

    def show_find_window(self, event=None):
        self.textarea.tag_configure('find_match', background=self.text_selection_bg)
        self.textarea.bg_color = self.bg_color
        self.textarea.fg_color = self.menu_fg
        self.textarea.active_fg = self.menubar_active_fg
        self.textarea.active_bg = self.menubar_active_bg
        FindWindow(self.textarea)
        self.control_key = False
        self.textarea.isControlPressed = False

    def select_all(self):
        self.selection_set(0, 'end')

    def autoclose_base(self, symbol):
        index = self.textarea.index(tk.INSERT)
        self.textarea.insert(index, symbol)
        self.textarea.mark_set(tk.INSERT, index)

    def autoclose_parens(self, event):
        _, second_char, _, _ = self.get_chars_in_front_and_back()
        if self.autoclose_parentheses and not second_char.isalnum():
            self.autoclose_base(')')

    def autoclose_curly_brackets(self, event):
        _, second_char, _, _ = self.get_chars_in_front_and_back()
        if self.autoclose_curlybraces and not second_char.isalnum():
            self.autoclose_base('}')

    def autoclose_square_brackets(self, event):
        _, second_char, _, _ = self.get_chars_in_front_and_back()
        if self.autoclose_squarebrackets and not second_char.isalnum():
            self.autoclose_base(']')

    def autoclose_double_quotes(self, event):
        _, second_char, _, _ = self.get_chars_in_front_and_back()
        if self.autoclose_doublequotes and not second_char.isalnum():
            self.autoclose_base('"')

    def autoclose_single_quotes(self, event):
        _, second_char, _, _ = self.get_chars_in_front_and_back()
        if self.autoclose_singlequotes and not second_char.isalnum():
            self.autoclose_base("'")

    def get_indent_level(self):
        text = self.textarea
        line = text.get('insert linestart', 'insert lineend')
        match = re.match(r'^(\s+)', line)
        current_indent = len(match.group(0)) if match else 0
        return current_indent

    def auto_indentation(self):
        text = self.textarea
        new_indent = self.get_indent_level()
        text.insert('insert', '\n' + '\t' * new_indent)

    def auto_block_indentation(self, event):
        prev_char, second_char, _, _ = self.get_chars_in_front_and_back()
        text = self.textarea
        if prev_char == ':':
            current_indent = self.get_indent_level()
            new_indent = current_indent + 1
            text.insert('insert', '\n' + '\t' * new_indent)
            return 'break'
        elif prev_char in '{[(' and second_char in '}])':
            current_indent = self.get_indent_level()
            new_indent = current_indent + 1
            text.insert('insert', '\n\n')
            text.insert('insert', '\t' * current_indent)
            index = text.index(tk.INSERT)
            text.mark_set('insert', str(round(float(index) - 1, 1)))
            text.insert('insert', '\t' * new_indent)
            return 'break'
        else:
            self.auto_indentation()
            return 'break'

    def get_chars_in_front_and_back(self):
        index = self.textarea.index(tk.INSERT)
        first_pos = f'{str(index)}-1c'
        end_second_pos = f'{str(index)}+1c'
        first_char = self.textarea.get(first_pos, index)
        second_char = self.textarea.get(index, end_second_pos)
        return (first_char, second_char, index, end_second_pos)
        
    def backspace_situations(self, event):
        first_char, second_char, index, end_second_pos = self.get_chars_in_front_and_back()

        if first_char == "'" and second_char == "'":
            self.textarea.delete(index, end_second_pos)
        elif first_char == '"' and second_char == '"':
            self.textarea.delete(index, end_second_pos)
        elif first_char == '(' and second_char == ')':
            self.textarea.delete(index, end_second_pos)
        elif first_char == '{' and second_char == '}':
            self.textarea.delete(index, end_second_pos)
        elif first_char == '[' and second_char == ']':
            self.textarea.delete(index, end_second_pos)

    def hide_and_unhide_menubar(self, key_event):
        if self.menu_hidden:
            self.menubar.show_menu()
        else:
            self.menubar.hide_menu()
        self.menu_hidden = not self.menu_hidden

    def tab_text(self, event):
        index = self.textarea.index("sel.first linestart")
        last = self.textarea.index("sel.last linestart")

        if last != index:
            if event.state == 0:
                while self.textarea.compare(index,"<=", last):
                    if len(self.textarea.get(index, 'end')) != 0:
                        self.textarea.insert(index, '\t')
                    index = self.textarea.index("%s + 1 line" % index)
            else:
                while self.textarea.compare(index,"<=", last):
                    if self.textarea.get(index, 'end')[:1] == "\t":
                        self.textarea.delete(index)
                    index = self.textarea.index("%s + 1 line" % index)
        else:
            if event.state == 0:
                index = self.textarea.index(tk.INSERT)
                self.textarea.insert(index, '\t')
            else:
                index = self.textarea.index("insert linestart")
                if self.textarea.get(index, 'end')[:1] == "\t":
                    self.textarea.delete(index)
        return "break"

    def bind_shortcuts(self, *args):
        text = self.textarea
        text.bind('<Control-n>', self.new_file)
        text.bind('<Control-o>', self.open_file)
        text.bind('<Control-s>', self.save)
        text.bind('<Control-S>', self.save_as)
        text.bind('<Control-b>', self.context_menu.bold)
        text.bind('<Control-h>', self.context_menu.hightlight)
        text.bind('<Control-a>', self.select_all_text)
        text.bind('<Control-m>', self.apply_hex_color)
        text.bind('<Control-r>', self.menubar.run)
        text.bind('<Control-q>', self.enter_quiet_mode)
        text.bind('<Control-f>', self.show_find_window)
        text.bind('<Control-p>', self.load_previous_file)
        text.bind('<Control-t>', self.show_file_tree)
        text.bind('<Control-z>', self.syntax_highlighter.initial_highlight)
        text.bind('<Control-y>', self.syntax_highlighter.initial_highlight)
        text.bind('<Escape>', self.leave_quiet_mode)
        text.bind('<<Change>>', self._on_change)
        text.bind('<Configure>', self._on_change)
        text.bind('<Button-3>', self.context_menu.popup)
        text.bind('<MouseWheel>', self._on_mousewheel)
        text.bind('<Button-4>', self._on_linux_scroll_up)
        text.bind('<Button-5>', self._on_linux_scroll_down)
        text.bind('<Key>', self._on_keydown)
        text.bind('<KeyRelease>', self.syntax_highlight)
        text.bind('<Shift-asciitilde>', self.syntax_highlighter.initial_highlight)
        text.bind('<Shift-parenleft>', self.autoclose_parens)
        text.bind('<bracketleft>', self.autoclose_square_brackets)
        text.bind('<quoteright>', self.autoclose_single_quotes)
        text.bind('<quotedbl>', self.autoclose_double_quotes)
        text.bind('<braceleft>', self.autoclose_curly_brackets)
        text.bind('<Return>', self.auto_block_indentation)
        text.bind('<BackSpace>', self.backspace_situations)
        text.bind('<Alt_L>', self.hide_and_unhide_menubar)
        text.bind('<Control-L>', self.toggle_linenumbers)
        text.bind('<KeyPress-Tab>', self.tab_text)
        if self.operating_system == 'Windows':
            text.bind('<Shift-Tab>', self.tab_text)
        else:
            text.bind('<Shift-ISO_Left_Tab>', self.tab_text)
Exemplo n.º 3
0
class QuietText(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        master.title('untitled - Quiet Text')
        # defined size of the editer window
        master.geometry('1920x1080')
        self.loader = QuietLoaders()
        user_operating_system = system()

        # start editor according to defined settings in settings.yaml
        self.settings = self.loader.load_settings_data()
        self.default_theme = self.loader.load_default_theme()

        self.settings['font_color'] = self.default_theme['font_color']
        self.settings['textarea_background_color'] = self.default_theme['bg_color']
        self.settings['menubar_active_bg'] = self.default_theme['menu_bg_active']
        self.settings['menubar_active_fg'] = self.default_theme['menu_fg_active']  
        self.settings['menu_active_bg'] = self.default_theme['menu_bg_active']
        self.settings['menu_active_fg'] = self.default_theme['menu_fg_active']
        self.settings['menu_bg'] = self.default_theme['bg_color']
        self.settings['font_color'] = self.default_theme['font_color']

        self.font_family = self.settings['font_family']
        self.bg_color = self.settings['textarea_background_color']
        self.font_color = self.settings['font_color']
        self.tab_size = self.settings['tab_size']
        self.font_size = int(self.settings['font_size'])
        self.top_spacing = self.settings['text_top_lineheight']
        self.bottom_spacing = self.settings['text_bottom_lineheight']
        self.padding_x = self.settings['textarea_padding_x']
        self.padding_y = self.settings['textarea_padding_y']
        self.insertion_blink = 300 if self.settings['insertion_blink'] else 0
        self.insertion_color = self.settings['insertion_color']
        self.tab_size_spaces = self.settings['tab_size']
        self.text_wrap = self.settings['text_wrap']
        self.autoclose_parens = self.settings['autoclose_parentheses']
        self.autoclose_curlybraces = self.settings['autoclose_curlybraces']
        self.autoclose_squarebrackets = self.settings['autoclose_squarebrackets']
        self.autoclose_singlequotes = self.settings['autoclose_singlequotes']
        self.autoclose_doublequotes = self.settings['autoclose_doublequotes']
        self.border = self.settings['textarea_border']
        self.text_selection_bg = self.settings['text_selection_bg']
        self.scrollx_clr = self.settings['horizontal_scrollbar_color']
        self.troughx_clr = self.settings['horizontal_scrollbar_trough_color']
        self.scrollx_width = self.settings['horizontal_scrollbar_width']
        self.scrollx_active_bg = self.settings['horizontal_scrollbar_active_bg']
        self.scrolly_clr = self.settings['vertical_scrollbar_color']
        self.troughy_clr = self.settings['vertical_scrollbar_trough_color']
        self.scrolly_width = self.settings['vertical_scrollbar_width']
        self.scrolly_active_bg = self.settings['vertical_scrollbar_active_bg']
        self.menu_fg = self.settings['menu_fg']
        self.menu_bg = self.settings['menu_bg']



        master.tk_setPalette(background=self.bg_color, foreground='black')
        self.font_style = tk_font.Font(family=self.font_family,
                                       size=self.settings['font_size'])

        #configuration of the file dialog text colors.

        self.italics = tk_font.Font(family=self.font_family, slant='italic')
        self.master = master
        self.filename = None
                                
        self.textarea = CustomText(self)

        self.scrolly = tk.Scrollbar(master,
                                    command=self.textarea.yview,
                                    bg=self.scrolly_clr,
                                    troughcolor=self.troughy_clr,
                                    bd=0,
                                    width=self.scrolly_width,
                                    highlightthickness=0,
                                    activebackground=self.scrolly_active_bg,
                                    orient='vertical')

        self.scrollx = tk.Scrollbar(master,
                                    command=self.textarea.xview,
                                    bg=self.scrollx_clr,
                                    troughcolor=self.troughx_clr,
                                    bd=0,
                                    width=self.scrollx_width,
                                    highlightthickness=0,
                                    activebackground=self.scrollx_active_bg,
                                    orient='horizontal')

        self.textarea.configure(yscrollcommand=self.scrolly.set,
                                xscrollcommand=self.scrollx.set,
                                bg=self.bg_color,
                                fg=self.font_color,
                                wrap= self.text_wrap,
                                spacing1=self.top_spacing, 
                                spacing3=self.bottom_spacing,
                                selectbackground= self.text_selection_bg,
                                insertbackground=self.insertion_color,
                                insertofftime=self.insertion_blink,
                                bd=self.border,
                                highlightthickness=self.border,
                                highlightbackground='black',
                                font=self.font_family,
                                undo=True,
                                autoseparators=True,
                                maxundo=-1,
                                padx=self.padding_x,
                                pady=self.padding_y)

        self.initial_content = self.textarea.get("1.0", tk.END)

        #retrieving the font from the text area and setting a tab width
        self._font = tk_font.Font(font=self.textarea['font'])
        self._tab_width = self._font.measure(' ' * self.tab_size_spaces)
        self.textarea.config(tabs=(self._tab_width,))

        self.menu_hidden = False
        self.context_menu = ContextMenu(self)
        self.statusbar = Statusbar(self)
        self.linenumbers = TextLineNumbers(self)
        self.syntax_highlighter = SyntaxHighlighting(self, self.textarea, self.initial_content)
        self.menubar = Menubar(self)

        self.linenumbers.attach(self.textarea)
        self.scrolly.pack(side=tk.RIGHT, fill=tk.Y)
        self.scrollx.pack(side=tk.BOTTOM, fill='both')
        self.linenumbers.pack(side=tk.LEFT, fill=tk.Y)
        self.textarea.pack(side=tk.RIGHT, fill='both', expand=True)
        
        self.textarea.tag_configure('find_match', background='#75715e')
        self.textarea.find_match_index = None
        self.textarea.find_search_starting_index = 1.0

        self.tags_configured = False
        #calling function to bind hotkeys.
        self.bind_shortcuts()
        self.control_key = False

    def clear_and_replace_textarea(self):
            self.textarea.delete(1.0, tk.END)
            try:
                with open(self.filename, 'r') as f:
                    self.textarea.insert(1.0, f.read())
            except TypeError:
                pass

    #reconfigure the tab_width depending on changes.
    def set_new_tab_width(self, tab_spaces = 'default'):
        if tab_spaces == 'default':
            space_count = self.tab_size_spaces
        else:
            space_count = tab_spaces
        _font = tk_font.Font(font=self.textarea['font'])
        _tab_width = _font.measure(' ' * int(space_count))
        self.textarea.config(tabs=(_tab_width,))

    # editor basic settings can be altered here
    #function used to reload settings after the user changes in settings.yaml
    def reconfigure_settings(self, overwrite_with_default=False):
            if overwrite_with_default:
                _settings = self.loader.load_settings_data(default=True)
            else:
                _settings = self.loader.load_settings_data()
            font_family = _settings['font_family']
            bg_color = _settings['textarea_background_color']
            font_color = _settings['font_color']
            top_spacing = _settings['text_top_lineheight']
            bottom_spacing = _settings['text_bottom_lineheight']
            insertion_blink = 300 if _settings['insertion_blink'] else 0
            insertion_color = _settings['insertion_color']
            tab_size_spaces = _settings['tab_size']
            padding_x = _settings['textarea_padding_x']
            padding_y = _settings['textarea_padding_y']
            text_wrap = _settings['text_wrap']
            border = _settings['textarea_border']
            text_selection_bg = _settings['text_selection_bg']
            scrollx_clr = _settings['horizontal_scrollbar_color']
            troughx_clr = _settings['horizontal_scrollbar_trough_color']
            scrollx_width = _settings['horizontal_scrollbar_width']
            scrollx_active_bg = _settings['horizontal_scrollbar_active_bg']
            menu_fg = _settings['menu_fg']
            menu_bg = _settings['menu_bg']

            font_style = tk_font.Font(family=font_family,
                                      size=_settings['font_size'])

            self.textarea.configure(font=font_style,
                                    bg=bg_color,
                                    pady=padding_y,
                                    padx=padding_x,
                                    fg=font_color,
                                    spacing1=top_spacing,
                                    spacing3=bottom_spacing,
                                    insertbackground=insertion_color,
                                    selectbackground= text_selection_bg,
                                    insertofftime=insertion_blink,
                                    bd=border,
                                    highlightthickness=border,
                                    wrap=text_wrap)

            self.set_new_tab_width(tab_size_spaces)
            self.menubar.reconfigure_settings()
            self.linenumbers.font_color = menu_fg
            self.linenumbers.config(bg=bg_color, highlightbackground=bg_color)
            self.statusbar._label.config(bg=bg_color)
            self.linenumbers.redraw()

            if overwrite_with_default:
                MsgBox = tk.messagebox.askquestion('Reset Settings?',
                                                   'Are you sure you want to reset the editor settings to their default value?',
                                                    icon='warning')
                if MsgBox == 'yes':
                    self.loader.store_settings_data(_settings)
                else:
                    self.save('config/settings.yaml')

    # editor quiet mode calling which removes status bar and menu bar
    def enter_quiet_mode(self, *args):
        self.statusbar.hide_status_bar()
        self.menubar.hide_menu()
        self.scrollx.configure(width=0)
        self.scrolly.configure(width=0)
        self.statusbar.update_status('quiet')

    # editor leaving quite enu to bring back status bar and menu bar
    def leave_quiet_mode(self, *args):
        self.statusbar.show_status_bar()
        self.menubar.show_menu()
        self.scrollx.configure(width=8)
        self.scrolly.configure(width=8)
        self.statusbar.update_status('hide')

    #hide status bar for text class so it can be used in menu class
    def hide_status_bar(self, *args):
        self.statusbar.hide_status_bar()

    # toggle the visibility of line numbers
    def toggle_linenumbers(self, *args):
        self.linenumbers.visible = not self.linenumbers.visible

    # setting up the editor title
    #Renames the window title bar to the name of the current file.
    def set_window_title(self, name=None):
        if name:
            self.master.title(f'{name} - QuietText')
        else:
            self.master.title('Untitled - QuietText')

    # new file creating in the editor feature
    #Deletes all of the text in the current area and sets window title to default.
    def new_file(self, *args):
        self.textarea.delete(1.0, tk.END)
        self.filename = None
        self.set_window_title()

    # opening an existing file in the editor
    def open_file(self, *args):
        # various file types that editor can support
        self.filename = filedialog.askopenfilename(
            defaultextension='.txt',
            filetypes=[('All Files', '*.*'),
                       ('Text Files', '*.txt'),
                       ('Python Scripts', '*.py'),
                       ('Markdown Documents', '*.md'),
                       ('Javascript Files', '*.js'),
                       ('HTML Documents', '*.html'),
                       ('CSS Documents', '*.css')])

        if self.filename:
            self.clear_and_replace_textarea()
            self.set_window_title(name=self.filename)
            self.syntax_highlighter.initial_highlight()

    # opening an existing file without TK filedialog
    def open_file_without_dialog(self, path):
        if os.path.isdir(path):
            self.statusbar.update_status('Unable to open directory.')
            return

        if not os.path.exists(path):
            self.statusbar.update_status('File does not exists.')
            return

        self.filename = path
        self.clear_and_replace_textarea()
        self.syntax_highlighter.initial_highlight()

    # saving changes made in the file
    def save(self,*args):
        if self.filename:
            try:
                textarea_content = self.textarea.get(1.0, tk.END)
                with open(self.filename, 'w') as f:
                    f.write(textarea_content)
                self.statusbar.update_status('saved')
                if self.filename == 'config/settings.yaml':
                    self.reconfigure_settings()
                    self.menubar.reconfigure_settings()
            except Exception as e:
                print(e)
        else:
            self.save_as()

    # saving file as a particular name
    def save_as(self, *args):
        try:
            new_file = filedialog.asksaveasfilename(
                initialfile='untitled.txt',
                defaultextension='.txt',
                filetypes=[('All Files', '*.*'),
                           ('Text Files', '*.txt'),
                           ('Python Scripts', '*.py'),
                           ('Markdown Documents', '*.md'),
                           ('Javascript Files', '*.js'),
                           ('HTML Documents', '*.js'),
                           ('CSS Documents', '*.css')])

            textarea_content = self.textarea.get(1.0, tk.END)
            with open(new_file, 'w') as f:
                f.write(textarea_content)
            self.filename = new_file
            self.set_window_title(self.filename)
            self.statusbar.update_status('saved')
        except Exception as e:
            print(e)
            
    #On exiting the Program
    def quit_save(self):
        try:
            os.path.isfile(self.filename)
            self.save()          
        except:
            self.save_as()
        quit()
                        

    def on_closing(self):
        message = tk.messagebox.askyesnocancel("Save On Close", "Do you want to save the changes before closing?")
        if message == True:
            self.quit_save()
        elif message == False:
            quit()
        else:
            return

    # running the python file
    def run(self, *args):
        try:
            if self.filename[-3:] == '.py':
                #run separate commands for different os
                if os.name == 'nt':
                    os.system(f'start cmd.exe @cmd /k "python {self.filename}"')
                else:
                    os.system(f"gnome-terminal -- python3.8 {self.filename}")
            else:
                self.statusbar.update_status('no python')
        except TypeError:
            self.statusbar.update_status('no file run')

    # opens the main setting file of the editor
    def open_settings_file(self):
        self.filename = 'config/settings.yaml'
        self.textarea.delete(1.0, tk.END)
        with open(self.filename, 'r') as f:
            self.textarea.insert(1.0, f.read())
        self.syntax_highlighter.initial_highlight()
        self.set_window_title(name=self.filename)

    # reset the settings set by the user to the default settings
    def reset_settings_file(self):
        self.reconfigure_settings(overwrite_with_default=True)
        self.clear_and_replace_textarea()
        self.syntax_highlighter.initial_highlight()

    # select all written text in the editor
    def select_all_text(self, *args):
        self.textarea.tag_add(tk.SEL, '1.0', tk.END)
        self.textarea.mark_set(tk.INSERT, '1.0')
        self.textarea.see(tk.INSERT)
        return 'break'

    # give hex colors to the file content for better understanding
    def apply_hex_color(self, key_event):
        new_color = self.menubar.open_color_picker()
        try:
            sel_start = self.textarea.index(tk.SEL_FIRST)
            sel_end = self.textarea.index(tk.SEL_LAST)
            self.textarea.delete(sel_start, sel_end)
            self.textarea.insert(sel_start, new_color)
        except tk.TclError:
            pass

    def _on_change(self, key_event):
        self.linenumbers.redraw()

    def _on_mousewheel(self, event):
        if self.control_key:
            self.change_font_size(1 if event.delta > 0 else -1)

    def _on_linux_scroll_up(self, _):
        if self.control_key:
            self.change_font_size(1)
            if self.filename == 'config/settings.yaml':
                self.syntax_highlighter.initial_highlight()

    def _on_linux_scroll_down(self, _):
        if self.control_key:
            self.change_font_size(-1)
            if self.filename == 'config/settings.yaml':
                self.syntax_highlighter.initial_highlight()

    def change_font_size(self, delta):
        self.font_size = self.font_size + delta
        min_font_size = 6
        self.font_size = min_font_size if self.font_size < min_font_size else self.font_size
        self.font_style = tk_font.Font(family=self.font_family,
                                       size=self.font_size)

        self.italics = tk_font.Font(family=self.font_family,
                                    size=self.font_size,
                                    slant='italic')

        self.textarea.configure(font=self.font_style)
        self.syntax_highlighter.text.tag_configure("Token.Name.Builtin.Pseudo",font=self.italics)
        self.set_new_tab_width()
        
        _settings = self.loader.load_settings_data()
        _settings['font_size'] = self.font_size
        self.loader.store_settings_data(_settings)

        if self.filename == 'config/settings.yaml':
            self.clear_and_replace_textarea()


    # control_l = 37
    # control_r = 109
    # mac_control = 262401 #control key in mac keyboard
    # mac_control_l = 270336 #tk.LEFT control key in mac os with normal keyboard
    # mac_control_r = 262145 #tk.RIGHT control key in mac os with normal keyboard
    def _on_keydown(self, event):
        if event.keycode in [17, 37, 109, 262401, 270336, 262145]:
            self.control_key = True
            self.textarea.isControlPressed = True
        else:
            self.statusbar.update_status('hide')

    def syntax_highlight(self, *args):
        self.syntax_highlighter.default_highlight()
        if not self.tags_configured:
            self.syntax_highlighter.syntax_theme_configuration()
        self.control_key = False
        self.textarea.isControlPressed = False

    def show_find_window(self, event=None):
        FindWindow(self.textarea)
        self.control_key = False
        self.textarea.isControlPressed = False

    def select_all(self):
        self.selection_set(0, 'end')

    def autoclose_base(self, symbol):
        index = self.textarea.index(tk.INSERT)
        self.textarea.insert(index, symbol)
        self.textarea.mark_set(tk.INSERT, index)

    def autoclose_parentheses(self, event):
        if self.autoclose_parentheses:
            self.autoclose_base(')')

    def autoclose_curly_brackets(self, event):
        if self.autoclose_curlybraces:
            self.autoclose_base('}')

    def autoclose_square_brackets(self, event):
        if self.autoclose_squarebrackets:
            self.autoclose_base(']')

    def autoclose_double_quotes(self, event):
        if self.autoclose_doublequotes:
            self.autoclose_base('"')

    def autoclose_single_quotes(self, event):
        if self.autoclose_singlequotes:
            self.autoclose_base("'")

    def auto_indentation(self, event):
        text = self.textarea
        line = text.get('insert linestart', 'insert lineend')
        new_indent = self.get_indent_level(line) * 4
        text.insert('insert', '\n' + ' ' * new_indent)
        return 'break'

    def get_indent_level(self, line):
        num_leading_whitespaces = len(line) - len(line.lstrip())
        return num_leading_whitespaces // 4

    def auto_block_indentation(self, event):
        text = self.textarea
        line = text.get('insert linestart', 'insert lineend')
        match = re.match(r'^(\s+)', line)
        current_indent = len(match.group(0)) if match else 0
        new_indent = current_indent + 4
        text.insert('insert', event.char + '\n' + ' ' * new_indent)
        return 'break'


    def get_chars_in_front_and_back(self):
        index = self.textarea.index(tk.INSERT)
        first_pos = f'{str(index)}-1c'
        end_second_pos = f'{str(index)}+1c'
        first_char = self.textarea.get(first_pos, index)
        second_char = self.textarea.get(index, end_second_pos)
        return (first_char, second_char, index, end_second_pos)
        
    def backspace_situations(self, event):
        first_char, second_char, index, end_second_pos = self.get_chars_in_front_and_back()

        if first_char == "'" and second_char == "'":
            self.textarea.delete(index, end_second_pos)
        elif first_char == '"' and second_char == '"':
            self.textarea.delete(index, end_second_pos)
        elif first_char == '(' and second_char == ')':
            self.textarea.delete(index, end_second_pos)
        elif first_char == '{' and second_char == '}':
            self.textarea.delete(index, end_second_pos)
        elif first_char == '[' and second_char == ']':
            self.textarea.delete(index, end_second_pos)

    def hide_and_unhide_menubar(self, key_event):
        if self.menu_hidden:
            self.menubar.show_menu()
        else:
            self.menubar.hide_menu()
        self.menu_hidden = not self.menu_hidden

    def tab_text(self, event):
        index = self.textarea.index("sel.first linestart")
        last = self.textarea.index("sel.last linestart")
        if last != index:
            while self.textarea.compare(index,"<=", last):
                self.textarea.insert(index, '\t')
                index = self.textarea.index("%s + 1 line" % index)
        else:
            index = self.textarea.index(tk.INSERT)
            self.textarea.insert(index, '\t')
        return "break"


    def bind_shortcuts(self, *args):
        text = self.textarea
        text.bind('<Control-n>', self.new_file)
        text.bind('<Control-o>', self.open_file)
        text.bind('<Control-s>', self.save)
        text.bind('<Control-S>', self.save_as)
        text.bind('<Control-b>', self.context_menu.bold)
        text.bind('<Control-h>', self.context_menu.hightlight)
        text.bind('<Control-a>', self.select_all_text)
        text.bind('<Control-m>', self.apply_hex_color)
        text.bind('<Control-r>', self.run)
        text.bind('<Control-q>', self.enter_quiet_mode)
        text.bind('<Control-f>', self.show_find_window)
        text.bind('<Control-z>', self.textarea.edit_undo())
        text.bind('<Control-Shift-z', self.textarea.edit_redo())
        text.bind('<Escape>', self.leave_quiet_mode)
        text.bind('<<Change>>', self._on_change)
        text.bind('<Configure>', self._on_change)
        text.bind('<Button-3>', self.context_menu.popup)
        text.bind('<MouseWheel>', self._on_mousewheel)
        text.bind('<Button-4>', self._on_linux_scroll_up)
        text.bind('<Button-5>', self._on_linux_scroll_down)
        text.bind('<Key>', self._on_keydown)
        text.bind('<KeyRelease>', self.syntax_highlight)
        text.bind_all('<<Paste>>', self.context_menu.paste)
        text.bind('<Shift-asciitilde>', self.syntax_highlighter.initial_highlight)
        text.bind('<Control-Shift-KeyRelease>', self.syntax_highlighter.initial_highlight)
        text.bind('<Shift-parenleft>', self.autoclose_parentheses)
        text.bind('<bracketleft>', self.autoclose_square_brackets)
        text.bind('<quoteright>', self.autoclose_single_quotes)
        text.bind('<quotedbl>', self.autoclose_double_quotes)
        text.bind('<braceleft>', self.autoclose_curly_brackets)
        text.bind('<Return>', self.auto_indentation)
        text.bind('<Shift-colon>', self.auto_block_indentation)
        text.bind('<BackSpace>', self.backspace_situations)
        text.bind('<Alt_L>', self.hide_and_unhide_menubar)
        text.bind('<Control-L>', self.toggle_linenumbers)
        text.bind('<KeyPress-Tab>', self.tab_text)
Exemplo n.º 4
0
class CustomText(tk.Text):
    def __init__(self, *args, **kwargs):
        tk.Text.__init__(self, *args, **kwargs)

        self.loader = QuietLoaders()
        # create a proxy for the underlying widget
        self.settings = self.loader.load_settings_data()
        self.bg_color = self.settings['textarea_background_color']
        self.fg_color = self.settings['menu_fg']
        self.active_fg = self.settings['menu_active_fg']
        self.active_bg = self.settings['menu_active_bg']
        self.isControlPressed = False
        self._orig = self._w + '_orig'
        self.tk.call('rename', self._w, self._orig)
        self.tk.createcommand(self._w, self._proxy)

    def _proxy(self, *args):
        # let the actual widget perform the requested action
        try:
            cmd = (self._orig, ) + args
            result = ''
            if not self.isControlPressed:
                # if command is not present, execute the event
                result = self.tk.call(cmd)
            else:
                # Suppress y-scroll and x-scroll when control is pressed
                if args[0:2] not in [('yview', 'scroll'), ('xview', 'scroll')]:
                    result = self.tk.call(cmd)
        except tk.TclError:
            result = ''

        # generate an event if something was added or deleted,
        # or the cursor position changed
        if (args[0] in ('insert', 'replace', 'delete')
                or args[0:3] == ('mark', 'set', 'insert')
                or args[0:2] == ('xview', 'moveto')
                or args[0:2] == ('xview', 'scroll')
                or args[0:2] == ('yview', 'moveto')
                or args[0:2] == ('yview', 'scroll')):
            self.event_generate('<<Change>>', when='tail')

        # return what the actual widget returned
        return result

    def find(self, text_to_find):
        length = tk.IntVar()
        index = self.search(text_to_find,
                            self.find_search_starting_index,
                            stopindex=tk.END,
                            count=length)

        if index:
            self.tag_remove('find_match', 1.0, tk.END)

            end = f'{index}+{length.get()}c'
            self.tag_add('find_match', index, end)
            self.see(index)

            self.find_search_starting_index = end
            self.find_match_index = index
        else:
            if self.find_match_index != 1.0:
                if tk.messagebox.askyesno(
                        "No more results",
                        "No further matches. Repeat from the beginning?"):
                    self.find_search_starting_index = 1.0
                    self.find_match_index = None
                    return self.find(text_to_find)
            else:
                tk.messagebox.showinfo("No Matches", "No matching text found")

    def replace_text(self, target, replacement):
        if self.find_match_index:
            current_found_index_line = str(self.find_match_index).split('.')[0]

            end = f"{self.find_match_index}+{len(target)}c"
            self.replace(self.find_match_index, end, replacement)

            self.find_search_starting_index = current_found_index_line + '.0'

    def cancel_find(self):
        self.find_search_starting_index = 1.0
        self.find_match_index = None
        self.tag_remove('find_match', 1.0, tk.END)

    def reload_text_settings(self):
        settings = self.loader.load_settings_data()
        self.bg_color = settings['textarea_background_color']
        self.bg_color = settings['textarea_background_color']
        self.fg_color = settings['menu_fg']
        self.active_fg = settings['menu_active_fg']
        self.active_bg = settings['menu_active_bg']