File: webboard.py

package info (click to toggle)
webboard 0.3-3
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 240 kB
  • ctags: 94
  • sloc: python: 679; sh: 61; makefile: 56
file content (348 lines) | stat: -rw-r--r-- 13,439 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# WebBoard allows you to publish text on a public pastebin on the net
# This applications is based on the command line tool paste of 
# Dennis Kaarsemaker
#
#   (c) 2005 - Dennis Kaarsemaker <dennis@kaarsemaker.net>
#   (c) 2006 - Sebastian Heinlein <sebastian.heinlein@web.de>
#   (c) 2009 - Olivier Le Thanh Duong <olivier@lethanh.be>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

import thread

import gtk
import gtk.glade
import gio
import pango
import gtksourceview
import gobject

import gettext
from gettext import gettext as _
gettext.bindtextdomain('webboard')
gettext.textdomain('webboard')
gtk.glade.bindtextdomain('webboard')
gtk.glade.textdomain('webboard')

import wbconfig, util

# The key used here is the format name as know by paste.debian.net
# FIXME Not all format supported by paste.debian.net are listed
langs_list = {
    "Plain"         : (_("Plain text"), "None"),
    "sh"            : ("Bash", "text/x-sh"),
    "python"        : ("Python", "text/x-python"),
    "c"             : ("C", "text/x-c"),
    "cpp"           : ("C++", "text/x-cpp"),
    "html4"         : ("HTML (4.0.1)", "text/html"),
    "java"          : ("Java", "text/x-java"),
    "javascript"    : ("Javascript", "text/x-javascript"),
    "perl"          : ("Perl", "text/x-perl"),
    "php"           : ("PHP", "text/x-php"),
    "sql"           : ("SQL", "text/x-sql"),
    "ada"           : ("Ada", "text/x-ada"),
    "apache"        : (_("Apache log file"), "None"),
    "asm"           : (_("ASM (NASM based)"), "None"),
    "aspvbs"        : ("Active Server Page", "None"),
    "dcl"           : ("CAD DCL", "None"),
    "lisp"          : ("CAD Lisp", "None"),
    "cs"            : ("C#", "text/x-csharp"),
    "css"           : ("CSS", "text/css"),
    "lisp"          : ("Lisp", "None"),
    "lua"           : ("Lua", "None"),
    "nsis"          : (_("NullSoft Installer"), "None"),
    "objc"          : (_("Objective C"), "text/x-c"),
    "oracle8"       : ("Oracle 8", "None"),
    "pascal"        : ("Pascal", "text/x-pascal"),
    "smarty"        : ("Smarty", "None"),
    "vb"            : ("Visual Basic", "text/x-vb"),
    "foxpro"        : ("Visual Fox Pro", "None"),
    "xml"           : ("XML", "text/xml")
}

