1#!/usr/local/bin/python3.8
2
3import sys
4import signal
5signal.signal(signal.SIGINT, signal.SIG_DFL)
6import _thread
7import gi
8gi.require_version('Gtk', '3.0')
9gi.require_version('Gdk', '3.0')
10from gi.repository import Gdk, Gtk, GObject, GLib, Pango
11
12import time
13from datetime import timedelta
14
15class Main:
16    def __init__(self):
17        if len(sys.argv) > 1 and sys.argv[1] == "--help":
18            self.end()
19        else:
20            self.start()
21
22    def end(self):
23        print("""
24              git-monkey: a simple repo manager for debian-based projects.
25
26              Run git-monkey, then click the + to add git repos to work with.
27
28              """)
29        quit()
30
31    def start(self):
32        self.builder = Gtk.Builder()
33        self.builder.add_from_file("cinnamon-stap-monitor.glade")
34        self.treebox = self.builder.get_object("treebox")
35        self.window = self.builder.get_object("window")
36        self.term_button = self.builder.get_object("terminal")
37        self.output_scroller = self.builder.get_object("scroller")
38        self.output = self.builder.get_object("output_view")
39        self.timer_label = self.builder.get_object("timer_label")
40
41        self.builder.connect_signals(self)
42
43        self.treeview = Gtk.TreeView()
44        self.model = Gtk.ListStore(str, int, int)
45
46        color = Gdk.RGBA()
47        Gdk.RGBA.parse(color, "black")
48        self.output.override_background_color(Gtk.StateFlags.NORMAL, color)
49        Gdk.RGBA.parse(color, "#00CC00")
50
51        fontdesc = Pango.FontDescription("monospace")
52        self.output.override_font(fontdesc)
53        self.output.override_color(Gtk.StateFlags.NORMAL, color)
54
55        self.window.connect("destroy", Gtk.main_quit)
56
57        cell = Gtk.CellRendererText()
58        column = Gtk.TreeViewColumn("GObject Name", cell, text=0)
59        column.set_sort_column_id(0)
60        self.treeview.append_column(column)
61
62        cell = Gtk.CellRendererText()
63        column = Gtk.TreeViewColumn("Instance count", cell, text=1)
64        column.set_sort_column_id(1)
65
66        self.treeview.append_column(column)
67
68        cell = Gtk.CellRendererText()
69        column = Gtk.TreeViewColumn("Rate", cell, text=2)
70        column.set_sort_column_id(2)
71
72        self.treeview.append_column(column)
73
74        self.treeview.set_model(self.model)
75
76        self.treebox.add(self.treeview)
77        # self.treeview.get_selection().connect("changed", lambda x: self.selection_changed())
78        # self.treeview.connect('button_press_event', self.on_button_press_event)
79
80        self.start_stdin_feed()
81
82        self.window.show_all()
83
84        GObject.timeout_add_seconds(1, self.update_timer_label)
85
86    # def on_button_press_event(self, widget, event):
87    #     if event.button == 1:
88    #         data=widget.get_path_at_pos(int(event.x),int(event.y))
89    #         if data:
90    #             path, column, x, y = data
91    #             if column.get_property('title')=="Abort":
92    #                 iter = self.model.get_iter(path)
93    #                 repo = self.model.get_value(iter, 0)
94    #                 self.job_manager.find_and_abort(repo)
95    #             elif event.type == Gdk.EventType._2BUTTON_PRESS:
96    #                 iter = self.model.get_iter(path)
97    #                 repo = self.model.get_value(iter, 0)
98    #                 if len(repo.state) == 0:
99    #                     repoedit.EditRepo(repo.dir, repo.upstream_remote, repo.upstream_branch, repo.push_remote)
100
101    def reset_timer(self):
102        self.start_time = time.time()
103
104    def update_timer_label(self):
105        now = time.time()
106
107        elapsed = now - self.start_time
108
109        time_passed = timedelta(seconds=int(elapsed + .5))
110
111        self.timer_label.set_text("Elapsed time: {}".format(time_passed))
112
113        return True
114
115    def start_stdin_feed(self):
116        self.reset_timer()
117        self.cancel_lock = _thread.allocate_lock()
118        self.cancelled = False
119        self.thread = _thread.start_new_thread(self.stdin_feed_thread, ())
120
121    def stdin_feed_thread(self):
122        for line in sys.stdin:
123            GObject.idle_add(self.handle_line, line)
124            self.cancel_lock.acquire()
125            cancelled = self.cancelled
126            self.cancel_lock.release()
127            if cancelled:
128                break
129
130        _thread.exit()
131
132    def handle_line(self, line):
133        if line[:7] == "GObject":
134            [prefix, name, delta] = line.split(":::")
135
136            [new, target_iter] = self.lookup_name_or_new(name)
137
138            if new:
139                self.model.set_value(target_iter, 0, name)
140                self.model.set_value(target_iter, 1, int(delta))
141                self.model.set_value(target_iter, 2, int(delta))
142            else:
143                current_val = self.model.get_value(target_iter, 1)
144                current_val = int(current_val) + int(delta)
145                self.model.set_value(target_iter, 1, current_val)
146                self.model.set_value(target_iter, 2, int(delta))
147
148        else:
149            self.write_line_to_buffer(line)
150
151    def lookup_name_or_new(self, name):
152        row_iter = self.model.get_iter_first()
153
154        while row_iter != None:
155            existing_name = self.model.get_value(row_iter, 0)
156            if name == existing_name:
157                return [False, row_iter]
158
159            row_iter = self.model.iter_next(row_iter)
160
161        return [True, self.model.insert_before(None, None)]
162
163
164    # def selection_changed(self):
165    #     model, treeiter = self.treeview.get_selection().get_selected()
166    #     if treeiter:
167    #         repo = self.model.get_value(treeiter, 0)
168    #         self.current_repo = repo
169    #         self.update_branch_combo(repo)
170    #         self.clean_button.set_sensitive(len(repo.untracked_files) != 0)
171    #         self.reset_button.set_sensitive(repo.is_dirty())
172    #         self.term_button.set_sensitive(True)
173    #         self.full_build_button.set_sensitive(True)
174    #         self.new_branch.set_sensitive(True)
175    #         self.rebase_button.set_sensitive(True)
176    #         self.pull_request_button.set_sensitive(True)
177    #         no_active = len(repo.state) == 0
178    #         self.branch_combo.set_sensitive(no_active)
179    #         self.remove_repo_button.set_sensitive(no_active)
180    #         self.refresh_button.set_sensitive(no_active)
181    #         self.add_repo_button.set_sensitive(no_active)
182    #         self.master_button.set_sensitive(no_active and repo.head.reference.name != repo.upstream_branch)
183
184    def on_terminal_clicked(self, button):
185        subprocess.Popen("gnome-terminal", cwd=self.current_repo.dir, shell=True)
186
187    def on_reset_clicked(self, button):
188        self.model.clear()
189        self.reset_timer()
190
191    def write_to_buffer(self, fd, condition):
192        if condition == GLib.IO_IN:
193            char = fd.readline()
194            buf = self.output.get_buffer()
195            iter = buf.get_end_iter()
196            buf.insert(iter, char)
197            iter = buf.get_end_iter()
198            self.output.scroll_to_iter(iter, .2, False, 0, 0)
199            # adj = self.output.get_vadjustment()
200            # if adj.get_value() >= adj.get_upper() - adj.get_page_size() - 200.0:
201            # adj.set_value(adj.get_upper())
202            return True
203        else:
204            return False
205
206    def write_line_to_buffer(self, string):
207        buf = self.output.get_buffer()
208        iter = buf.get_end_iter()
209        buf.insert(iter, string)
210        iter = buf.get_end_iter()
211        self.output.scroll_to_iter(iter, .2, False, 0, 0)
212
213    def ask(self, msg):
214        dialog = Gtk.MessageDialog(None,
215                                   Gtk.DialogFlags.DESTROY_WITH_PARENT,
216                                   Gtk.MessageType.QUESTION,
217                                   Gtk.ButtonsType.YES_NO,
218                                   None)
219        dialog.set_default_size(400, 200)
220        dialog.set_markup(msg)
221        dialog.show_all()
222        response = dialog.run()
223        dialog.destroy()
224        return response == Gtk.ResponseType.YES
225
226    def inform_error(self, msg, detail):
227        dialog = Gtk.MessageDialog(None,
228                                   Gtk.DialogFlags.DESTROY_WITH_PARENT,
229                                   Gtk.MessageType.ERROR,
230                                   Gtk.ButtonsType.OK,
231                                   None)
232        dialog.set_default_size(400, 200)
233        dialog.set_markup(msg)
234        dialog.format_secondary_markup(detail)
235        dialog.show_all()
236        response = dialog.run()
237        dialog.destroy()
238        return
239
240    def inform(self, msg, detail):
241        dialog = Gtk.MessageDialog(None,
242                                   Gtk.DialogFlags.DESTROY_WITH_PARENT,
243                                   Gtk.MessageType.INFO,
244                                   Gtk.ButtonsType.OK,
245                                   None)
246        dialog.set_default_size(400, 200)
247        dialog.set_markup(msg)
248        dialog.format_secondary_markup(detail)
249        dialog.show_all()
250        response = dialog.run()
251        dialog.destroy()
252        return
253
254if __name__ == "__main__":
255    Main()
256    Gtk.main()
257