1 import rox, os, gobject, gtk, struct
2 from rox import processes
3
4 import traylib
5 from traylib import *
6 from pixbuf_helper import *
7
8 _ = rox.i18n.translation(os.path.join(os.path.dirname(
9 os.path.dirname(
10 os.path.dirname(traylib.__file__))),
11 'Messages'))
12
14 global dir_icon, home_icon
15 home_icon = None
16 dir_icon = None
17 for icon_name in ['mime-inode:directory', 'folder', 'gnome-fs-directory']:
18 try:
19 dir_icon = icon_theme.load_icon(icon_name, 32, 0)
20 break
21 except:
22 pass
23 for icon_name in ['user-home', 'gnome-fs-home']:
24 try:
25 home_icon = icon_theme.load_icon(icon_name, 32, 0)
26 break
27 except:
28 pass
29
30 home_icon = None
31 dir_icon = None
32 icon_theme.connect("changed", _load_icons)
33 _load_icons(icon_theme)
34
35 -def _kill(menu_item, pids, name):
36 if rox.confirm(_("Really force %s to quit?") % name,
37 gtk.STOCK_QUIT, _("Force quit")):
38 for pid in pids:
39 rox.processes.PipeThroughCommand(('kill', '-KILL', str(pid)),
40 None, None).wait()
41
43 name = window.get_name()
44 if (window.get_class_group().get_name() != 'ROX-Filer'
45 or not (name.startswith('/') or name.startswith('~'))):
46 return ''
47 for i in range(1-len(name), 0):
48 if name[-i] == '(' or name[-i] == '+':
49 name = name[:-(i+1)]
50 break
51 return name
52
54 """
55 A menu that shows actions for a C{wnck.Window}.
56 """
57
63 """
64 Creates a new C{WindowActionMenu}.
65
66 @param has_kill: If C{True}, the menu contains an entry to kill the
67 process the window belongs to.
68 @param path: If not C{None}, indicates that the window is a filemanager
69 window showing the directory C{path}.
70 @param parent: The L{WindowMenu} showing all directories.
71 """
72 assert isinstance(window, wnck.Window)
73 gtk.Menu.__init__(self)
74
75 self.__window = window
76 if path:
77 self.__path = os.path.expanduser(path)
78 else:
79 self.__path = None
80 self.__parent = parent
81
82 item = gtk.ImageMenuItem(_("Activate"))
83 item.get_image().set_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
84 item.connect("activate", self.__winaction2, wnck.Window.activate)
85 self.append(item)
86
87 self.append(gtk.SeparatorMenuItem())
88
89 actions = window.get_actions()
90
91 if window.is_maximized():
92 item = gtk.ImageMenuItem(_("Unmaximize"))
93 item.get_image().set_from_stock(gtk.STOCK_ZOOM_OUT,
94 gtk.ICON_SIZE_MENU)
95 item.connect("activate", self.__winaction, wnck.Window.unmaximize)
96 item.set_sensitive(wnck.WINDOW_ACTION_UNMAXIMIZE & actions)
97 else:
98 item = gtk.ImageMenuItem(_("Maximize"))
99 item.get_image().set_from_stock(gtk.STOCK_ZOOM_100,
100 gtk.ICON_SIZE_MENU)
101 item.connect("activate", self.__winaction, wnck.Window.maximize)
102 item.set_sensitive(wnck.WINDOW_ACTION_MAXIMIZE & actions)
103 self.append(item)
104
105 if window.is_minimized():
106 item = gtk.ImageMenuItem(_("Show"))
107 item.get_image().set_from_stock(gtk.STOCK_REDO, gtk.ICON_SIZE_MENU)
108 item.connect("activate", self.__winaction2, wnck.Window.unminimize)
109 else:
110 item = gtk.ImageMenuItem(_("Hide"))
111 item.get_image().set_from_stock(gtk.STOCK_UNDO, gtk.ICON_SIZE_MENU)
112 item.connect("activate", self.__minimize)
113 self.append(item)
114
115 if window.is_shaded():
116 item = gtk.ImageMenuItem(_("Unshade"))
117 item.get_image().set_from_stock(gtk.STOCK_GOTO_BOTTOM,
118 gtk.ICON_SIZE_MENU)
119 item.connect("activate", self.__winaction, wnck.Window.unshade)
120 item.set_sensitive(wnck.WINDOW_ACTION_UNSHADE & actions)
121 else:
122 item = gtk.ImageMenuItem(_("Shade"))
123 item.get_image().set_from_stock(gtk.STOCK_GOTO_TOP,
124 gtk.ICON_SIZE_MENU)
125 item.connect("activate", self.__winaction, wnck.Window.shade)
126 item.set_sensitive(wnck.WINDOW_ACTION_SHADE & actions)
127 self.append(item)
128
129 self.append(gtk.SeparatorMenuItem())
130
131 if window.is_pinned() or window.is_sticky():
132 item = gtk.ImageMenuItem(_("Only on this workspace"))
133 item.get_image().set_from_stock(gtk.STOCK_REMOVE,
134 gtk.ICON_SIZE_MENU)
135 item.connect("activate", self.__winaction, wnck.Window.unstick)
136 item.connect("activate", self.__winaction, wnck.Window.unpin)
137 item.set_sensitive(wnck.WINDOW_ACTION_UNSTICK & actions)
138 else:
139 item = gtk.ImageMenuItem(_("Always on visible workspace"))
140 item.get_image().set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
141 item.connect("activate", self.__winaction, wnck.Window.stick)
142 item.connect("activate", self.__winaction, wnck.Window.pin)
143 item.set_sensitive(wnck.WINDOW_ACTION_STICK & actions)
144 self.append(item)
145
146 if screen.get_workspace_count() > 1:
147 item = gtk.ImageMenuItem(_("Move to workspace"))
148 item.get_image().set_from_stock(gtk.STOCK_JUMP_TO,
149 gtk.ICON_SIZE_MENU)
150 self.append(item)
151 submenu = gtk.Menu()
152 item.set_submenu(submenu)
153 for i in range(0, screen.get_workspace_count()):
154 workspace = screen.get_workspace(i)
155 item = gtk.MenuItem(workspace.get_name())
156 if workspace != window.get_workspace():
157 item.connect("activate",
158 self.__move_to_workspace,
159 workspace)
160 else:
161 item.set_sensitive(False)
162 submenu.append(item)
163
164 if has_kill:
165 self.append(gtk.SeparatorMenuItem())
166 app = window.get_application()
167 pid = app.get_pid()
168 item = gtk.ImageMenuItem(_("Force quit"))
169 item.get_image().set_from_stock(gtk.STOCK_QUIT, gtk.ICON_SIZE_MENU)
170 self.append(item)
171 item.connect("activate", _kill, [pid], app.get_name())
172
173 self.append(gtk.SeparatorMenuItem())
174
175 if self.__parent and self.__path and os.path.isdir(self.__path):
176 item = gtk.ImageMenuItem(_("Close subdirectories"))
177 item.get_image().set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
178 item.connect("activate", self.__close_subdirs)
179 self.append(item)
180
181 item = gtk.ImageMenuItem(gtk.STOCK_CLOSE)
182 item.connect("activate", self.__winaction2, wnck.Window.close)
183 self.append(item)
184
186 path = self.__path + os.sep
187 windows = []
188 for window in self.__parent.get_windows():
189 window_path = os.path.expanduser(_get_filer_window_path(window))
190 if window_path and window_path.startswith(path):
191 windows.append(window)
192 if not windows:
193 rox.info(_("There are no windows showing subdirectories of %s")
194 % self.__path)
195 n_windows = len(windows)
196 if n_windows > 1:
197 if not rox.confirm(_("Close all %d subdirectories of %s?")
198 % (n_windows, self.__path), gtk.STOCK_CLOSE):
199 return
200 for window in windows:
201 window.close(gtk.get_current_event_time())
202
205
207 func(self.__window, gtk.get_current_event_time())
208
210 self.__window.move_to_workspace(workspace)
211
213 self.__window.unshade()
214 self.__window.minimize()
215
217 """
218 A menu item representing a window.
219 """
220
222 """
223 Creates a new WindowMenuItem.
224
225 @param window: The C{wnck.Window} the menu item represents.
226 """
227 pixbuf = None
228 self.__window = window
229 self.__path = _get_filer_window_path(window)
230 if self.__path:
231 name = self.__path
232 if root:
233 root_dirname = os.path.dirname(root)
234 if root_dirname == '/':
235 l = 1
236 else:
237 l = len(root_dirname) + 1
238 if name != '/':
239 name = os.path.expanduser(name)[l:]
240 name = name.replace(os.path.expanduser('~'), '~')
241 else:
242 name = window.get_name()
243 size = 22
244 if window.is_minimized():
245 name = "[ " + name + " ]"
246 if window.is_shaded():
247 name = "= " + name + " ="
248 if window.needs_attention():
249 name = "!! " + name + " !!"
250 if window == screen.get_active_window():
251 size = 32
252 gtk.ImageMenuItem.__init__(self, name.replace('_', '__'))
253 if self.__path:
254 icon_path = os.path.expanduser(os.path.join(self.__path,
255 '.DirIcon'))
256 if os.access(icon_path, os.F_OK):
257 pixbuf = gtk.gdk.pixbuf_new_from_file(icon_path)
258 elif self.__path != root:
259 if os.path.expanduser(self.__path) == os.path.expanduser('~'):
260 pixbuf = home_icon
261 else:
262 pixbuf = dir_icon
263 elif self.__path == root and root_icon:
264 pixbuf = root_icon
265 if not pixbuf:
266 if icon:
267 pixbuf = icon
268 else:
269 pixbuf = window.get_icon()
270 pixbuf = scale_pixbuf_to_size(pixbuf, size)
271 self.get_image().set_from_pixbuf(pixbuf)
272
273 self.drag_source_set(gtk.gdk.BUTTON1_MASK,
274 [("application/x-wnck-window-id",
275 0,
276 TARGET_WNCK_WINDOW_ID)],
277 gtk.gdk.ACTION_MOVE)
278 self.connect("drag-begin", self.__drag_begin, pixbuf)
279 self.connect("drag-data-get", self.__drag_data_get)
280
282 assert widget == self
283 context.set_icon_pixbuf(pixbuf, 0,0)
284
286 xid = self.__window.get_xid()
287 data.set(data.target, 8, apply(struct.pack, ['1i', xid]))
288
291
292
293 TYPE_SELECT = 0
294
295
296 TYPE_OPTIONS = 1
297
299 """
300 The menu for a list of windows.
301 """
302
305 """
306 Creates a new WindowMenu.
307
308 @param windows: A list of C{wnck.Window}s.
309 @param type: The type of the menu.\n
310 C{TYPE_SELECT}: A menu from which a window can be selected to be
311 activated.\n
312 C{TYPE_OPTIONS}: A menu which shows a submenu for each window,
313 "hide all" or "show all" and "close all" menu items. If
314 C{has_kill} is C{True}, it also contains a "force quit" menu
315 entry.
316 @param root: If not C{None}, indicates that the windows are filemanager
317 windows and the path prefix "root" should not be omitted.
318 @param root_icon: The icon to show for the root menu entry. This is the
319 menu entry showing C{root}.
320 @param has_kill: If C{True} and C{type==TYPE_OPTIONS}, the menu contains
321 a "kill" menu entry which kills the process the windows belong to.
322 If the windows belong to different processes, each submenu has its
323 own "kill" menu entry.
324 """
325 assert type in (TYPE_SELECT, TYPE_OPTIONS)
326 gtk.Menu.__init__(self)
327 self.__group_name = group_name
328 self.__type = type
329 self.__windows = windows
330 windows.sort(key=wnck.Window.get_name)
331 time = gtk.get_current_event_time()
332 self.__active_window = screen.get_active_window()
333 has_minimized_windows = False
334 has_unminimized_windows = False
335 same_app = True
336 self.__pids = []
337 for window in windows:
338 pid = window.get_application().get_pid()
339 if pid in self.__pids:
340 continue
341 self.__pids.append(pid)
342 for window in windows:
343 if window.is_minimized():
344 has_minimized_windows = True
345 else:
346 has_unminimized_windows = True
347 item = WindowMenuItem(window, icon, root, root_icon)
348 if type == TYPE_OPTIONS:
349 item.set_submenu(WindowActionMenu(window,
350 has_kill and len(self.__pids) > 1,
351 item.get_path(), self))
352 else:
353 item.connect("scroll-event", self.__scroll, window)
354 item.connect("activate", self.__window_selected, window, time)
355 self.append(item)
356 if self.__type == TYPE_OPTIONS:
357 self.append(gtk.SeparatorMenuItem())
358 item = gtk.ImageMenuItem(_("Show all"))
359 item.get_image().set_from_stock(gtk.STOCK_REDO, gtk.ICON_SIZE_MENU)
360 item.connect("activate", self.__restore_all)
361 item.set_sensitive(has_minimized_windows)
362 self.append(item)
363 item = gtk.ImageMenuItem(_("Hide all"))
364 item.get_image().set_from_stock(gtk.STOCK_UNDO, gtk.ICON_SIZE_MENU)
365 item.connect("activate", self.__minimize_all)
366 item.set_sensitive(has_unminimized_windows)
367 self.append(item)
368 if has_kill:
369 self.append(gtk.SeparatorMenuItem())
370 item = gtk.ImageMenuItem(_("Force quit"))
371 item.get_image().set_from_stock(gtk.STOCK_QUIT,
372 gtk.ICON_SIZE_MENU)
373 item.connect("activate", _kill, self.__pids, group_name)
374 self.append(item)
375 self.append(gtk.SeparatorMenuItem())
376 item = gtk.ImageMenuItem(_("Close all"))
377 item.get_image().set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
378 item.connect("activate", self.__close_all)
379 self.append(item)
380
382 """
383 @return: The windows belonging to the C{WindowMenu}.
384 """
385 return self.__windows
386
388 for window in self.__windows:
389 window.minimize()
390
392 for window in self.__windows:
393 if window.is_minimized():
394 window.unminimize(gtk.get_current_event_time())
395
397 n_windows = len(self.__windows)
398 if n_windows == 0:
399 rox.info(_("%s hasn't got any open windows anymore.")
400 % self.__group_name)
401 return
402 if not rox.confirm(_("Close all %d windows of %s?")
403 % (n_windows, self.__group_name),
404 gtk.STOCK_CLOSE, _("Close all")):
405 return
406 for window in self.__windows:
407 window.close(gtk.get_current_event_time())
408
410 window.activate(time)
411
424