1# Plugin by Sinan Nalkaya <sardok@gmail.com>
2# See LICENSE of Terminator package.
3
4""" logger.py - Terminator Plugin to log 'content' of individual
5terminals """
6
7import os
8import sys
9from gi.repository import Gtk
10import terminatorlib.plugin as plugin
11from terminatorlib.translation import _
12
13AVAILABLE = ['Logger']
14
15class Logger(plugin.MenuItem):
16    """ Add custom command to the terminal menu"""
17    capabilities = ['terminal_menu']
18    loggers = None
19    dialog_action = Gtk.FileChooserAction.SAVE
20    dialog_buttons = (_("_Cancel"), Gtk.ResponseType.CANCEL,
21                      _("_Save"), Gtk.ResponseType.OK)
22
23    def __init__(self):
24        plugin.MenuItem.__init__(self)
25        if not self.loggers:
26            self.loggers = {}
27
28    def callback(self, menuitems, menu, terminal):
29        """ Add save menu item to the menu"""
30        vte_terminal = terminal.get_vte()
31        if vte_terminal not in self.loggers:
32            item = Gtk.MenuItem.new_with_mnemonic(_('Start _Logger'))
33            item.connect("activate", self.start_logger, terminal)
34        else:
35            item = Gtk.MenuItem.new_with_mnemonic(_('Stop _Logger'))
36            item.connect("activate", self.stop_logger, terminal)
37            item.set_has_tooltip(True)
38            item.set_tooltip_text("Saving at '" + self.loggers[vte_terminal]["filepath"] + "'")
39        menuitems.append(item)
40
41    def write_content(self, terminal, row_start, col_start, row_end, col_end):
42        """ Final function to write a file """
43        content = terminal.get_text_range(row_start, col_start, row_end, col_end,
44                                          lambda *a: True)
45        content = content[0]
46        fd = self.loggers[terminal]["fd"]
47        # Don't write the last char which is always '\n'
48        fd.write(content[:-1])
49        self.loggers[terminal]["col"] = col_end
50        self.loggers[terminal]["row"] = row_end
51
52    def save(self, terminal):
53        """ 'contents-changed' callback """
54        last_saved_col = self.loggers[terminal]["col"]
55        last_saved_row = self.loggers[terminal]["row"]
56        (col, row) = terminal.get_cursor_position()
57        # Save only when buffer is nearly full,
58        # for the sake of efficiency
59        if row - last_saved_row < terminal.get_row_count():
60            return
61        self.write_content(terminal, last_saved_row, last_saved_col, row, col)
62
63    def start_logger(self, _widget, Terminal):
64        """ Handle menu item callback by saving text to a file"""
65        savedialog = Gtk.FileChooserDialog(title=_("Save Log File As"),
66                                           action=self.dialog_action,
67                                           buttons=self.dialog_buttons)
68        savedialog.set_transient_for(_widget.get_toplevel())
69        savedialog.set_do_overwrite_confirmation(True)
70        savedialog.set_local_only(True)
71        savedialog.show_all()
72        response = savedialog.run()
73        if response == Gtk.ResponseType.OK:
74            try:
75                logfile = os.path.join(savedialog.get_current_folder(),
76                                       savedialog.get_filename())
77                fd = open(logfile, 'w+')
78                # Save log file path,
79                # associated file descriptor, signal handler id
80                # and last saved col,row positions respectively.
81                vte_terminal = Terminal.get_vte()
82                (col, row) = vte_terminal.get_cursor_position()
83
84                self.loggers[vte_terminal] = {"filepath":logfile,
85                                              "handler_id":0, "fd":fd,
86                                              "col":col, "row":row}
87                # Add contents-changed callback
88                self.loggers[vte_terminal]["handler_id"] = vte_terminal.connect('contents-changed', self.save)
89            except:
90                e = sys.exc_info()[1]
91                error = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR,
92                                          Gtk.ButtonsType.OK, e.strerror)
93                error.set_transient_for(savedialog)
94                error.run()
95                error.destroy()
96        savedialog.destroy()
97
98    def stop_logger(self, _widget, terminal):
99        vte_terminal = terminal.get_vte()
100        last_saved_col = self.loggers[vte_terminal]["col"]
101        last_saved_row = self.loggers[vte_terminal]["row"]
102        (col, row) = vte_terminal.get_cursor_position()
103        if last_saved_col != col or last_saved_row != row:
104            # Save unwritten bufer to the file
105            self.write_content(vte_terminal, last_saved_row, last_saved_col, row, col)
106        fd = self.loggers[vte_terminal]["fd"]
107        fd.close()
108        vte_terminal.disconnect(self.loggers[vte_terminal]["handler_id"])
109        del(self.loggers[vte_terminal])
110