5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
43 gtk.gdk.threads_init()
51 # here we define the path constants so that other modules can use it.
52 # this allows us to get access to the shared files without having to
53 # know the actual location, we just use the location of the current
54 # file and use paths relative to that.
55 SHARED_FILES = os.path.abspath(os.path.join(os.path.dirname(__file__),
58 LOCALE_PATH = os.path.join('/usr', 'share', 'locale')
59 RESOURCE_PATH = os.path.join(SHARED_FILES, 'res')
61 # the name of the gettext domain. because we have our translation files
62 # not in a global folder this doesn't really matter, setting it to the
63 # application name is a good idea tough.
64 GETTEXT_DOMAIN = 'time-slider'
66 # set up the glade gettext system and locales
67 gtk.glade.bindtextdomain(GETTEXT_DOMAIN, LOCALE_PATH)
68 gtk.glade.textdomain(GETTEXT_DOMAIN)
71 MEGABYTES = KILOBYTES*1024
72 GIGABYTES = MEGABYTES*1024
73 TERABYTES = GIGABYTES*1024
77 (TERABYTES, '%0.1f TB'),
78 (GIGABYTES, '%0.1f GB'),
79 (MEGABYTES, '%0.1f MB'),
80 (KILOBYTES, '%0.1f KB'),
83 def __init__(self, path):
85 self.file = gio.File (path)
87 self.info = self.file.query_info ("*")
94 icon_factory = gnome.ui.ThumbnailFactory(gnome.ui.THUMBNAIL_SIZE_NORMAL)
95 mtime = os.path.getmtime(self.path)
96 uri = gnomevfs.make_uri_from_input(self.path)
97 thumb = icon_factory.lookup (uri, mtime)
99 return gtk.gdk.pixbuf_new_from_file (thumb)
100 thumb = icon_factory.generate_thumbnail (uri, self.info.get_content_type())
102 icon_factory.save_thumbnail (thumb, uri, mtime)
104 #fallback get the themed icon
105 return gtk.icon_theme_get_default().choose_icon (self.info.get_icon().get_property ("names"), 48, gtk.ICON_LOOKUP_USE_BUILTIN).load_icon ()
108 amount = self.info.get_size ()
109 for treshold, template in self.displayTemplates:
110 if amount > treshold:
112 amount = amount /treshold
113 return "%s (%d bytes)" % (template % amount, self.info.get_size ())
114 return "%d byte" % amount
116 def add_if_unique (self, versions):
118 for file in versions:
119 if int (file.info.get_modification_time ()) == int (self.info.get_modification_time ()):
122 versions.append (self)
126 def get_mime_type (self):
127 return gnomevfs.get_mime_type(gnomevfs.make_uri_from_input(self.path))
139 class FileVersionWindow:
140 meld_hint_displayed = False
142 def __init__(self, snap_path, file):
143 self.snap_path = snap_path
145 self.xml = gtk.glade.XML("%s/../../glade/time-slider-version.glade" \
146 % (os.path.dirname(__file__)))
147 self.window = self.xml.get_widget("toplevel")
148 self.progress = self.xml.get_widget("progress")
149 self.version_label = self.xml.get_widget("num_versions_label")
151 dic = {"on_toplevel_delete_event": self.exit3 ,
152 "on_close_clicked": self.exit ,
153 "on_compare_button_clicked": self.on_compare_button_clicked,
154 "on_current_file_button_clicked": self.on_current_file_button_clicked,
155 "on_treeview_row_activated": self.on_treeview_row_activated,
156 "on_treeview_cursor_changed": self.on_treeview_cursor_changed}
157 self.xml.signal_autoconnect(dic)
159 self.filename_label = self.xml.get_widget("filename_label")
160 self.size_label = self.xml.get_widget("size_label")
161 self.date_label = self.xml.get_widget("date_label")
162 self.older_versions_label = self.xml.get_widget("older_versions_label")
163 self.compare_button = self.xml.get_widget("compare_button")
164 self.button_init = False
168 self.file = File (file)
169 self.filename_label.set_text (self.file.info.get_name ())
170 self.size_label.set_text (self.file.get_size ())
171 self.date_label.set_text (time.strftime ("%d/%m/%y %Hh%Ms%S", time.localtime(self.file.info.get_modification_time ())))
172 self.xml.get_widget("icon_image").set_from_pixbuf (self.file.get_icon ())
174 self.treeview = self.xml.get_widget("treeview")
175 self.model = gtk.ListStore(gtk.gdk.Pixbuf,
181 self.treeview.set_model (self.model)
182 self.__add_columns (self.treeview)
184 self.scanner = VersionScanner (self)
187 def __add_columns(self, treeview):
188 model = treeview.get_model()
190 renderer = gtk.CellRendererPixbuf()
191 column = gtk.TreeViewColumn('Icon', renderer, pixbuf=COLUMN_ICON)
192 treeview.append_column(column)
194 self.date_column = gtk.TreeViewColumn('Last Modified Date', gtk.CellRendererText(),
195 text=COLUMN_STRING_DATE)
196 self.date_column.set_sort_column_id(COLUMN_DATE)
197 treeview.append_column(self.date_column)
199 # column for description
200 column = gtk.TreeViewColumn('Size', gtk.CellRendererText(),
202 column.set_sort_column_id(COLUMN_SIZE)
203 treeview.append_column(column)
205 def add_file (self, file):
206 iter = self.model.append ()
207 self.model.set (iter,
208 COLUMN_ICON, file.get_icon (),
209 COLUMN_NAME, file.path,
210 COLUMN_STRING_DATE, time.strftime ("%d/%m/%y %Hh%Ms%S", time.localtime(file.info.get_modification_time ())),
211 COLUMN_DATE, file.info.get_modification_time (),
212 COLUMN_SIZE, file.get_size ())
214 def exit3 (self, blah, blih):
217 def exit (self, blah):
221 def on_current_file_button_clicked (self, widget):
222 application = gnomevfs.mime_get_default_application (gnomevfs.get_mime_type(gnomevfs.make_uri_from_input(self.filename)))
224 subprocess.Popen (str.split (application[2]) + [self.filename])
226 def on_treeview_row_activated (self, treeview, path, column):
227 (model, iter) = treeview.get_selection ().get_selected ()
228 filename = model.get (iter, 1)[0]
229 application = gnomevfs.mime_get_default_application (gnomevfs.get_mime_type(gnomevfs.make_uri_from_input(filename)))
231 subprocess.Popen (str.split (application[2]) + [filename])
233 def on_treeview_cursor_changed (self, treeview):
234 if not self.button_init:
235 self.button_init = True
236 if self.file.get_mime_type ().find ("text") != -1 :
237 self.compare_button.set_sensitive (True)
239 def on_compare_button_clicked (self, widget):
240 (model, iter) = self.treeview.get_selection ().get_selected ()
241 filename = model.get (iter, 1)[0]
242 if os.path.exists ("/usr/bin/meld"):
243 subprocess.Popen (["/usr/bin/meld",self.filename, filename])
245 if not self.meld_hint_displayed:
246 dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, _("Hint"))
247 dialog.set_title (_("Hint"))
248 dialog.format_secondary_text(_("Installing the optional meld package will enhance the file comparison visualization"))
251 self.meld_hint_displayed = True
252 p1 = subprocess.Popen(["/usr/bin/diff", "-u", self.filename, filename], stdout=subprocess.PIPE)
253 p2 = subprocess.Popen(str.split ("/usr/bin/zenity --text-info --editable"), stdin=p1.stdout, stdout=subprocess.PIPE)
256 class VersionScanner(threading.Thread):
258 def __init__(self, window):
260 self._stopevent = threading.Event()
261 threading.Thread.__init__(self)
264 l = self.w.snap_path.split (".zfs")
265 path_before_snap = l[0]
266 l = self.w.filename.split (path_before_snap)
267 path_after_snap = l[1]
268 snap_path = "%s.zfs/snapshot/" % path_before_snap;
269 dirs = os.listdir(snap_path)
274 gobject.idle_add (self.w.progress.set_pulse_step, (1.0 / num_dirs))
275 gobject.idle_add (self.w.progress.set_text, ("Scanning for older versions (%d/%d)" % (current_dir, num_dirs)))
277 versions = [File (self.w.filename)]
280 if not self._stopevent.isSet ():
281 file = File ("%s%s/%s" % (snap_path, dir, path_after_snap))
283 if file.add_if_unique(versions):
284 gobject.idle_add (self.w.add_file, file)
285 fraction = self.w.progress.get_fraction ()
286 fraction += self.w.progress.get_pulse_step ()
290 gobject.idle_add (self.w.progress.set_fraction, fraction)
292 gobject.idle_add (self.w.progress.set_text, "Scanning for older versions (%d/%d)" % (current_dir, num_dirs))
296 gobject.idle_add(self.w.progress.hide)
297 gobject.idle_add(self.w.older_versions_label.set_markup , "<b>Older Versions</b> (%d) " % (len(versions) - 1))
299 gobject.idle_add(self.w.date_column.emit, "clicked")
300 gobject.idle_add(self.w.date_column.emit, "clicked")
302 def join(self, timeout=None):
303 self._stopevent.set ()
304 threading.Thread.join(self, timeout)
308 opts, args = getopt.getopt(sys.argv[1:], "", [])
309 except getopt.GetoptError:
312 dialog = gtk.MessageDialog(None,
316 _("Invalid arguments count."))
317 dialog.set_title ("Error")
318 dialog.format_secondary_text(_("Version explorer requires"
319 " 2 arguments :\n- The path of the "
320 "root snapshot directory.\n"
321 "- The filename to explore."))
325 window = FileVersionWindow(args[0], args[1])
326 gtk.gdk.threads_enter()
328 gtk.gdk.threads_leave()