changelog: Describe v0.4, closes 1 bug
[dragbox/debian.git] / Dragbox / main.py
blobab43913f6d3fc2aa36abcad69ef34a8881a4887d
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 Entrance point for the dragbox application
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 success = True
56 quit_done = False
57 if nofork:
58 self.dbus_connection = None
59 elif list_names:
60 self.get_dbus_names()
61 quit_done = True
62 else:
63 self.dbus_connection = self.get_dbus_connection()
64 if get:
65 success = self.try_get_data()
66 quit_done = True
67 elif not nofork and self.dbus_connection:
68 self.dbus_send(files, texts)
69 quit_done = True
70 else:
71 self.dbus_service = self.start_dbus_service()
72 # Fork to disconnect from terminal
73 pid = fork()
74 if pid:
75 quit_done = True
76 if quit_done:
77 raise SystemExit(not success)
79 (self.data_controller, self.writer) = self.init_data()
80 self.add_files(None, files)
81 self.add_texts(None, texts)
82 self.run_window()
84 def init_data(self):
85 """
86 Create the data controller and a connected plugins.Writer
88 return a tuple of data controller and writer
89 """
90 from data import DataController
91 data_controller = DataController()
93 quiet = self.cli_flags.get("quiet", True)
94 onexit = self.cli_flags.get("write-on-exit", False)
95 if onexit or not quiet:
96 writer = self.make_writer()
97 if not quiet:
98 data_controller.connect("item-added", writer.print_item)
99 else:
100 writer = None
101 return (data_controller, writer)
103 def make_writer(self):
105 Return a plugins.Writer object
107 Initialized with current null termination and
108 paths/uris settings
110 from plugins import Writer
111 null_term = self.cli_flags.get("null", False)
112 paths = self.cli_flags.get("paths", True)
113 writer = Writer(paths, null_term)
114 return writer
116 def run_window(self):
118 Retrieve the window controller, ready it and run the mainloop
120 from window import WindowController
122 window_title = self.cli_flags.get("window-title", "Dragbox")
123 if self.identifier:
124 window_title += (" (%s)" % self.identifier)
125 self.wc = WindowController(self.data_controller, window_title)
126 self.wc.ready_window()
128 try:
129 from gtk import main
130 main()
131 except KeyboardInterrupt:
132 # catch ctrl-c and exit nicely
133 pass
134 self.prepare_quit()
135 # quit
137 def prepare_quit(self):
138 exit = self.cli_flags.get("write-on-exit")
139 if self.writer and exit:
140 self.writer.print_all(self.data_controller)
142 def read_opts(self, argv):
144 Uses gnu getopt to read command line arguments
146 returns a tuple of (files, texts, options)
148 from getopt import gnu_getopt, GetoptError
149 short_opts = "hf:t:nvpu0axm:"
150 long_opts = [
151 'help',
152 'file=',
153 'text=',
154 'no-fork',
155 'version',
156 'paths',
157 'uris',
158 'null-terminate',
159 'window-title=',
160 'write-on-exit',
161 'write-async',
162 'get',
163 'name=',
164 'list'
166 try:
167 (options, arguments) = gnu_getopt(argv, short_opts, long_opts)
168 except GetoptError, exception:
169 print_error("%s" % exception)
170 self.print_usage(usage_error=True)
171 prefs = {}
172 # store command line flags in `prefs`
173 # quiet: if True, output async
174 # list-names: if True, list running instances
175 for (o,a) in options:
176 if o in ("-h", "--help"):
177 self.print_usage()
178 elif o in ("-v", "--version"):
179 self.print_version()
180 elif o in ("-n", "--no-fork"):
181 prefs["no-fork"] = True
182 prefs["quiet"] = True
183 prefs["write-on-exit"] = True
184 elif o in ("-p", "--paths"):
185 prefs["paths"] = True
186 elif o in ("-u", "--uris"):
187 prefs["paths"] = False
188 elif o in ("-0", "--null-terminate"):
189 prefs["null"] = True
190 elif o in ("--window-title"):
191 prefs["window-title"] = a
192 elif o in ("-x", "--write-on-exit"):
193 # don't write _asyncronously_
194 prefs["quiet"] = True
195 prefs["write-on-exit"] = True
196 elif o in ("-a", "--write-async"):
197 # don't write _asyncronously_
198 prefs["quiet"] = False
199 prefs["write-on-exit"] = False
200 elif o in ("--get"):
201 prefs["get"] = True
202 elif o in ("-m", "--name"):
203 prefs["identifier"] = a
204 elif o in ("--list"):
205 prefs["list-names"] = True
207 # Handle strongly typed items
208 files = []
209 texts = []
210 for (o, a) in options:
211 if o in ("-f", "--file"):
212 path = self.check_file_exists(a, warn=True)
213 if path: files.append(path)
214 elif o in ("-t", "--text"):
215 texts.append(a)
216 # Handle rest
217 # Try if they are files
218 text = ""
219 for item in arguments :
220 path = self.check_file_exists(item, warn=False)
221 if path:
222 files.append(path)
223 else:
224 text = text + " " + item
226 # Take rest as one text block
227 if text: texts.append(text)
228 return (files, texts, prefs)
230 def check_file_exists(self, f, warn=True):
232 Check if a file exists
234 f: a local path
235 warn: If True, print an error message and exit if nonexistant
237 p = path.abspath(f)
238 if path.exists(p):
239 return p
240 if not warn:
241 return None
242 print_error("The file %s does not exist" % f)
243 raise SystemExit(1)
245 def get_dbus_connection(self):
247 Tries to connect to already running instance
249 try:
250 import dbuscontrol
251 except ImportError, info:
252 print_error(info)
253 return None
255 try:
256 dclient = dbuscontrol.Connection(self.identifier)
257 except dbuscontrol.ServiceUnavailable, info:
258 return None
259 return dclient
261 def dbus_send(self, files, texts):
263 Sends files, texts and activates
265 # Send data to running Service
266 iface = self.dbus_connection.get_interface()
267 if files: iface.send_files(files)
268 if texts: iface.send_texts(texts)
269 iface.activate()
270 send_success = True
271 print_debug("Sent data to %s" % iface)
273 def try_get_data(self):
275 Send get_data method to another instance and try to get its data
277 from shelfitem import make_item
278 if not self.dbus_connection:
279 # dbus is not available or the requested name
280 # is simply not there
281 name = self.identifier and self.identifier or "Default"
282 print_error("Shelf %s not found" % name)
283 self.get_dbus_names()
284 return False
285 datas, types = self.dbus_connection.get_interface().get_data()
286 writer = self.make_writer()
287 for data, type in zip(datas, types):
288 writer.print_item(None, make_item(data, type))
289 return True
291 def data_requested(self, service, send_callback, error_callback):
293 data-requested-callback from the dbus service
295 print_debug("Data requested from %s" % service)
296 items = self.data_controller.get_items()
297 datas, types = [], []
298 for item in items:
299 datas.append(item.get_object())
300 types.append(item.get_type())
301 send_callback(datas, types)
303 def start_dbus_service(self):
305 Create a dbus Service object
307 Set up signals for the service
308 return the created service or None
310 import dbuscontrol
311 try:
312 dserver = dbuscontrol.make_service(self.identifier)
313 except Exception, info:
314 from utils import print_debug, print_error
315 print_debug(info)
316 print_error("Failed to use dbus, might not be available")
317 dbus_service = None
318 else:
319 dbus_service = dserver
320 dbus_service.connect("files-received", self.add_files)
321 dbus_service.connect("texts-received", self.add_texts)
322 dbus_service.connect("activate", self.activate_application)
323 dbus_service.connect("data-requested", self.data_requested)
324 return dbus_service
326 def get_dbus_names(self):
327 from dbuscontrol import list_names
328 from utils import print_error
329 try:
330 names = list(list_names())
331 except Exception, info:
332 print_error(info)
333 return
334 print "%d shelves available" % len(names)
335 if names:
336 print " ",
337 print u"\n ".join(names)
339 def add_files(self, service, flist):
341 Adds a list of files to the dragbox
343 flist: a list of absolute paths
345 from shelfitem import FILE_TYPE
347 for f in flist:
348 fileloc = path.abspath(f)
349 self.data_controller.add_item(fileloc, FILE_TYPE)
351 def add_texts(self, service, tlist):
353 Adds a list of text to the dragbox
355 tlist: a list of strings
357 from shelfitem import TEXT_TYPE
359 for text in tlist:
360 self.data_controller.add_item(text, TEXT_TYPE)
362 def activate_application(self, service):
364 Activate and bring to front
366 self.wc.show_window()
368 def print_usage(self, usage_error=False):
369 usage_string = """
370 Usage: dragbox [-naxpu0] [--get | --list] [--name id] [item ...]
372 item add item to dragbox
373 added as file if it is an existing path
375 -t, --text "text" add text
376 -f, --file file add file
378 --get output contents of a running dragbox
379 --list list running dragboxes by identifier
381 -m, --name name set identifier
382 --window-title title set window title
383 -n, --no-fork don't fork (also disables intercommunication)
385 Output options
386 -a, --write-async write items as they are recieved
387 -x, --write-on-exit write items on exit
388 -p, --paths print paths of dragged-in files (default)
389 -u, --uris print uris of dragged-in files
390 -0, --null-terminate separate output items with \\0, not \\n
393 -h, --help display this help
394 -v, --version display version information
396 print usage_string
397 raise SystemExit(usage_error)
399 def print_version(self):
400 import version
401 print "%s %s\n\n%s" % (version.package_name, version.version, version.copyright_info)
402 raise SystemExit