Implement multi-dragbox
[dragbox.git] / Dragbox / main.py
blobba6c84b61dfc3cbcacf95ade2d45807363e64813
1 # Copyright (C) 2006, 2007 Ulrik Sverdrup
3 # dragbox -- Commandline drag-and-drop tool for GNOME
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
18 # USA
20 from sys import exit, argv
21 from os import path, fork
23 from utils import print_error, print_debug
25 class dragbox(object):
26 """
27 Dragbox application object
28 """
29 def __init__(self):
30 """
31 This class runs one instance of dragbox
33 + Read commandline
34 + Try dbus connection
35 + Add items
36 + Run
37 """
38 # Read commandline switches
39 (files, texts, cli_flags) = self.read_opts(argv[1:])
41 # Read from stdin
42 from sys import stdin
43 if not stdin.isatty():
44 pipe_text = stdin.read()
45 if pipe_text:
46 texts += [pipe_text]
48 self.cli_flags = cli_flags
50 get = self.cli_flags.get("get")
51 list_names = self.cli_flags.get("list-names")
52 nofork = self.cli_flags.get("no-fork")
53 self.identifier = self.cli_flags.get("identifier")
55 if nofork:
56 self.dbus_connection = None
57 elif list_names:
58 self.get_dbus_names()
59 raise SystemExit
60 else:
61 self.dbus_connection = self.get_dbus_connection()
62 if get:
63 success = self.try_get_data()
64 raise SystemExit(not success)
65 elif not nofork and self.dbus_connection:
66 self.dbus_send(files, texts)
67 # Done, sent to other running instance
68 raise SystemExit
69 else:
70 self.start_dbus_service()
71 # Fork!
72 pid = fork()
73 if pid:
74 raise SystemExit
76 (self.data_controller, self.writer) = self.init_data()
77 self.add_files(None, files)
78 self.add_texts(None, texts)
79 self.run_window()
81 def init_data(self):
82 from data import DataController
83 data_controller = DataController()
85 quiet = self.cli_flags.get("quiet", True)
86 onexit = self.cli_flags.get("write-on-exit", False)
87 if onexit or not quiet:
88 writer = self.make_writer()
89 if not quiet:
90 data_controller.connect("item-added", writer.print_item)
91 else:
92 writer = None
93 return (data_controller, writer)
95 def make_writer(self):
96 """
97 Return a plugins.Writer object
99 Initialized with current null termination and
100 paths/uris settings
102 from plugins import Writer
103 null_term = self.cli_flags.get("null", False)
104 paths = self.cli_flags.get("paths", True)
105 writer = Writer(paths, null_term)
106 return writer
108 def run_window(self):
109 from window import WindowController
111 window_title = self.cli_flags.get("window-title", "Dragbox")
112 if self.identifier:
113 window_title += (" (%s)" % self.identifier)
114 self.wc = WindowController(self.data_controller, window_title)
115 self.wc.ready_window()
117 try:
118 from gtk import main
119 main()
120 except KeyboardInterrupt:
121 # catch ctrl-c and exit nicely
122 pass
123 self.prepare_quit()
124 # quit
126 def prepare_quit(self):
127 exit = self.cli_flags.get("write-on-exit")
128 if self.writer and exit:
129 self.writer.print_all(self.data_controller)
131 def read_opts(self, argv):
133 Uses gnu getopt to read command line arguments
135 returns a tuple of (files, texts, options)
137 from getopt import gnu_getopt, GetoptError
138 short_opts = "hf:t:nvpu0ax"
139 long_opts = [
140 'help',
141 'file=',
142 'text=',
143 'no-fork',
144 'version',
145 'paths',
146 'uris',
147 'null-terminate',
148 'window-title=',
149 'write-on-exit',
150 'write-async',
151 'get',
152 'name=',
153 'list'
155 try:
156 (options, arguments) = gnu_getopt(argv, short_opts, long_opts)
157 except GetoptError, exception:
158 print_error("Sorry, %s" % exception)
159 self.print_usage(usage_error=True)
160 prefs = {}
161 # store command line flags in `prefs`
162 # quiet: if True, output async
163 # list-names: if True, list running instances
164 for (o,a) in options:
165 if o in ("-h", "--help"):
166 self.print_usage()
167 elif o in ("-v", "--version"):
168 self.print_version()
169 elif o in ("-n", "--no-fork"):
170 prefs["no-fork"] = True
171 prefs["quiet"] = True
172 prefs["write-on-exit"] = True
173 elif o in ("-p", "--paths"):
174 prefs["paths"] = True
175 elif o in ("-u", "--uris"):
176 prefs["paths"] = False
177 elif o in ("-0", "--null-terminate"):
178 prefs["null"] = True
179 elif o in ("--window-title"):
180 prefs["window-title"] = a
181 elif o in ("-x", "--write-on-exit"):
182 # don't write _asyncronously_
183 prefs["quiet"] = True
184 prefs["write-on-exit"] = True
185 elif o in ("-a", "--write-async"):
186 # don't write _asyncronously_
187 prefs["quiet"] = False
188 prefs["write-on-exit"] = False
189 elif o in ("--get"):
190 prefs["get"] = True
191 elif o in ("--name"):
192 prefs["identifier"] = a
193 elif o in ("--list"):
194 prefs["list-names"] = True
196 # Handle strongly typed items
197 files = []
198 texts = []
199 for (o, a) in options:
200 if o in ("-f", "--file"):
201 path = self.check_file_exists(a, warn=True)
202 if path: files.append(path)
203 elif o in ("-t", "--text"):
204 texts.append(a)
205 # Handle rest
206 # Try if they are files
207 text = ""
208 for item in arguments :
209 path = self.check_file_exists(item, warn=False)
210 if path:
211 files.append(path)
212 else:
213 text = text + " " + item
215 # Take rest as one text block
216 if text: texts.append(text)
217 return (files, texts, prefs)
219 def check_file_exists(self, f, warn=True):
221 Check if a file exists
223 f: a local path
224 warn: If True, print an error message and exit if nonexistant
226 p = path.abspath(f)
227 if path.exists(p):
228 return p
229 if not warn:
230 return None
231 print_error("The file %s does not exist" % f)
232 raise SystemExit(1)
234 def get_dbus_connection(self):
236 Tries to connect ot already running instance
238 from version import dbus_enabled
239 if dbus_enabled != "yes":
240 return None #return with no success
242 try:
243 import dbuscontrol
244 except ImportError, info:
245 print_error(info)
246 return None
248 try:
249 dclient = dbuscontrol.Connection(self.identifier)
250 except dbuscontrol.ServiceUnavailable, info:
251 return None
252 return dclient
254 def dbus_send(self, files, texts):
256 Sends files, texts and activates
258 # Send data to running Service
259 iface = self.dbus_connection.get_interface()
260 if files: iface.send_files(files)
261 if texts: iface.send_texts(texts)
262 iface.activate()
263 send_success = True
264 print_debug("Sent data to %s" % iface)
266 def try_get_data(self):
268 Send get_data method to another instance and try to get its data
270 from shelfitem import make_item
271 if not self.dbus_connection:
272 name = self.identifier and self.identifier or "Default"
273 print_error("Shelf %s not found" % name)
274 return False
275 datas, types = self.dbus_connection.get_interface().get_data()
276 writer = self.make_writer()
277 for data, type in zip(datas, types):
278 writer.print_item(None, make_item(data, type))
279 return True
281 def data_requested(self, service, send_callback, error_callback):
282 print_debug("Data requested from %s" % service)
283 items = self.data_controller.get_items()
284 datas, types = [], []
285 for item in items:
286 datas.append(item.get_object())
287 types.append(item.get_type())
288 send_callback(datas, types)
289 #error_callback(Exception())
291 def start_dbus_service(self):
293 Create a dbus Service object
295 Set up signals for the service
297 import dbuscontrol
298 try:
299 dserver = dbuscontrol.make_service(self.identifier)
300 except Exception, info:
301 from utils import print_debug, print_error
302 print_debug(info)
303 print_error("Failed to use dbus, might not be available")
304 else:
305 self.dbus_service = dserver
306 self.dbus_service.connect("files-received", self.add_files)
307 self.dbus_service.connect("texts-received", self.add_texts)
308 self.dbus_service.connect("activate", self.activate_application)
309 self.dbus_service.connect("data-requested", self.data_requested)
311 def get_dbus_names(self):
312 from dbuscontrol import list_names
313 names = list(list_names())
314 print "%d shelves available" % len(names)
315 if names:
316 print " ",
317 print u"\n ".join(names)
319 def add_files(self, service, flist):
321 Adds a list of files to the dragbox
323 flist: a list of absolute paths
325 from shelfitem import FILE_TYPE
327 for f in flist:
328 fileloc = path.abspath(f)
329 self.data_controller.add_item(fileloc, FILE_TYPE)
331 def add_texts(self, service, tlist):
333 Adds a list of text to the dragbox
335 tlist: a list of strings
337 from shelfitem import TEXT_TYPE
339 for text in tlist:
340 self.data_controller.add_item(text, TEXT_TYPE)
342 def activate_application(self, service):
344 Activate and bring to front
346 self.wc.show_window()
348 def print_usage(self, usage_error=False):
349 print """
350 Usage: %s [-nq] item [...]
352 item add item to dragbox
353 added as file if it is a valid path
355 -t, --text "text" add text
356 -f, --file file add file
358 --window-title name set window title to name
360 -n, --no-fork don't fork (also disables use of dbus)
362 The options below only apply with --no-fork:
363 -q, --quiet don't print dragged-in items
364 -p, --paths print paths of dragged-in files (default)
365 -u, --uris print uris of dragged-in files
366 -0, --null-terminate separate items' output with \\0, not \\n
367 -x, --write-on-exit only write contents on exit
370 -h, --help display this help
371 -v, --version display version information
372 """ % "dragbox"
373 raise SystemExit(usage_error)
375 def print_version(self):
376 import version
377 print "%s %s\n\n%s" % (version.package_name, version.version, version.copyright_info)
378 raise SystemExit