1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # GNU Library General Public License for more details.
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 # See the COPYING file for license information.
17 # Copyright (c) 2006 Guillaume Chazarain <guichaz@yahoo.fr>
23 assert gtk
.pygtk_version
>= (2, 8)
29 from pysize
.ui
.gtk
.gazpacho_loader
.loader
import ObjectBuilder
31 from pysize
.ui
.gtk
.pysize_widget
import PysizeWidget
32 from pysize
.ui
.utils
import human_unit
, short_string
, sanitize_string
33 from pysize
.core
import compute_size
34 from pysize
.core
.compute_size
import size_observable
35 from pysize
.core
import history
36 from pysize
.core
.deletion
import get_deleted
, restore
38 def hover_changed(main_widget
, hover_node
, status_bar
):
40 node
= hover_node
or main_widget
.tree
.root
41 label
= human_unit(node
.size
) + ' | ' + node
.get_fullname()
42 status_bar
.push(0, label
)
44 def size_observer(progress_bar
):
46 if now
- size_observer
.last_pulse
> 0.04:
47 size_observer
.last_pulse
= now
48 gobject
.idle_add(lambda: progress_bar
.pulse())
49 size_observer
.last_pulse
= time
.time()
51 def hide_pbar(progress_bar
, visible
):
57 def update_action(zoom_fit_action
, main_widget
):
58 # Enable if the zoom is not already 'auto' and there is a tree
59 enable
= main_widget
.options
.min_size
!= 'auto' and \
60 not not main_widget
.tree
.root
.get_fullpaths()
61 zoom_fit_action
.set_sensitive(enable
)
63 def update_title(window
, main_widget
):
64 root
= main_widget
.tree
.root
65 window
.set_title('Pysize - %s | %s' %
66 (human_unit(root
.size
), sanitize_string(root
.get_name())))
68 def update_back_action(back_action
, next_insertion_index
, history
):
69 back_action
.set_sensitive(next_insertion_index
> 1)
71 def update_forward_action(forward_action
, next_insertion_index
, history
):
72 forward_action
.set_sensitive(next_insertion_index
< len(history
))
74 def update_hist_menu(main_widget
, hist_menu
, next_insertion_index
, hist_list
):
75 class activator(object):
76 def __init__(self
, index
):
79 def activate(self
, unused_menu_item
):
80 main_widget
.set_paths(history
.go_to_history(self
.index
))
82 for nr
, item
in enumerate(hist_menu
):
83 if nr
> 2: # Keep 'Back' and 'Forward'
84 hist_menu
.remove(item
)
85 item
= gtk
.SeparatorMenuItem()
88 for nr
, (paths
, name
) in enumerate(hist_list
):
89 # We want underscores, not mnemonics
90 name
= short_string(sanitize_string(name
), 100).replace('_', '__')
91 item
= gtk
.CheckMenuItem(name
)
92 if nr
+ 1 == next_insertion_index
:
94 item
.connect('activate', activator(nr
).activate
)
95 item
.set_draw_as_radio(True)
99 def show_deleted_files(dialog
, treeview
):
100 model
= treeview
.get_model()
102 for path
in get_deleted():
103 size
= human_unit(compute_size
.slow(path
, account_deletion
=False))
104 model
.append((size
, path
))
108 def iter_selection(treeview
):
109 model
= treeview
.get_model()
110 selection
= treeview
.get_selection().get_selected_rows()[1]
113 for treepath
in selection
:
114 treeiter
= model
.get_iter(treepath
)
115 path
= model
.get_value(treeiter
, 1)
118 def restore_deleted_files(treeview
, main_widget
):
119 if treeview
.get_selection().count_selected_rows() > 0:
120 model
= treeview
.get_model()
121 for treeiter
, path
in iter_selection(treeview
):
123 model
.remove(treeiter
)
124 main_widget
.schedule_new_tree()
126 def delete_deleted_files(treeview
):
127 if treeview
.get_selection().count_selected_rows() <= 0:
129 dialog
= gtk
.MessageDialog(parent
=None, flags
=gtk
.DIALOG_MODAL
,
130 type=gtk
.MESSAGE_WARNING
,
131 buttons
=gtk
.BUTTONS_YES_NO
,
132 message_format
='The selected will be permanently'
133 'deleted, do you really want to delete them?')
134 response
= dialog
.run()
136 if response
== gtk
.RESPONSE_YES
:
137 model
= treeview
.get_model()
138 for treeiter
, path
in iter_selection(treeview
):
140 if os
.path
.isdir(path
):
144 model
.remove(treeiter
)
146 class PysizeWindow(object):
147 def __init__(self
, options
, args
):
148 create_widget
= lambda **unused_kwargs
: PysizeWidget(options
, args
)
149 glade_filename
= os
.path
.join(os
.path
.dirname(__file__
),
151 builder
= ObjectBuilder(glade_filename
,
152 custom
= {'pysize_widget': create_widget
})
154 main_widget
= builder
.get_widget('main_widget')
155 status_bar
= builder
.get_widget('status_bar')
156 main_widget
.connect('hover_changed', hover_changed
, status_bar
)
158 progress_bar
= builder
.get_widget('progress_bar')
159 size_observable
.add_observer(lambda: size_observer(progress_bar
))
160 pbar_hider
= lambda w
, building
: hide_pbar(progress_bar
, building
)
161 main_widget
.connect('building-tree-state-changed', pbar_hider
)
163 max_depth
= builder
.get_widget('max_depth')
164 spin_init
= lambda w
: max_depth
.set_value(main_widget
.options
.max_depth
)
165 main_widget
.connect('realize', spin_init
)
167 ui_manager
= builder
.get_widget('uimanager')
168 zoom_fit_action
= ui_manager
.get_action('ui/toolbar/ZoomFit')
169 action_updater
= lambda w
, building
: update_action(zoom_fit_action
, w
)
170 main_widget
.connect('building-tree-state-changed', action_updater
)
172 # Disable actions when the tree is empty: pysize launched with no arg
173 class disabler(object):
174 def __init__(self
, action
):
176 def disable(self
, main_widget
, building
):
177 sensitive
= not not main_widget
.tree
.root
.get_fullpaths()
178 self
.action
.set_sensitive(sensitive
)
179 for name
in 'ParentDirectory', 'Refresh', 'ZoomIn', 'ZoomOut':
180 action
= ui_manager
.get_action('ui/toolbar/' + name
)
181 action_disabler
= disabler(action
).disable
182 main_widget
.connect('building-tree-state-changed', action_disabler
)
184 back_action
= ui_manager
.get_action('ui/toolbar/Back')
185 back_action
.set_sensitive(False)
186 update_back
= lambda n
, h
: update_back_action(back_action
, n
, h
)
187 history
.history_observable
.add_observer(update_back
)
189 forward_action
= ui_manager
.get_action('ui/toolbar/Forward')
190 forward_action
.set_sensitive(False)
191 update_forw
= lambda n
, h
: update_forward_action(forward_action
, n
, h
)
192 history
.history_observable
.add_observer(update_forw
)
194 hist_menu_action
= ui_manager
.get_action('ui/menubar/HistoryMenu')
195 hist_menu
= hist_menu_action
.get_proxies()[0].get_submenu()
196 upd_hist
= lambda n
, h
: update_hist_menu(main_widget
, hist_menu
, n
, h
)
197 history
.history_observable
.add_observer(upd_hist
)
199 toolbar
= builder
.get_widget('toolbar')
200 toolbar_remaining
= builder
.get_widget('toolbar_remaining')
201 toolbar_remaining
.unparent()
202 tool_item
= gtk
.ToolItem()
203 tool_item
.add(toolbar_remaining
)
204 toolbar
.insert(tool_item
, -1)
206 deleted_files_dialog
= builder
.get_widget('deleted_files_dialog')
207 deleted_files_treeview
= builder
.get_widget('deleted_files_treeview')
208 deleted_files_treeview
.get_selection().set_mode(gtk
.SELECTION_MULTIPLE
)
210 logo_filename
= os
.path
.join(os
.path
.dirname(__file__
), 'logo.svg')
211 gtk
.window_set_default_icon_from_file(logo_filename
)
214 'quit_action': lambda w
: gtk
.main_quit(),
215 'refresh_tree_action': lambda w
: main_widget
.refresh_tree(),
216 'zoom_fit_action': lambda w
: main_widget
.zoom_fit(),
217 'zoom_in_action': lambda w
: main_widget
.zoom_in(),
218 'zoom_out_action': lambda w
: main_widget
.zoom_out(),
219 'parent_directory_action': lambda w
: main_widget
.parent_directory(),
220 'open_action': lambda w
: main_widget
.open(),
221 'back_action': lambda w
: main_widget
.set_paths(history
.back()),
222 'forw_action': lambda w
: main_widget
.set_paths(history
.forward()),
223 'deleted_files_action': lambda w
:
224 show_deleted_files(deleted_files_dialog
, deleted_files_treeview
),
225 'close_deleted_files': lambda b
: deleted_files_dialog
.hide(),
226 'restore_deleted_files': lambda b
:
227 restore_deleted_files(deleted_files_treeview
, main_widget
),
228 'delete_deleted_files': lambda b
:
229 delete_deleted_files(deleted_files_treeview
),
230 'max_depth_changed': main_widget
.max_depth_changed
232 builder
.signal_autoconnect(callbacks
)
234 window
= builder
.get_widget('main_window')
235 title_updater
= lambda w
, tree
: update_title(window
, main_widget
)
236 main_widget
.connect('building-tree-state-changed', title_updater
)