62
62
self.icon = awn.ThemedIcon()
63
63
self.icon.set_tooltip_text(_("File Browser Launcher"))
64
64
self.icon.set_size(self.get_size())
65
self.dialog = awn.Dialog(self)
65
self.dialog = awn.Dialog(self.icon, self)
67
67
#AwnConfigClient instance
68
68
self.client = awn.config_get_default_for_applet(self)
70
70
#Get the default icon theme
71
71
self.theme = gtk.icon_theme_get_default()
72
72
self.icons[24] = {}
73
self.icons[24]['stock_folder'] = self.theme.load_icon('stock_folder', 24, 0)
73
self.icons[24]['folder'] = self.theme.load_icon('folder', 24, 0)
76
76
self.mode = self.client.get_int(group, 'mode')
84
84
self.icon_box.add(self.icon)
87
self.icon.set_info_simple('file-browser-launcher', uid, 'stock_folder')
87
self.icon.set_info_simple('file-browser-launcher', uid, 'folder')
89
#This part (and other progress overlay code) adapted from
90
#mhr3's 'Dropper' applet
90
#This part (and other progress overlay code) adapted from
91
#mhr3's 'Dropper' applet
92
92
#Set the progress overlay
93
93
self.timer_overlay = awn.OverlayProgressCircle()
94
94
self.timer_overlay.props.active = False
95
95
self.timer_overlay.props.apply_effects = False
96
96
self.icon.add_overlay(self.timer_overlay)
99
98
#Read fstab for mounting info
100
99
#(It it assumed that fstab won't change after the applet is started)
103
102
self.fstab2.close()
105
104
#Check if nautilus-connect-server is installed
106
if os.path.exists('/usr/bin/nautilus-connect-server') or os.path.exists\
105
if os.path.exists('/usr/bin/nautilus-connect-server') or os.path.exists \
107
106
('/usr/local/bin/nautilus-connect-server'):
108
107
self.nautilus_connect_server = True
110
109
self.nautilus_connect_server = False
111
if os.path.exists('/usr/share/applications/nautilus-computer.desktop') or \
112
os.path.exists('/usr/local/share/applications/nautilus-computer.desktop'):
113
self.nautilus_computer = True
115
self.nautilus_computer = False
117
def trash_count_cb(*args):
124
self.trash = vfs.Trash.get_default()
125
self.trash.connect('file-count-changed', trash_count_cb)
112
127
#Make the dialog, will only be shown when approiate
113
128
#Make all the things needed for a treeview for the homefolder, root dir, bookmarks, and mounted drives
114
129
self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str, gtk.gdk.Pixbuf, str, int)
144
160
'mount-changed', 'mount-removed'):
145
161
self.monitor.connect(signal, self.do_gio_places)
147
for key in ('show_home', 'show_local', 'show_network', 'show_connect', 'show_bookmarks',
163
for key in ('show_computer', 'show_home', 'show_filesystem', 'show_local', 'show_network',
164
'show_connect', 'show_trash', 'show_bookmarks'):
149
165
self.client.notify_add(group, key, self.do_gio_places)
151
167
self.do_gio_places()
169
185
self.entry = gtk.Entry()
170
186
self.entry.set_text(os.environ['HOME'])
171
187
self.entry.connect('key-release-event', self.detect_enter)
172
190
#Open button to run the file browser
173
191
self.enter = gtk.Button(stock=gtk.STOCK_OPEN)
174
192
self.enter.connect('clicked', self.launch_fb)
175
195
#HBox to put the two together
176
self.hbox = gtk.HBox()
177
self.hbox.pack_start(self.entry)
178
self.hbox.pack_start(self.enter, False)
196
entry_hbox = gtk.HBox()
197
entry_hbox.pack_start(self.entry)
198
entry_hbox.pack_start(self.enter, False)
179
200
#And add the HBox to the vbox and add the vbox to the dialog
180
self.vbox.pack_end(self.hbox)
201
self.vbox.pack_end(entry_hbox)
181
202
self.dialog.add(self.vbox)
183
204
#Connect to signals
284
308
dropped_paths = data.data.split('\n')
287
for dropped_path in dropped_paths:
288
if len(dropped_path) >= 8:
289
if dropped_path[:8] == 'file:///':
290
dropped_path = dropped_path.strip()
292
dropped_path = urllib.unquote(dropped_path)
294
from_file = gio.File(dropped_path)
296
to_file = gio.File(path + '/' + from_file.get_basename())
298
#Make sure we're not just moving the file to the same directory
299
if not from_file.equal(to_file):
300
if from_file.move(to_file):
304
config_fb = self.client.get_string(group, 'fb')
305
open_dir = path.replace(' ', '\ ')
306
os.system('%s %s &' % (config_fb, open_dir))
311
if path == 'trash:///':
312
for dropped_path in dropped_paths:
313
from_file = vfs.File.for_uri(urllib.unquote(dropped_path).strip())
315
self.trash.send_to_trash(from_file)
320
for dropped_path in dropped_paths:
321
if len(dropped_path) >= 8:
322
if dropped_path[:8] == 'file:///':
323
dropped_path = dropped_path.strip()
325
dropped_path = urllib.unquote(dropped_path)
327
from_file = gio.File(dropped_path)
329
to_file = gio.File(path + '/' + from_file.get_basename())
331
#Make sure we're not just moving the file to the same directory
332
if not from_file.equal(to_file):
333
if from_file.move(to_file):
337
config_fb = self.client.get_string(group, 'fb')
338
open_dir = path.replace(' ', '\ ')
339
os.system('%s %s &' % (config_fb, open_dir))
329
362
self.dialog.hide()
331
364
#Certain places, regardless of GIO/not GIO
365
def do_places(self, *args):
333
366
self.liststore.clear()
334
367
self.places_paths = []
335
368
self.places_data = []
337
370
#Get the needed config values
371
self.show_computer = self.client.get_bool(group, 'show_computer')
338
372
self.show_home = self.client.get_int(group, 'show_home')
373
self.show_filesystem = self.client.get_int(group, 'show_filesystem')
339
374
self.show_local = self.client.get_int(group, 'show_local')
340
375
self.show_network = self.client.get_int(group, 'show_network')
341
376
self.show_connect = self.client.get_int(group, 'show_connect')
342
377
self.show_bookmarks = self.client.get_int(group, 'show_bookmarks')
343
self.show_filesystem = self.client.get_int(group, 'show_filesystem')
378
self.show_trash = self.client.get_bool(group, 'show_trash')
380
if self.show_computer and self.nautilus_computer:
381
self.place('computer', _("Computer"), 'exec://nautilus computer:')
346
384
if self.show_home == 2:
462
500
self.do_bookmarks()
464
503
if self.docklet_visible:
465
504
self.update_docklet()
467
506
#A volume was mounted through file-browser-launcher; open the file manager to the path
468
507
def gio_mounted(self, vol, blah):
469
self.launch_fb(None, vol.get_mount().get_root().get_uri())
509
uri = vol.get_mount().get_root().get_uri()
513
self.launch_fb(None, uri)
471
515
#If nautilus-connect-server is installed, offer to start it "Connect to server..."
472
516
def do_ncs(self):
528
572
elif dir == gettext.dgettext('xdg-user-dirs', 'Documents'):
529
self.place(('folder-documents', 'stock_folder'), name, path)
573
self.place(('folder-documents', 'folder'), name, path)
532
576
elif dir == gettext.dgettext('xdg-user-dirs', 'Downloads'):
533
self.place(('folder-downloads', 'stock_folder'), name, path)
577
self.place(('folder-downloads', 'folder'), name, path)
536
580
elif dir == gettext.dgettext('xdg-user-dirs', 'Music'):
537
self.place(('folder-music', 'stock_folder'), name, path)
581
self.place(('folder-music', 'folder'), name, path)
540
584
elif dir == gettext.dgettext('xdg-user-dirs', 'Pictures'):
541
self.place(('folder-pictures', 'stock_folder'), name, path)
585
self.place(('folder-pictures', 'folder'), name, path)
544
588
elif dir == gettext.dgettext('xdg-user-dirs', 'Public'):
545
self.place(('folder-publicshare', 'stock_folder'), name, path)
589
self.place(('folder-publicshare', 'folder'), name, path)
548
592
elif dir == gettext.dgettext('xdg-user-dirs', 'Templates'):
549
self.place(('folder-templates', 'stock_folder'), name, path)
593
self.place(('folder-templates', 'folder'), name, path)
552
596
elif dir == gettext.dgettext('xdg-user-dirs', 'Videos'):
553
self.place(('folder-videos', 'stock_folder'), name, path)
597
self.place(('folder-videos', 'folder'), name, path)
557
self.place('stock_folder', name, path)
601
self.place('folder', name, path)
561
self.place('stock_folder', name, path)
605
self.place('folder', name, path)
563
607
#computer://, trash://, network fs, etc.
568
612
elif type in ['network', 'smb', 'nfs', 'ftp', 'sftp', 'ssh']:
569
613
self.place('network-folder', name, path, _("Network"))
571
elif type == 'trash':
572
#Get whether the trash is empty or not - but first find out if the Trash is in
573
#~/.Trash or ~/.local/share/Trash
576
if os.path.isdir(os.path.expanduser('~/.local/share/Trash/files')):
577
self.trash_path = os.path.expanduser('~/.local/share/Trash/files')
579
self.trash_path = os.path.expanduser('~/.Trash')
581
#Get number of items in trash
582
if len(os.listdir(self.trash_path)) > 0:
583
self.trash_full = True
585
self.trash_full = False
588
#Maybe the trash is in a different location? Just put false
589
self.trash_full = False
592
self.place('user-trash-full', name, path, _("Trash"))
595
self.place('user-trash', name, path, _("Trash"))
597
615
elif type == 'x-nautilus-search':
598
616
self.place('search', name, path, _("Search"))
606
624
#Default to folder
608
self.place('stock_folder', name, path, _("Folder"))
626
self.place('folder', name, path, _("Folder"))
630
count = self.trash.props.file_count
632
self.place('user-trash-full', _("Trash (%d)" % count), 'trash:///')
635
self.place('user-trash', _("Trash"), 'trash:///')
638
self.droppable_places.append('trash:///')
610
640
#Function to show the home folder, mounted drives/partitions, and bookmarks according to config
611
641
#This also refreshes in case a CD was inserted, MP3 player unplugged, bookmark added, etc.
792
821
#If no icon does exists - load default folder icon
794
if 'stock_folder' not in self.icons[size]:
795
self.icons[size]['stock_folder'] = self.theme.load_icon('stock_folder', size, 0)
823
if 'folder' not in self.icons[size]:
824
self.icons[size]['folder'] = self.theme.load_icon('folder', size, 0)
797
icon = self.icons[size]['stock_folder']
826
icon = self.icons[size]['folder']
798
827
self.icons[size][name] = icon
944
973
icon.set_from_pixbuf(self.load_pixbuf(place[0], self.get_size()))
945
974
icon.set_tooltip_text(place[1])
946
975
icon.connect('clicked', self.docklet_icon_clicked, place[3])
976
icon.connect('middle-clicked', self.docklet_icon_middle_clicked, place[3])
947
977
icon.connect('context-menu-popup', self.docklet_icon_menu, place)
949
979
box.set_child_packing(icon, False, True, 0, gtk.PACK_START)
969
999
dropped_paths = data.data.split('\n')
972
for dropped_path in dropped_paths:
973
if len(dropped_path) >= 8:
974
if dropped_path[:8] == 'file:///':
975
dropped_path = dropped_path.strip()
977
dropped_path = urllib.unquote(dropped_path)
979
from_file = gio.File(dropped_path)
981
to_file = gio.File(path + '/' + from_file.get_basename())
983
#Make sure we're not just moving the file to the same directory
984
if not from_file.equal(to_file):
985
if from_file.move(to_file):
989
config_fb = self.client.get_string(group, 'fb')
990
open_dir = path.replace(' ', '\ ')
991
os.system('%s %s &' % (config_fb, open_dir))
1002
if path == 'trash:///':
1003
for dropped_path in dropped_paths:
1004
from_file = vfs.File.for_uri(urllib.unquote(dropped_path).strip())
1006
self.trash.send_to_trash(from_file)
1011
for dropped_path in dropped_paths:
1012
if len(dropped_path) >= 8:
1013
if dropped_path[:8] == 'file:///':
1014
dropped_path = dropped_path.strip()
1016
dropped_path = urllib.unquote(dropped_path)
1018
from_file = gio.File(dropped_path)
1020
to_file = gio.File(path + '/' + from_file.get_basename())
1022
#Make sure we're not just moving the file to the same directory
1023
if not from_file.equal(to_file):
1024
if from_file.move(to_file):
1028
config_fb = self.client.get_string(group, 'fb')
1029
open_dir = path.replace(' ', '\ ')
1030
os.system('%s %s &' % (config_fb, open_dir))
1012
1051
if self.mode == 1:
1013
1052
self.docklet.destroy()
1054
def docklet_icon_middle_clicked(self, icon, uri):
1055
if not os.path.isdir(uri.replace('file:///', '/')):
1058
self.icon_dialog = awn.Dialog(icon, self)
1059
self.icon_dialog.connect('focus-out-event', self.icon_dialog_focusout)
1061
self.icon_entry = gtk.Entry()
1062
self.icon_entry.connect('key-release-event', self.detect_enter)
1063
self.icon_entry.set_text(uri.replace('file:///', '/') + ['/', ''][uri[-1] == '/'])
1065
self.icon_enter = gtk.Button(stock=gtk.STOCK_OPEN)
1066
self.icon_enter.connect('clicked', self.launch_fb, self.icon_entry)
1069
hbox.pack_start(self.icon_entry)
1070
hbox.pack_start(self.icon_enter, False)
1072
self.icon_dialog.add(hbox)
1073
self.icon_dialog.show_all()
1075
self.icon_entry.grab_focus()
1076
self.icon_entry.set_position(-1)
1078
def icon_dialog_focusout(self, widget, event):
1081
gobject.timeout_add_seconds(1, widget.destroy)
1015
1083
def docklet_icon_menu(self, icon, event, place):
1016
1084
menu = self.create_default_menu()
1018
#If the place is ejectable
1020
eject = awn.image_menu_item_new_with_label(_("Eject"))
1021
image = gtk.image_new_from_icon_name('media-eject', gtk.ICON_SIZE_MENU)
1022
eject.set_image(image)
1025
eject.connect('activate', self.docklet_menu_eject, place[4])
1086
if place is not None:
1087
#If the place is ejectable
1089
eject = awn.image_menu_item_new_with_label(_("Eject"))
1090
image = gtk.image_new_from_icon_name('media-eject', gtk.ICON_SIZE_MENU)
1091
eject.set_image(image)
1094
eject.connect('activate', self.docklet_menu_eject, place[4])
1096
elif place[3] == 'trash:///':
1097
empty = gtk.MenuItem(_("Empty Trash"))
1100
if self.trash.props.file_count == 0:
1101
empty.set_sensitive(False)
1103
empty.connect('activate', self.docklet_empty_trash)
1027
1105
prefs = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
1028
1106
prefs.connect('activate', self.open_prefs)
1038
1116
def docklet_menu_eject(self, menu, num):
1039
1117
self.unmount(num)
1119
def docklet_empty_trash(self, menu):
1120
dialog = gtk.Dialog(_("Confirm deletion"), self, gtk.DIALOG_MODAL,
1121
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
1122
dialog.set_icon_name('user-trash-full')
1124
vbox = gtk.VBox(False, 6)
1125
vbox.set_border_width(12)
1127
label1 = gtk.Label()
1128
label1.set_markup(_("<big><b>Are you sure you want to delete\nevery item from the trash?</b></big>"))
1130
label2 = gtk.Label(_("This action cannot be undone."))
1131
label2.set_alignment(0.0, 0.0)
1133
vbox.pack_start(label1, False)
1134
vbox.pack_start(label2, False)
1136
dialog.vbox.pack_start(vbox)
1138
response = dialog.run()
1141
if response == gtk.RESPONSE_ACCEPT:
1041
1147
#The user changed the icon theme
1042
1148
def icon_theme_changed(self, icon_theme):
1043
1149
for d in self.icons.values():
1121
1234
self.update_docklet()
1123
1236
#If the user hits the enter key on the main part OR the number pad
1124
def detect_enter(self, a, event):
1237
def detect_enter(self, widget, event):
1125
1238
if event.keyval == 65293 or event.keyval == 65421:
1126
self.enter.clicked()
1239
if widget == self.entry:
1240
self.enter.clicked()
1242
self.icon_enter.clicked()
1128
1244
#Launces file browser to open "path". If "path" is None: use value from the entry widget
1129
1245
def launch_fb(self, widget, path=None):
1130
1246
self.dialog.hide()
1247
if isinstance(path, gtk.Widget):
1248
path = path.get_text()
1249
self.icon_dialog.hide()
1251
self.docklet.destroy()
1132
1253
path = self.entry.get_text()
1134
1255
#Get the file browser app