Style tablist text & update window title.
[uzbl-mobile] / examples / data / uzbl / scripts / uzbl_tabbed.py
1 #!/usr/bin/python
2
3 # Uzbl tabbing wrapper using a fifo socket interface
4 # Copywrite (c) 2009, Tom Adams <tom@holizz.com>
5 # Copywrite (c) 2009, quigybo <?>
6 # Copywrite (c) 2009, Mason Larobina <mason.larobina@gmail.com>
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21
22 # Author(s): 
23 #   Tom Adams <tom@holizz.com>
24 #       Wrote the original uzbl_tabbed.py as a proof of concept.
25 #
26 #   quigybo <?>
27 #       Made signifigant headway on the uzbl_tabbing.py script on the 
28 #       uzbl wiki <http://www.uzbl.org/wiki/uzbl_tabbed> 
29 #
30 #   Mason Larobina <mason.larobina@gmail.com>
31 #       Rewrite of the uzbl_tabbing.py script to use a fifo socket interface
32 #       and inherit configuration options from the user's uzbl config.
33 #
34 # Contributor(s):
35 #   (None yet)
36
37
38 # Issues: 
39 #   - status_background colour is not honoured (reverts to gtk default).
40 #   - new windows are not caught and opened in a new tab.
41 #   - need an easier way to read a uzbl instances window title instead of 
42 #     spawning a shell to spawn uzblctrl to communicate to the uzbl 
43 #     instance via socket to dump the window title to then pipe it to 
44 #     the tabbing managers fifo socket.
45 #   - probably missing some os.path.expandvars somewhere. 
46
47
48 # Todo: 
49 #   - add command line options to use a different session file, not use a
50 #     session file and or open a uri on starup. 
51 #   - ellipsize individual tab titles when the tab-list becomes over-crowded
52 #   - add "<" & ">" arrows to tablist to indicate that only a subset of the 
53 #     currently open tabs are being displayed on the tablist.
54 #   - probably missing some os.path.expandvars somewhere and other 
55 #     user-friendly.. things, this is still a very early version. 
56 #   - fix status_background issues & style tablist. 
57 #   - add the small tab-list display when both gtk tabs and text vim-like
58 #     tablist are hidden (I.e. [ 1 2 3 4 5 ])
59 #   - check spelling.
60
61
62 import pygtk
63 import gtk
64 import subprocess
65 import os
66 import re
67 import time
68 import getopt
69 import pango
70 import select
71 import sys
72 import gobject
73
74 pygtk.require('2.0')
75
76 def error(msg):
77     sys.stderr.write("%s\n"%msg)
78
79 if 'XDG_DATA_HOME' in os.environ.keys() and os.environ['XDG_DATA_HOME']:
80     data_dir = os.path.join(os.environ['XDG_DATA_HOME'], 'uzbl/')
81
82 else:
83     data_dir = os.path.join(os.environ['HOME'], '.local/share/uzbl/')
84
85 # === Default Configuration ====================================================
86
87 # Location of your uzbl configuration file.
88 uzbl_config = os.path.join(os.environ['HOME'],'.config/uzbl/config')
89
90 # All of these settings can be inherited from your uzbl config file.
91 config = {'show_tabs': True,
92   'show_gtk_tabs': False,
93   'switch_to_new_tabs': True,
94   'save_session': True,
95   'fifo_dir': '/tmp',
96   'icon_path': os.path.join(data_dir, 'uzbl.png'),
97   'session_file': os.path.join(data_dir, 'session'),
98   'tab_colours': 'foreground = "#999"',
99   'tab_text_colours': 'foreground = "#444"',
100   'selected_tab': 'foreground = "#aaa" background="#303030"',
101   'selected_tab_text': 'foreground = "green"',
102   'window_size': "800,800",
103   'monospace_size': 10, 
104   'bind_new_tab': 'gn',
105   'bind_tab_from_clipboard': 'gY', 
106   'bind_close_tab': 'gC',
107   'bind_next_tab': 'gt',
108   'bind_prev_tab': 'gT',
109   'bind_goto_tab': 'gi_',
110   'bind_goto_first': 'g<',
111   'bind_goto_last':'g>'}
112
113 # === End Configuration =======================================================
114
115 def readconfig(uzbl_config, config):
116     '''Loads relevant config from the users uzbl config file into the global
117     config dictionary.'''
118
119     if not os.path.exists(uzbl_config):
120         error("Unable to load config %r" % uzbl_config)
121         return None
122     
123     # Define parsing regular expressions
124     isint = re.compile("^[0-9]+$").match
125     findsets = re.compile("^set\s+([^\=]+)\s*\=\s*(.+)$",\
126       re.MULTILINE).findall
127
128     h = open(os.path.expandvars(uzbl_config), 'r')
129     rawconfig = h.read()
130     h.close()
131     
132     for (key, value) in findsets(rawconfig):
133         key = key.strip()
134         if key not in config.keys(): continue
135         if isint(value): value = int(value)
136         config[key] = value
137
138
139 def rmkdir(path):
140     '''Recursively make directories.
141     I.e. `mkdir -p /some/nonexistant/path/`'''
142
143     path, sep = os.path.realpath(path), os.path.sep
144     dirs = path.split(sep)
145     for i in range(2,len(dirs)+1):
146         dir = os.path.join(sep,sep.join(dirs[:i]))
147         if not os.path.exists(dir):
148             os.mkdir(dir)
149
150
151 def counter():
152     '''To infinity and beyond!'''
153
154     i = 0
155     while True:
156         i += 1
157         yield i
158
159
160 class UzblTabbed:
161     '''A tabbed version of uzbl using gtk.Notebook'''
162
163     class UzblInstance:
164         '''Uzbl instance meta-data/meta-action object.'''
165
166         def __init__(self, parent, socket, fifo, pid, url='', switch=True):
167             self.parent = parent
168             self.socket = socket # the gtk socket
169             self.fifo = fifo
170             self.pid = pid
171             self.title = "New tab"
172             self.url = url
173             self.timers = {}
174             self._lastprobe = 0
175             self._switch_on_config = switch
176             self._outgoing = []
177             self._configured = False
178
179             # When notebook tab deleted the kill switch is raised.
180             self._kill = False
181             
182             # Queue binds for uzbl child
183             self.parent.config_uzbl(self)
184
185
186         def flush(self, timer_call=False):
187             '''Flush messages from the queue.'''
188             
189             if self._kill:
190                 error("Flush called on dead page.")
191                 return False
192
193             if os.path.exists(self.fifo):
194                 h = open(self.fifo, 'w')
195                 while len(self._outgoing):
196                     msg = self._outgoing.pop(0)
197                     h.write("%s\n" % msg)
198                 h.close()
199
200             elif not timer_call and self._configured:
201                 # TODO: I dont know what to do here. A previously thought
202                 # alright uzbl client fifo socket has now gone missing.
203                 # I think this should be fatal (at least for the page in
204                 # question). I'll wait until this error appears in the wild. 
205                 error("Error: fifo %r lost in action." % self.fifo)
206             
207             if not len(self._outgoing) and timer_call:
208                 self._configured = True
209
210                 if timer_call in self.timers.keys():
211                     gobject.source_remove(self.timers[timer_call])
212                     del self.timers[timer_call]
213
214                 if self._switch_on_config:
215                     notebook = list(self.parent.notebook)
216                     try:
217                         tabid = notebook.index(self.socket)
218                         self.parent.goto_tab(tabid)
219
220                     except ValueError:
221                         pass
222                 
223             return len(self._outgoing)
224
225
226         def probe(self):
227             '''Probes the client for information about its self.'''
228             
229             # Ugly way of getting the socket path. Screwed if fifo is in any
230             # other part of the fifo socket path.
231
232             socket = 'socket'.join(self.fifo.split('fifo'))
233             
234             # I feel so dirty
235             subcmd = 'print title %s @<document.title>@' % self.pid
236             cmd = 'uzblctrl -s "%s" -c "%s" > "%s" &' % (socket, subcmd, \
237               self.parent.fifo_socket)
238
239             subprocess.Popen([cmd], shell=True)
240
241             self._lastprobe = time.time()
242             
243         
244         def send(self, msg):
245             '''Child fifo write function.'''
246
247             self._outgoing.append(msg)
248             # Flush messages from the queue if able.
249             return self.flush()
250
251
252     def __init__(self):
253         '''Create tablist, window and notebook.'''
254         
255         self.pages = {}
256         self._pidcounter = counter()
257         self.next_pid = self._pidcounter.next
258         self._watchers = {}
259         self._timers = {}
260         self._buffer = ""
261
262         # Create main window
263         self.window = gtk.Window()
264         try: 
265             window_size = map(int, config['window_size'].split(','))
266             self.window.set_default_size(*window_size)
267
268         except:
269             error("Invalid value for default_size in config file.")
270
271         self.window.set_title("Uzbl Browser")
272         self.window.set_border_width(0)
273         
274         # Set main window icon
275         icon_path = config['icon_path']
276         if os.path.exists(icon_path):
277             self.window.set_icon(gtk.gdk.pixbuf_new_from_file(icon_path))
278
279         else:
280             icon_path = '/usr/share/uzbl/examples/data/uzbl/uzbl.png'
281             if os.path.exists(icon_path):
282                 self.window.set_icon(gtk.gdk.pixbuf_new_from_file(icon_path))
283         
284         # Attach main window event handlers
285         self.window.connect("delete-event", self.quit)
286         
287         # Create tab list 
288         if config['show_tabs']:
289             vbox = gtk.VBox()
290             self.window.add(vbox)
291
292             self.tablist = gtk.Label()
293             self.tablist.set_use_markup(True)
294             self.tablist.set_justify(gtk.JUSTIFY_LEFT)
295             self.tablist.set_line_wrap(False)
296             self.tablist.set_selectable(False)
297             self.tablist.set_padding(0,2)
298             self.tablist.set_alignment(0,0)
299             self.tablist.set_ellipsize(pango.ELLIPSIZE_END)
300             self.tablist.set_text(" ")
301             self.tablist.show()
302             vbox.pack_start(self.tablist, False, False, 0)
303         
304         # Create notebook
305         self.notebook = gtk.Notebook()
306         self.notebook.set_show_tabs(config['show_gtk_tabs'])
307         self.notebook.set_show_border(False)
308         self.notebook.connect("page-removed", self.tab_closed)
309         self.notebook.connect("switch-page", self.tab_changed)
310         self.notebook.show()
311         if config['show_tabs']:
312             vbox.pack_end(self.notebook, True, True, 0)
313             vbox.show()
314         else:
315             self.window.add(self.notebook)
316         
317         self.window.show()
318         self.wid = self.notebook.window.xid
319         # Fifo socket definition
320         self._refindfifos = re.compile('^uzbl_fifo_%s_[0-9]+$' % self.wid)
321         fifo_filename = 'uzbltabbed_%d' % os.getpid()
322         self.fifo_socket = os.path.join(config['fifo_dir'], fifo_filename)
323
324         self._watchers = {}
325         self._buffer = ""
326         self._create_fifo_socket(self.fifo_socket)
327         self._setup_fifo_watcher(self.fifo_socket)
328
329
330     def run(self):
331         
332         # Update tablist timer
333         timer = "update-tablist"
334         timerid = gobject.timeout_add(500, self.update_tablist,timer)
335         self._timers[timer] = timerid
336
337         # Due to the hackish way in which the window titles are read 
338         # too many window will cause the application to slow down insanely
339         timer = "probe-clients"
340         timerid = gobject.timeout_add(1000, self.probe_clients, timer)
341         self._timers[timer] = timerid
342
343         gtk.main()
344
345
346     def _find_fifos(self, fifo_dir):
347         '''Find all child fifo sockets in fifo_dir.'''
348         
349         dirlist = '\n'.join(os.listdir(fifo_dir))
350         allfifos = self._refindfifos.findall(dirlist)
351         return sorted(allfifos)
352
353
354     def _create_fifo_socket(self, fifo_socket):
355         '''Create interprocess communication fifo socket.''' 
356
357         if os.path.exists(fifo_socket):
358             if not os.access(fifo_socket, os.F_OK | os.R_OK | os.W_OK):
359                 os.mkfifo(fifo_socket)
360
361         else:
362             basedir = os.path.dirname(self.fifo_socket)
363             if not os.path.exists(basedir):
364                 rmkdir(basedir)
365             os.mkfifo(self.fifo_socket)
366         
367         print "Listening on %s" % self.fifo_socket
368
369
370     def _setup_fifo_watcher(self, fifo_socket, fd=None):
371         '''Open fifo socket fd and setup gobject IO_IN & IO_HUP watchers.
372         Also log the creation of a fd and store the the internal
373         self._watchers dictionary along with the filename of the fd.'''
374         
375         #TODO: Convert current self._watcher dict manipulation to the better 
376         # IMHO self._timers handling by using "timer-keys" as the keys instead
377         # of the fifo fd's as keys.
378
379         if fd:
380             os.close(fd)
381             if fd in self._watchers.keys():
382                 d = self._watchers[fd]
383                 watchers = d['watchers']
384                 for watcher in list(watchers):
385                     gobject.source_remove(watcher)
386                     watchers.remove(watcher)
387                 del self._watchers[fd]         
388         
389         fd = os.open(fifo_socket, os.O_RDONLY | os.O_NONBLOCK)
390         self._watchers[fd] = {'watchers': [], 'filename': fifo_socket}
391             
392         watcher = self._watchers[fd]['watchers'].append
393         watcher(gobject.io_add_watch(fd, gobject.IO_IN, self.read_fifo))
394         watcher(gobject.io_add_watch(fd, gobject.IO_HUP, self.fifo_hangup))
395         
396
397     def probe_clients(self, timer_call):
398         '''Load balance probe all uzbl clients for up-to-date window titles 
399         and uri's.'''
400         
401         p = self.pages 
402         probetimes = [(s, p[s]._lastprobe) for s in p.keys()]
403         socket, lasttime = sorted(probetimes, key=lambda t: t[1])[0]
404
405         if (time.time()-lasttime) > 5:
406             # Probe a uzbl instance at most once every 10 seconds
407             self.pages[socket].probe()
408
409         return True
410
411
412     def fifo_hangup(self, fd, cb_condition):
413         '''Handle fifo socket hangups.'''
414         
415         # Close fd, re-open fifo_socket and watch.
416         self._setup_fifo_watcher(self.fifo_socket, fd)
417
418         # And to kill any gobject event handlers calling this function:
419         return False
420
421
422     def read_fifo(self, fd, cb_condition):
423         '''Read from fifo socket and handle fifo socket hangups.'''
424
425         self._buffer = os.read(fd, 1024)
426         temp = self._buffer.split("\n")
427         self._buffer = temp.pop()
428
429         for cmd in [s.strip().split() for s in temp if len(s.strip())]:
430             try:
431                 #print cmd
432                 self.parse_command(cmd)
433
434             except:
435                 #raise
436                 error("Invalid command: %s" % ' '.join(cmd))
437         
438         return True
439
440     def parse_command(self, cmd):
441         '''Parse instructions from uzbl child processes.'''
442         
443         # Commands ( [] = optional, {} = required ) 
444         # new [uri]
445         #   open new tab and head to optional uri. 
446         # close [tab-num] 
447         #   close current tab or close via tab id.
448         # next [n-tabs]
449         #   open next tab or n tabs down. Supports negative indexing.
450         # prev [n-tabs]
451         #   open prev tab or n tabs down. Supports negative indexing.
452         # goto {tab-n}
453         #   goto tab n.  
454         # first
455         #   goto first tab.
456         # last
457         #   goto last tab. 
458         # title {pid} {document-title}
459         #   updates tablist title.
460         # url {pid} {document-location}
461          
462         # WARNING SOME OF THESE COMMANDS MIGHT NOT BE WORKING YET OR FAIL.
463
464         if cmd[0] == "new":
465             if len(cmd) == 2:
466                 self.new_tab(cmd[1])
467
468             else:
469                 self.new_tab()
470
471         elif cmd[0] == "newfromclip":
472             url = subprocess.Popen(['xclip','-selection','clipboard','-o'],\
473               stdout=subprocess.PIPE).communicate()[0]
474             if url:
475                 self.new_tab(url)
476
477         elif cmd[0] == "close":
478             if len(cmd) == 2:
479                 self.close_tab(int(cmd[1]))
480
481             else:
482                 self.close_tab()
483
484         elif cmd[0] == "next":
485             if len(cmd) == 2:
486                 self.next_tab(int(cmd[1]))
487                    
488             else:
489                 self.next_tab()
490
491         elif cmd[0] == "prev":
492             if len(cmd) == 2:
493                 self.prev_tab(int(cmd[1]))
494
495             else:
496                 self.prev_tab()
497         
498         elif cmd[0] == "goto":
499             self.goto_tab(int(cmd[1]))
500
501         elif cmd[0] == "first":
502             self.goto_tab(0)
503
504         elif cmd[0] == "last":
505             self.goto_tab(-1)
506
507         elif cmd[0] in ["title", "url"]:
508             if len(cmd) > 2:
509                 uzbl = self.get_uzbl_by_pid(int(cmd[1]))
510                 if uzbl:
511                     setattr(uzbl, cmd[0], ' '.join(cmd[2:]))
512                 else:
513                     error("Cannot find uzbl instance with pid %r" % int(cmd[1]))
514         else:
515             error("Unknown command: %s" % ' '.join(cmd))
516
517     
518     def get_uzbl_by_pid(self, pid):
519         '''Return uzbl instance by pid.'''
520
521         for socket in self.pages.keys():
522             if self.pages[socket].pid == pid:
523                 return self.pages[socket]
524         return False
525    
526
527     def new_tab(self,url='', switch=True):
528         '''Add a new tab to the notebook and start a new instance of uzbl.
529         Use the switch option to negate config['switch_to_new_tabs'] option 
530         when you need to load multiple tabs at a time (I.e. like when 
531         restoring a session from a file).'''
532        
533         pid = self.next_pid()
534         socket = gtk.Socket()
535         socket.show()
536         self.notebook.append_page(socket)
537         sid = socket.get_id()
538         
539         if url:
540             url = '--uri %s' % url
541         
542         fifo_filename = 'uzbl_fifo_%s_%0.2d' % (self.wid, pid)
543         fifo_socket = os.path.join(config['fifo_dir'], fifo_filename)
544         uzbl = self.UzblInstance(self, socket, fifo_socket, pid,\
545           url=url, switch=switch)
546         self.pages[socket] = uzbl
547         cmd = 'uzbl -s %s -n %s_%0.2d %s &' % (sid, self.wid, pid, url)
548         subprocess.Popen([cmd], shell=True)        
549         
550         # Add gobject timer to make sure the config is pushed when fifo socket
551         # has been created. 
552         timerid = gobject.timeout_add(100, uzbl.flush, "flush-initial-config")
553         uzbl.timers['flush-initial-config'] = timerid
554     
555         self.update_tablist()
556
557
558     def config_uzbl(self, uzbl):
559         '''Send bind commands for tab new/close/next/prev to a uzbl 
560         instance.'''
561
562         binds = []
563         bind_format = 'bind %s = sh "echo \\\"%s\\\" > \\\"%s\\\""'
564         bind = lambda key, action: binds.append(bind_format % (key, action, \
565           self.fifo_socket))
566         
567         # Keys are defined in the config section
568         # bind ( key , command back to fifo ) 
569         bind(config['bind_new_tab'], 'new')
570         bind(config['bind_tab_from_clipboard'], 'newfromclip')
571         bind(config['bind_close_tab'], 'close')
572         bind(config['bind_next_tab'], 'next')
573         bind(config['bind_prev_tab'], 'prev')
574         bind(config['bind_goto_tab'], 'goto %s')
575         bind(config['bind_goto_first'], 'goto 0')
576         bind(config['bind_goto_last'], 'goto -1')
577
578         uzbl.send("\n".join(binds))
579
580
581     def goto_tab(self, n):
582         '''Goto tab n (supports negative indexing).'''
583         
584         notebook = list(self.notebook)
585         
586         try: 
587             page = notebook[n]
588             i = notebook.index(page)
589             self.notebook.set_current_page(i)
590         
591         except IndexError:
592             pass
593
594         self.update_tablist()
595
596
597     def next_tab(self, n=1):
598         '''Switch to next tab or n tabs right.'''
599         
600         if n >= 1:
601             numofpages = self.notebook.get_n_pages()
602             pagen = self.notebook.get_current_page() + n
603             self.notebook.set_current_page( pagen % numofpages ) 
604
605         self.update_tablist()
606
607
608     def prev_tab(self, n=1):
609         '''Switch to prev tab or n tabs left.'''
610         
611         if n >= 1:
612             numofpages = self.notebook.get_n_pages()
613             pagen = self.notebook.get_current_page() - n
614             while pagen < 0: 
615                 pagen += numofpages
616             self.notebook.set_current_page(pagen)
617
618         self.update_tablist()
619
620
621     def close_tab(self, tabid=None):
622         '''Closes current tab. Supports negative indexing.'''
623         
624         if not tabid: 
625             tabid = self.notebook.get_current_page()
626         
627         try: 
628             socket = list(self.notebook)[tabid]
629
630         except IndexError:
631             error("Invalid index. Cannot close tab.")
632             return False
633
634         uzbl = self.pages[socket]
635         # Kill timers:
636         for timer in uzbl.timers.keys():
637             error("Removing timer %r %r" % (timer, uzbl.timers[timer]))
638             gobject.source_remove(uzbl.timers[timer])
639
640         uzbl._outgoing = []
641         uzbl._kill = True
642         del self.pages[socket]
643         self.notebook.remove_page(tabid)
644
645         self.update_tablist()
646
647
648     def tab_closed(self, notebook, socket, page_num):
649         '''Close the window if no tabs are left. Called by page-removed 
650         signal.'''
651         
652         if socket in self.pages.keys():
653             uzbl = self.pages[socket]
654             for timer in uzbl.timers.keys():
655                 error("Removing timer %r %r" % (timer, uzbl.timers[timer]))
656                 gobject.source_remove(uzbl.timers[timer])
657
658             uzbl._outgoing = []
659             uzbl._kill = True
660             del self.pages[socket]
661         
662         if self.notebook.get_n_pages() == 0:
663             self.quit()
664
665         self.update_tablist()
666
667
668     def tab_changed(self, notebook, page, page_num):
669         '''Refresh tab list. Called by switch-page signal.'''
670
671         self.update_tablist()
672
673
674     def update_tablist(self, timer_call=None):
675         '''Upate tablist status bar.'''
676
677         pango = ""
678
679         normal = (config['tab_colours'], config['tab_text_colours'])
680         selected = (config['selected_tab'], config['selected_tab_text'])
681         
682         tab_format = "<span %s> [ %d <span %s> %s</span> ] </span>"
683         
684         title_format = "%s - Uzbl Browser"
685
686         uzblkeys = self.pages.keys()
687         curpage = self.notebook.get_current_page()
688
689         for index, socket in enumerate(self.notebook):
690             if socket not in uzblkeys:
691                 #error("Theres a socket in the notebook that I have no uzbl "\
692                 #  "record of.")
693                 continue
694             uzbl = self.pages[socket]
695             
696             if index == curpage:
697                 colours = selected
698                 self.window.set_title(title_format % uzbl.title)
699
700             else:
701                 colours = normal
702             
703             pango += tab_format % (colours[0], index, colours[1], uzbl.title)
704
705         self.tablist.set_markup(pango)
706
707         return True
708
709
710     #def quit(self, window, event):
711     def quit(self, *args):
712         '''Cleanup the application and quit. Called by delete-event signal.'''
713
714         for fd in self._watchers.keys():
715             d = self._watchers[fd]
716             watchers = d['watchers']
717             for watcher in list(watchers):
718                 gobject.source_remove(watcher)
719         
720         for timer in self._timers.keys():
721             gobject.source_remove(self._timers[timer])
722
723         if os.path.exists(self.fifo_socket):
724             os.unlink(self.fifo_socket)
725             print "Unlinked %s" % self.fifo_socket
726         
727         if config['save_session']:
728             session_file = os.path.expandvars(config['session_file'])
729             if self.notebook.get_n_pages():
730                 if not os.path.isfile(session_file):
731                     dirname = os.path.dirname(session_file)
732                     if not os.path.isdir(dirname):
733                         rmkdir(dirname)
734
735                 h = open(session_file, 'w')
736                 h.write('current = %s\n' % self.notebook.get_current_page())
737                 h.close()
738                 for socket in list(self.notebook):
739                     if socket not in self.pages.keys(): continue
740                     uzbl = self.pages[socket]
741                     uzbl.send('sh "echo $6 >> %s"' % session_file)
742                     time.sleep(0.05)
743
744             else:
745                 # Notebook has no pages so delete session file if it exists.
746                 # Its better to not exist than be blank IMO. 
747                 if os.path.isfile(session_file):
748                     os.remove(session_file)
749
750         gtk.main_quit() 
751
752
753 if __name__ == "__main__":
754     
755     # Read from the uzbl config into the global config dictionary. 
756     readconfig(uzbl_config, config)
757      
758     uzbl = UzblTabbed()
759     
760     if os.path.isfile(os.path.expandvars(config['session_file'])):
761         h = open(os.path.expandvars(config['session_file']),'r')
762         urls = [s.strip() for s in h.readlines()]
763         h.close()
764         current = 0
765         for url in urls:
766             if url.startswith("current"):
767                 current = int(url.split()[-1])
768             else:
769                 uzbl.new_tab(url, False)
770     else:
771         uzbl.new_tab()
772
773     uzbl.run()
774
775