class WebBoard:
    def __init__(self, config, history, clip=False, file=None):
        """
        Initialize the main window of webboard
        """
        self.config = config
        self.config.add_notifier(self.on_config_changed)
        self.history = history

        # setup an icon for the application
        icons = gtk.icon_theme_get_default()
        self.logo_pixbuf=icons.load_icon("gtk-paste", 32, 0)
        gtk.window_set_default_icon_list(self.logo_pixbuf)
        
        # get the glade widget
        self.glade = gtk.glade.XML(util.find_glade_file("webboard.glade"))
        self.glade.signal_autoconnect(self)

        #set the default language : Plain text
        # FIXME : Couldn't we autodetect this ?
        self.language = "Plain"

        # setup the gtksourceview
        self.lm = gtksourceview.SourceLanguagesManager()
        buffer = gtksourceview.SourceBuffer()
        buffer.set_data('languages-manager', self.lm)
        self.textview = gtksourceview.SourceView(buffer)
        mono_font = config.get_mono_font()
        if mono_font :
            font = pango.FontDescription(mono_font)
            self.textview.modify_font(font)
        scroller = self.glade.get_widget("scrolledwindow_code")
        scroller.add(self.textview)

        # setup the combobox to select the language
        lang_store = gtk.ListStore(gobject.TYPE_STRING,
                                   gobject.TYPE_STRING,
                                   gobject.TYPE_STRING)
        lang_store.set_sort_column_id(1, gtk.SORT_ASCENDING)

        for format in langs_list.keys():
            lang_store.append([format, langs_list[format][0],
                              langs_list[format][1]])

        self.combobox_syntax = self.glade.get_widget("combobox_syntax")
        cell = gtk.CellRendererText()
        self.combobox_syntax.pack_start(cell, True)
        self.combobox_syntax.add_attribute(cell, 'text', 1)

        self.combobox_syntax.set_model(lang_store)
        # FIXME : Do this in a cleaner way (won't work when translated)
        self.combobox_syntax.set_active(21)
        self.combobox_syntax.connect("changed", self.on_combobox_syntax_changed)
        
        # setup drag'n'drop
        self.textview.drag_dest_set(gtk.DEST_DEFAULT_ALL, \
                                    [('text/uri-list',0 , 0)], \
                                    gtk.gdk.ACTION_COPY)
        self.textview.connect("drag_motion", \
                              self.on_textview_drag_motion)
        self.textview.connect("drag_data_received", \
                              self.on_textview_drag_data_received)
        self.window_main = self.glade.get_widget("window_main")
        self.toolbutton_open = self.glade.get_widget("toolbutton_open")
        self.toolbutton_copy = self.glade.get_widget("toolbutton_copy")
        self.toolbutton_send = self.glade.get_widget("toolbutton_send")
        self.combobox_pastebin = self.glade.get_widget("combobox_pastebin")
        self.statusbar = self.glade.get_widget("statusbar")

        self.context = self.statusbar.get_context_id("context_webboard")

        self.clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD)

        self.tooltips = gtk.Tooltips()

        self.on_config_changed()

        self.buffer = self.textview.get_buffer()
        text = self.clipboard.wait_for_text()
        if clip and text:
            self.buffer.set_text(text)
        else:
            self.buffer.set_text(self.welcome)
        if file:
            self.open_file(file)

        # Select the whole text
        self.buffer.select_range(self.buffer.get_start_iter(),
                                 self.buffer.get_end_iter())

        self.textview.show()
        self.window_main.show()

    def on_combobox_syntax_changed(self, combobox):
        """
        Set the selected syntax highlightenging for
        the source view
        """
        lang_store = combobox.get_model()
        iter = combobox.get_active_iter()
        mime = lang_store.get_value(iter, 2)
        self.language = lang_store.get_value(iter, 0)
        buffer = self.textview.get_buffer()
        if mime == "None":
            buffer.set_highlight(False)
        else:
            try:
                language = self.lm.get_language_from_mime_type(mime)
                buffer.set_language(language)
                buffer.set_highlight(True)
            except:
                buffer.set_highlight(False)

    def on_textview_drag_motion(self, widget, content, x, y, time):
        """
        Deselect all text in the text view, so that the drag'n'drop
        doesn't replace the selection
        """
        self.buffer.select_range(self.buffer.get_start_iter(),
                                 self.buffer.get_start_iter())

    def open_file(self, path):
        """
        Read the content of the specified file and write it to
        the text view
        """
        def read_async_cb(input_stream, result):
            data = input_stream.read_finish(result)
            self.textview.get_buffer().set_text(str(data))
            input_stream.close()
            self.window_main.window.set_cursor(None)
            
        try:
            self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
            fp = gio.File(path)
            input_stream = fp.read()
            #64k ought to be enough for anyone ...
            input_stream.read_async(1024 ** 2, read_async_cb)
        except Exception, err:
            self.window_main.window.set_cursor(None)
            raise

    def on_textview_drag_data_received(self, widget, context, x, y,
                                       selection, target_type, timestamp):
        # FIXME : why is it called two time when we drop a file?
        for path in util.uri_from_dnd(selection.data):
            # We open all the file in the liste but they overwite each other
            self.open_file(path)

    def on_config_changed(self):
        # TRANSLATORS: tooltip of the button "publish"
        #              %s is the URL of the pastebin server
        self.tooltips.set_tip(self.toolbutton_send,
                              _("Publish the text on %s and copy the link "\
                                "to the relating website into the clipboard") \
                                % self.config.pastebin)
        # TRANSLATORS: default welcome text
        #              %s is the URL of the pastebin server
        self.welcome = _("Enter, copy or drag and drop source code and text\n"
                         "notes for publishing on %s") % self.config.pastebin
        # TRANSLATORS: The Window title - %s is the URL of the server
        self.window_main.set_title(_("WebBoard - %s") % self.config.pastebin)


    def on_window_main_destroy(self, widget):
        self.clipboard.store()

    def on_history_activate(self, widget, data):
        self.clipboard.set_text(data, len=-1)

    def on_button_preferences_clicked(self, widget=None):
        """
        Show the preferences window
        """
        self.config.preferences()

    def on_button_send_clicked(self, widget=None):
        """
        Lock the interface and run the send function
        in the background
        """
        self.post = self.buffer.get_text(self.buffer.get_start_iter(),
                                             self.buffer.get_end_iter(), False)

        if self.post is "":
            self.statusbar.push(self.context,_("No text for publishing"))
            return

        self.statusbar.push(self.context,_("Publishing..."))
        self.window_main.set_sensitive(False)
        self.textview.set_sensitive(False)
        self.textview.set_editable(False)
        self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))

        lock = thread.allocate_lock()
        lock.acquire()
        thread.start_new_thread(self.send, (self.post, self.language, lock))
        gobject.idle_add(self.check_lock_cb, lock)

    def check_lock_cb(self, lock):
        """Check if the publishing is finished, by looking at the lock."""
        if lock.locked():
            # The sending isn't finished, return true so we get called again
            # later.
            return True

        if self.ret is False:
            self.statusbar.push(self.context,_("Could not publish the text"\
                                               " at %s") % self.config.pastebin)
            self.toolbutton_copy.set_sensitive(False)
            self.toolbutton_open.set_sensitive(False)
        else:

            # Set message in the statusbar
            self.statusbar.push(self.context,
                    _("Published at %s") % self.url)
            # add to history
            self.history.add_paste(self.post, self.url)
            # copy link to the clipboard
            self.clipboard.set_text(self.url, len=-1)
            self.toolbutton_copy.set_sensitive(True)
            self.toolbutton_open.set_sensitive(True)

        self.window_main.window.set_cursor(None)
        self.window_main.set_sensitive(True)
        self.textview.set_editable(True)
        self.textview.set_sensitive(True)

        # Returning False so we won't get called again
        return False

    def on_menu_quit_activate(self, widget):
        self.window_main.destroy()

    def on_button_about_clicked(self, widget):
        util.show_about_dialog()

    def on_button_copy_clicked(self, widget):
        """
        Copy the current URL to the clipboard
        """
        self.clipboard.set_text(self.url, len=-1)

    def on_button_clear_clicked(self, widget):
        """
        Clear the textview
        """
        self.buffer.set_text("")

    def on_button_open_clicked(self, widget=None):
        """
        Open the URL of the latest pastebin publication in a browser window
        """
        util.open_url(self.url)

    def send(self, post, language, lock, *args):
        """
        Call the function to send to pastebin, release the lock afterwards
        """
        self.ret = False
        pastebin = self.config.pastebin
        user = self.config.user

        try:
            self.ret = util.send(post, language, pastebin, user)
            if self.ret:
                self.url = "%s/%s" % (self.config.pastebin, self.ret)
                if not (self.url.startswith("http://") or
                        self.url.startswith("https://")):
                    self.url = "http://" + self.url
        finally:
            lock.release()