您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

289 行
9.1 KiB

15 年前
15 年前
15 年前
15 年前
15 年前
15 年前
15 年前
15 年前
15 年前
15 年前
15 年前
15 年前
  1. #!/usr/bin/env python2
  2. import subprocess
  3. import cPickle
  4. import os.path
  5. import os
  6. import sys
  7. import pygtk
  8. pygtk.require('2.0')
  9. import gtk
  10. from autojump import open_dic,get_dic_file
  11. defaults={}
  12. actions={}
  13. #directory finding helpers, conforming to the XDG Base Directory Specification
  14. def data_dir():
  15. xdg_data_dir = os.environ.get('XDG_DATA_HOME') or os.path.join(os.environ['HOME'], '.local', 'share')
  16. return os.path.join(xdg_data_dir, 'autojump')
  17. def config_dir():
  18. xdg_config_dir = os.environ.get('XDG_CONFIG_HOME') or os.path.join(os.environ['HOME'], '.config')
  19. return os.path.join(xdg_config_dir, 'autojump')
  20. #decorator truff
  21. def action(validator,name=None):
  22. if name is None:
  23. def wrapper(action):
  24. actions[action.__name__]=(action,validator)
  25. else:
  26. def wrapper(action):
  27. actions[name]=(action,validator)
  28. return wrapper
  29. #validator helper
  30. def has_child_dir(dirname,recursion=0):
  31. def wrapper(path):
  32. k=recursion
  33. ii=""
  34. while k>=0:
  35. if os.path.isdir(os.path.join(path,ii,dirname)): return True
  36. k-=1
  37. ii=os.path.join("..",ii)
  38. return False
  39. return wrapper
  40. def has_child_file(filename):
  41. def wrapper(path):
  42. return os.path.isfile(os.path.join(path,filename))
  43. return wrapper
  44. def load_paths(maxpath=10):
  45. path_dict=open_dic(get_dic_file())
  46. path_dict=path_dict.items()
  47. path_dict.sort(key=lambda x: x[1],reverse=True)
  48. return [path for path,score in path_dict[:maxpath]]
  49. def load_settings_file():
  50. filename = os.path.join(config_dir(), 'jumpapplet_py')
  51. #migration from old location
  52. old_filename = os.path.join(os.environ['HOME'], '.jumpapplet_py')
  53. if not os.path.exists(filename) and os.path.exists(old_filename):
  54. os.rename(old_filename, filename)
  55. print "loading settings"
  56. global defaults
  57. dic_file = filename
  58. try:
  59. aj_file=open(dic_file,'r')
  60. defaults=cPickle.load(aj_file)
  61. aj_file.close()
  62. except IOError:
  63. print "no config file"
  64. pass
  65. if not "terminal" in defaults: defaults["terminal"]="gnome-terminal"
  66. if not "navigator" in defaults: defaults["navigator"]="nautilus"
  67. if not "maxpath" in defaults: defaults["maxpath"]=15
  68. if not "invert" in defaults: defaults["invert"]=False
  69. if not "collapse" in defaults: defaults["collapse"]=True
  70. create_actions()
  71. def save_settings_file(filename=None):
  72. directory = config_dir()
  73. if not filename:
  74. filename = os.path.join(directory, 'jumpapplet_py')
  75. if not os.path.exists(directory):
  76. os.makedirs(directory)
  77. print "saving settings"
  78. dic_file= filename
  79. aj_file=open(dic_file,'w')
  80. cPickle.dump(defaults,aj_file)
  81. aj_file.close()
  82. def get_actions(path):
  83. return [(name,action) for name,(action,validator) in actions.items() if validator(path)]
  84. def popup(sender,button,activation):
  85. paths=load_paths(maxpath=defaults["maxpath"])
  86. if defaults["collapse"]:
  87. def collapse_home(path):
  88. return path.replace(os.path.expanduser('~'),"~")
  89. else:
  90. def collapse_home(path):
  91. return path
  92. menu=gtk.Menu()
  93. if defaults["invert"]:
  94. item=gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT)
  95. item.connect("activate",quit)
  96. menu.append(item)
  97. item=gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES)
  98. item.connect("activate",settings)
  99. menu.append(item)
  100. menu.append(gtk.SeparatorMenuItem())
  101. for path in reversed(paths):
  102. actions=get_actions(path)
  103. if not actions: continue
  104. item=gtk.MenuItem(collapse_home(path),use_underline=False)
  105. submenu=gtk.Menu()
  106. item.set_submenu(submenu)
  107. for name,action in actions:
  108. subitem=gtk.MenuItem(name)
  109. subitem.connect("activate",action,path)
  110. submenu.append(subitem)
  111. menu.append(item)
  112. else:
  113. for path in paths:
  114. actions=get_actions(path)
  115. if not actions: continue
  116. item=gtk.MenuItem(collapse_home(path),use_underline=False)
  117. submenu=gtk.Menu()
  118. item.set_submenu(submenu)
  119. for name,action in actions:
  120. subitem=gtk.MenuItem(name)
  121. subitem.connect("activate",action,path)
  122. submenu.append(subitem)
  123. menu.append(item)
  124. menu.append(gtk.SeparatorMenuItem())
  125. item=gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES)
  126. item.connect("activate",settings)
  127. menu.append(item)
  128. item=gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT)
  129. item.connect("activate",quit)
  130. menu.append(item)
  131. menu.show_all()
  132. menu.popup(None,None,gtk.status_icon_position_menu,button,activation,sender)
  133. def settings(sender):
  134. window=gtk.Dialog("jump applet preferences",None,gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,(gtk.STOCK_SAVE,gtk.RESPONSE_OK,gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL))
  135. window.set_border_width(3)
  136. window.set_resizable(False)
  137. if os.path.isfile("icon.png"): window.set_icon_from_file("icon.png")
  138. elif os.path.isfile("/usr/share/autojump/icon.png"): window.set_icon_from_file("/usr/share/autojump/icon.png")
  139. vbox=gtk.Table(5,2)
  140. vbox.set_row_spacings(3)
  141. window.get_child().add(vbox)
  142. def add_string_setting(name,label,nsettings):
  143. label=gtk.Label(label+' ')
  144. label.set_alignment(1.,.5)
  145. entry=gtk.Entry()
  146. if name in defaults: entry.set_text(defaults[name])
  147. vbox.attach(label,0,1,nsettings,nsettings+1)
  148. vbox.attach(entry,1,2,nsettings,nsettings+1)
  149. return (name,entry)
  150. def add_integer_setting(name,label,nsettings):
  151. label=gtk.Label(label+' ')
  152. label.set_alignment(1.,.5)
  153. entry=gtk.SpinButton()
  154. entry.set_range(5,35)
  155. entry.set_numeric(True)
  156. entry.set_increments(1,5)
  157. entry.set_snap_to_ticks(True)
  158. if name in defaults: entry.set_value(defaults[name])
  159. vbox.attach(label,0,1,nsettings,nsettings+1)
  160. vbox.attach(entry,1,2,nsettings,nsettings+1)
  161. return (name,entry)
  162. def add_bool_setting(name,label,nsettings):
  163. entry=gtk.CheckButton(label=label,use_underline=False)
  164. if name in defaults: entry.set_active(defaults[name])
  165. vbox.attach(entry,0,2,nsettings,nsettings+1)
  166. return (name,entry)
  167. entries=[]
  168. entries.append(add_string_setting("terminal","Terminal program",0))
  169. entries.append(add_string_setting("navigator","Navigator program",1))
  170. entries.append(add_integer_setting("maxpath","Number of directories",2))
  171. entries.append(add_bool_setting("invert","List directories in reverse order",3))
  172. entries.append(add_bool_setting("collapse","Collapse home directory to ~",4))
  173. window.connect("response",save_settings,entries,window)
  174. window.show_all();
  175. def save_settings(sender,response,entries,window):
  176. window.hide_all()
  177. if response!=gtk.RESPONSE_OK: return
  178. global defaults
  179. for name,entry in entries:
  180. try:
  181. defaults[name]=int(entry.get_text())
  182. except (ValueError,AttributeError):
  183. try:
  184. defaults[name]=entry.get_active()
  185. except AttributeError:
  186. defaults[name]=entry.get_text()
  187. save_settings_file()
  188. create_actions()
  189. def init():
  190. load_settings_file()
  191. if os.path.isfile("icon.png"): icon=gtk.status_icon_new_from_file("icon.png")
  192. elif os.path.isfile("/usr/share/autojump/icon.png"): icon=gtk.status_icon_new_from_file("/usr/share/autojump/icon.png")
  193. else: icon=gtk.status_icon_new_from_icon_name("help")
  194. icon.set_visible(True)
  195. icon.connect("popup-menu",popup)
  196. def quit(sender):
  197. gtk.main_quit()
  198. ######################################################
  199. #insert other actions here using the action decorator#
  200. ######################################################
  201. def create_actions():
  202. global actions
  203. actions={}
  204. @action(has_child_dir(".git",recursion=3))
  205. def gitk(sender,path):
  206. if not os.fork():
  207. os.chdir(path)
  208. subprocess.Popen(['gitk']).wait()
  209. sys.exit()
  210. @action(has_child_file("CMakeCache.txt"),"configure")
  211. def cmake(sender,path):
  212. if not os.fork():
  213. os.chdir(path)
  214. subprocess.Popen(['cmake-gui','.']).wait()
  215. sys.exit()
  216. @action(os.path.isdir)
  217. def terminal(sender,path):
  218. print "launch terminal '%s'" % defaults["terminal"]
  219. if not os.fork():
  220. try:
  221. if defaults["terminal"]=="konsole":
  222. subprocess.Popen([defaults["terminal"],"--workdir=%s"%path]).wait()
  223. else:
  224. os.chdir(path)
  225. subprocess.Popen([defaults["terminal"]]).wait()
  226. except OSError:
  227. pass
  228. sys.exit()
  229. @action(os.path.isdir)
  230. def navigator(sender,path):
  231. print "launch navigator '%s'" % defaults["navigator"]
  232. if not os.fork():
  233. try:
  234. subprocess.Popen([defaults["navigator"],path]).wait()
  235. except OSError:
  236. pass
  237. sys.exit()
  238. if __name__=='__main__':
  239. init()
  240. gtk.main()