davemds pushed a commit to branch master.

http://git.enlightenment.org/enlightenment/modules/edgar.git/commit/?id=fb1afc286b81d4a60580aed566258f4c555aeb62

commit fb1afc286b81d4a60580aed566258f4c555aeb62
Author: Dave Andreoli <d...@gurumeditation.it>
Date:   Sun Aug 30 13:56:03 2015 +0200

    Add a new Dropbox gadget
    
    It show the dropbox status and enable to start/stop the daemon
---
 GADGETS/dropbox/Makefile                     |  36 ++++
 GADGETS/dropbox/__init__.py                  | 252 +++++++++++++++++++++++++++
 GADGETS/dropbox/docs/dropboxd-protocol       |  97 +++++++++++
 GADGETS/dropbox/dropbox.edc                  |  98 +++++++++++
 GADGETS/dropbox/images/dropbox.png           | Bin 0 -> 8219 bytes
 GADGETS/dropbox/images/dropbox_gray.png      | Bin 0 -> 8463 bytes
 GADGETS/dropbox/images/emblem-syncing.png    | Bin 0 -> 6708 bytes
 GADGETS/dropbox/images/emblem-unsyncable.png | Bin 0 -> 3584 bytes
 GADGETS/dropbox/images/emblem-uptodate.png   | Bin 0 -> 4358 bytes
 9 files changed, 483 insertions(+)

diff --git a/GADGETS/dropbox/Makefile b/GADGETS/dropbox/Makefile
new file mode 100644
index 0000000..50b1f27
--- /dev/null
+++ b/GADGETS/dropbox/Makefile
@@ -0,0 +1,36 @@
+# Simple Makefile for Enlightenment (edgar) gadgets
+
+# gadget specific config
+GADGET_NAME = dropbox
+EXTRA_FILES = 
+
+
+# nothing  should be changed below this point
+GADGET_FILES = __init__.pyc $(GADGET_NAME).edj
+prefix = $(shell pkg-config --variable=libdir enlightenment)
+gadget_folder = ${prefix}/enlightenment/gadgets/$(GADGET_NAME)
+
+.PHONY: all install clean
+
+all: $(GADGET_FILES) $(EXTRA_FILES)
+
+install: all
+       @mkdir -p -v ${gadget_folder}
+       @cp --preserve=mode -v $(GADGET_FILES) $(EXTRA_FILES) $(gadget_folder)
+
+uninstall: all
+       @rm -rfv ${gadget_folder}
+
+clean:
+       @rm -fv *.edj *.pyc
+
+
+EDJE_CC = edje_cc
+EDJE_FLAGS = -v -id images/ -fd fonts/
+
+%.edj: %.edc images/*
+       $(EDJE_CC) $(EDJE_FLAGS) $<
+       @chmod -v og+r $@
+
+%.pyc: %.py
+       python3 -c "from py_compile import compile; compile('$<', '$@')"
diff --git a/GADGETS/dropbox/__init__.py b/GADGETS/dropbox/__init__.py
new file mode 100644
index 0000000..fdfb415
--- /dev/null
+++ b/GADGETS/dropbox/__init__.py
@@ -0,0 +1,252 @@
+# This python file use the following encoding: utf-8
+
+import os
+import sys
+import socket
+
+import e
+
+from efl import ecore
+from efl import evas
+from efl import edje
+from efl.elementary.label import Label
+from efl.elementary.entry import utf8_to_markup
+from efl.elementary.button import Button
+
+
+__gadget_name__ = 'Dropbox'
+__gadget_vers__ = '0.1'
+__gadget_auth__ = 'DaveMDS'
+__gadget_mail__ = 'd...@gurumeditation.it'
+__gadget_desc__ = 'Dropbox info gadget.'
+__gadget_vapi__ = 1
+__gadget_opts__ = { 'popup_on_desktop': False }
+
+
+#def DBG(msg):
+#    print("DB: " + msg)
+#    sys.stdout.flush()
+
+
+class Gadget(e.Gadget):
+
+    def __init__(self):
+        super().__init__()
+        self.db = Dropbox(self.db_status_changed_cb)
+
+    def instance_created(self, obj, site):
+        super().instance_created(obj, site)
+        obj.size_hint_aspect = evas.EVAS_ASPECT_CONTROL_BOTH , 16, 16
+
+    def instance_destroyed(self, obj):
+        super().instance_destroyed(obj)
+
+    def popup_created(self, popup):
+        super().popup_created(popup)
+
+        popup.data['lb'] = Label(popup)
+        popup.part_box_append('popup.box', popup.data['lb'])
+        popup.data['lb'].show()
+
+        popup.data['bt'] = Button(popup)
+        popup.data['bt'].callback_clicked_add(self.start_stop_clicked_cb)
+        popup.part_box_append('popup.box', popup.data['bt'])
+        popup.data['bt'].show()
+
+        self.popup_update(popup)
+
+    def popup_destroyed(self, popup):
+        super().popup_destroyed(popup)
+
+    def db_status_changed_cb(self):
+        for icon in self._instances:
+            if self.db.is_running:
+                icon.signal_emit('daemon,running', '')
+                icon.signal_emit('state,'+self.db.status, '')
+            else:
+                icon.signal_emit('daemon,not_running', '')
+                icon.signal_emit('state,unwatched', '')
+
+        for popup in self._popups:
+            self.popup_update(popup)
+
+    def popup_update(self, popup):
+        if self.db.is_running:
+            popup.data['lb'].text = utf8_to_markup(self.db.status_msg)
+            popup.data['bt'].text = 'Stop Dropbox'
+            popup.data['bt'].disabled = False
+        elif self.db.is_installed:
+            popup.data['lb'].text = "Dropbox isn't running!"
+            popup.data['bt'].text = 'Start Dropbox'
+            popup.data['bt'].disabled = False
+        else:
+            popup.data['lb'].text = "Dropbox isn't installed!"
+            popup.data['bt'].text = 'Install Dropbox'
+            popup.data['bt'].disabled = True
+
+        # force the popup to recalculate it's size
+        popup.size_hint_min = popup.size_min
+
+    def start_stop_clicked_cb(self, btn):
+        if self.db.is_running:
+            self.db.stop()
+        else:
+            self.db.start()
+
+
+class Dropbox(object):
+    def __init__(self, status_changed_cb=None):
+        self._status_changed_cb = status_changed_cb
+
+        self.BASE_FOLDER = os.path.expanduser('~/Dropbox')
+        self.DAEMON = os.path.expanduser('~/.dropbox-dist/dropboxd')
+        self.PIDFILE = os.path.expanduser('~/.dropbox/dropbox.pid')
+        self.CMD_SOCKET = os.path.expanduser('~/.dropbox/command_socket')
+
+        self._cmd_socket = None
+        self._cmd_fdh = None
+        self._reply_buffer = ''
+        self._status = ''
+        self._status_msg = ''
+
+        self._connect_timer()
+        ecore.Timer(2.0, self._connect_timer)
+        ecore.Timer(2.0, self._fetch_status_timer)
+
+    @property
+    def is_installed(self):
+        return os.path.exists(self.DAEMON)
+
+    @property
+    def is_running(self):
+        """ Check if the dropbox daemon is running """
+        try:
+            with open(self.PIDFILE, 'r') as f:
+                pid = int(f.read())
+            with open('/proc/%d/cmdline' % pid, 'r') as f:
+                cmdline = f.read().lower()
+        except:
+            cmdline = ''
+
+        return 'dropbox' in cmdline
+
+    @property
+    def is_connected(self):
+        """ are we connected to the deamon socket ? """
+        return self._cmd_fdh != None
+
+    @property
+    def status(self):
+        """ 'up to date', 'syncing', 'unsyncable' or 'unwatched' """
+        return self._status
+
+    @property
+    def status_msg(self):
+        """ Long status message (more than one line) """
+        return self._status_msg
+
+    def start(self):
+        """ Start the dropbox daemon """
+        ecore.Exe(self.DAEMON)
+
+    def stop(self):
+        """ Stop the dropbox daemon """
+        if self.is_connected:
+            cmd = 'tray_action_hard_exit\ndone\n'
+            try:
+                self._cmd_socket.sendall(cmd.encode('utf-8'))
+            except:
+                self._disconnect()
+
+    def _connect_timer(self):
+        """ Try to connect to the daemon socket (if needed) """
+        if self.is_connected:
+            return ecore.ECORE_CALLBACK_RENEW
+
+        if not self.is_running:
+            return ecore.ECORE_CALLBACK_RENEW
+
+        try:
+            self._cmd_socket = socket.socket(socket.AF_UNIX, 
socket.SOCK_STREAM)
+            self._cmd_socket.connect(self.CMD_SOCKET)
+        except:
+            self._cmd_socket = None
+            return ecore.ECORE_CALLBACK_RENEW
+
+        self._cmd_fdh = ecore.FdHandler(self._cmd_socket,
+                                        ecore.ECORE_FD_READ | 
ecore.ECORE_FD_ERROR,
+                                        self._cmd_socket_data_available)
+
+        return ecore.ECORE_CALLBACK_RENEW
+
+    def _disconnect(self):
+        """ Disconnect from the daemon socket """
+        self._cmd_fdh.delete()
+        self._cmd_fdh = None
+        self._cmd_socket.close()
+        self._cmd_socket = None
+        self._reply_buffer = ''
+        self._status = ''
+        self._status_msg = ''
+
+        # alert the user that the state has changed
+        self._status_changed_cb()
+
+    def _update_status(self, new_status):
+        if new_status == self._status:
+            return
+        self._status = new_status
+
+    def _update_status_msg(self, new_status):
+        if new_status == self._status_msg:
+            return
+        self._status_msg = new_status
+
+        # alert the user that the state has changed
+        self._status_changed_cb()
+
+    def _cmd_socket_data_available(self, fdh):
+        if fdh.has_error():
+            self._disconnect()
+            return ecore.ECORE_CALLBACK_CANCEL
+
+        while True:
+            tmp = self._cmd_socket.recv(1024)
+            self._reply_buffer += tmp.decode('utf-8')
+            if len(tmp) < 1024:
+                break
+
+        self._finalize_reply()
+        return ecore.ECORE_CALLBACK_RENEW
+
+    def _finalize_reply(self):
+        reply = self._reply_buffer
+        if reply.endswith('done\n'):
+            self._reply_buffer = ''
+
+            for cmd in reply.split('done\n'):
+                if not cmd:
+                    continue
+
+                cmd = cmd.split('\n')
+                if cmd[0] != 'ok':
+                    return
+                if len(cmd) > 1 and cmd[1].startswith('status'):
+                    status = cmd[1].split('\t')
+                    if len(status) == 2 and status[1] in ('up to date', 
'syncing',
+                                                          
'unsyncable','unwatched'):
+                        self._update_status(status[1])
+                    elif len(status) >= 2:
+                        self._update_status_msg('\n'.join(status[1:]))
+
+    def _fetch_status_timer(self):
+        if self.is_connected:
+            c1 = 'icon_overlay_file_status\npath\t%s\ndone\n' % 
self.BASE_FOLDER
+            c2 = 'get_dropbox_status\ndone\n'
+            try:
+                self._cmd_socket.sendall((c1 + c2).encode('utf-8'))
+            except:
+                self._disconnect()
+
+        return ecore.ECORE_CALLBACK_RENEW
+
diff --git a/GADGETS/dropbox/docs/dropboxd-protocol 
b/GADGETS/dropbox/docs/dropboxd-protocol
new file mode 100644
index 0000000..2fc8cd5
--- /dev/null
+++ b/GADGETS/dropbox/docs/dropboxd-protocol
@@ -0,0 +1,97 @@
+
+Created by Steffen Schuldenzucker, 2010. If you know something important that I
+missed, feel free to contact me: sschuldenzucker (at) uni-bonn (dot) de
+
+This is what I got out of the source code of nautilus-dropbox, dropbox.py and
+dbcli.py. A big thanks to the authors of these tools.
+
+Protocol:
+=========
+
+request:
+--------
+<command>
+<key1>    <value11>    <value12>    ...
+...
+done
+
+
+reply (ok):
+-----------
+ok
+<key1>    <value11>    <value12>    ...
+...
+done
+
+reply (error):
+--------------
+notok
+
+Delimiters for several items within a line (key and values above) are tabs. For
+each key, any number of values may be given (including 0). Any number of
+key-value-lines may be given (including 0)
+Every message is terminated by a newline (after "done" or "notok", 
respectively)
+Encoding is utf-8.
+All paths mentioned have to be absolute (to "/").
+
+Available Commands:
+===================
+I only list replys for a successful command. TODO: can we get a more precise
+error message?
+A key may only appear once. If it appears more often here, these are
+alternatives.
+
+<- get_dropbox_status # get deamon's status
+-> status # if Idle
+   status    <msg> 
+   # ^ if something is happening. msg is a human-readable description
+
+<- get_public_link # see also the "copypublic" action below
+   path    /file/in/Public # has to be a plain file, no folder
+-> link    http://...
+
+<- get_folder_tag /some/folder/
+-> tag    shared  # this folder is shared
+   tag    dropbox # this is your dropbox's root folder
+   tag    public  # this is your public folder
+   tag    photos  # this is your photos folder
+   tag            # otherwise
+
+<- icon_overlay_file_status # is the file up-to-date?
+   path    /path/in/Dropbox
+-> status    up to date
+   status    syncing
+   status    unsyncable # TODO: when can this occur?
+   status    unwatched  
+   # ^ file is outside your Dropbox or is one of the ".dropbox" system files.
+
+# TODO: the name "paths" suggests that several files can be given. However,
+# this is not done in nautilus-dropbox (and not seen elsewhere).
+<- icon_overlay_context_options # get a list of available actions on this file
+   paths    /path/in/Dropbox
+-> options    item1~desc1~action1    item2~desc2~action2    ...
+   # item: what is displayed in the menu
+   # desc: a tool tip for this item
+   # action: which verb to use to activate this option (see below)
+
+<- icon_overlay_context_action # perform some context action (see above)
+   verb    <action> # see below
+   paths    /path/in/Dropbox
+-> # no reply, except "ok" and "done"
+
+Where <action> is one of
+
+# these open a page in your web browser
+browse
+revisions   # only on plain files
+share       # only on folders
+# these copy a http://... link to the clipboard
+copypublic  # only on plain files within the Public folder
+copygallery # only on folders within the Photos folder
+
+NOTE that the clipboard (i.e. Ctrl-C / Ctrl-V) is something different than the
+selection (select / middle mouse button). That took me some time today...
+
+<- tray_action_hard_exit # terminate the deamon
+-> # NO reply. not even "ok".
+
diff --git a/GADGETS/dropbox/dropbox.edc b/GADGETS/dropbox/dropbox.edc
new file mode 100644
index 0000000..b499e19
--- /dev/null
+++ b/GADGETS/dropbox/dropbox.edc
@@ -0,0 +1,98 @@
+/**
+ *  EDGAR Dropbox Gadget
+ */
+
+
+images {
+   image: "dropbox.png" COMP;
+   image: "dropbox_gray.png" COMP;
+   image: "emblem-uptodate.png" COMP;
+   image: "emblem-syncing.png" COMP;
+   image: "emblem-unsyncable.png" COMP;
+}
+
+
+collections {
+/**
+ *  API [e/gadget/icon] The group used for the icon of the gadget
+ */
+   group { name: "e/gadgets/dropbox/icon";
+      parts {
+         part { name: "icon";
+            description {
+               state: "default" 0.0;
+               aspect: 1.0 1.0;
+               aspect_preference: BOTH;
+               image {
+                  normal: "dropbox.png";
+               }
+            }
+         }
+      }
+   }
+
+/**
+ *  API [e/gadget/main] The main group of the gadget
+ */
+   group { name: "e/gadgets/dropbox/main";
+      parts {
+         image { "icon";
+            desc { "default";
+               aspect: 1.0 1.0; aspect_preference: BOTH;
+               image.normal: "dropbox_gray.png";
+               link.base: "daemon,not_running";
+            }
+            desc { "running";
+               aspect: 1.0 1.0; aspect_preference: BOTH;
+               image.normal: "dropbox.png";
+               link.base: "daemon,running";
+            }
+         }
+         image { "status";
+            desc { "default";
+               visible: 0;
+               rel1.relative: 0.6 0.6;
+               rel2.relative: 0.95 0.95;
+               image.normal: "emblem-uptodate.png";
+               link.base: "state,unwatched";
+            }
+            desc { "uptodate";
+               inherit: "default";
+               visible: 1;
+               image.normal: "emblem-uptodate.png";
+               link.base: "state,up to date";
+            }
+            desc { "syncing";
+               inherit: "default";
+               visible: 1;
+               image.normal: "emblem-syncing.png";
+               link.base: "state,syncing";
+            }
+            desc { "unsyncable";
+               inherit: "default";
+               visible: 1;
+               image.normal: "emblem-unsyncable.png";
+               link.base: "state,unsyncable";
+            }
+         }
+      }
+   }
+
+/**
+ *  API [e/gadget/popup] This is the group that will be placed inside popups
+ */
+   group { name: "e/gadgets/dropbox/popup";
+      // min: 310 0;
+      parts {
+         box { "popup.box";
+            desc { "default";
+               box {
+                  layout: "vertical";
+                  padding: 0 6;
+                  min: 1 1;
+               }
+            }
+         }
+      }
+   }
+}
diff --git a/GADGETS/dropbox/images/dropbox.png 
b/GADGETS/dropbox/images/dropbox.png
new file mode 100644
index 0000000..4450929
Binary files /dev/null and b/GADGETS/dropbox/images/dropbox.png differ
diff --git a/GADGETS/dropbox/images/dropbox_gray.png 
b/GADGETS/dropbox/images/dropbox_gray.png
new file mode 100644
index 0000000..8c50460
Binary files /dev/null and b/GADGETS/dropbox/images/dropbox_gray.png differ
diff --git a/GADGETS/dropbox/images/emblem-syncing.png 
b/GADGETS/dropbox/images/emblem-syncing.png
new file mode 100644
index 0000000..82c3d96
Binary files /dev/null and b/GADGETS/dropbox/images/emblem-syncing.png differ
diff --git a/GADGETS/dropbox/images/emblem-unsyncable.png 
b/GADGETS/dropbox/images/emblem-unsyncable.png
new file mode 100644
index 0000000..0f6704f
Binary files /dev/null and b/GADGETS/dropbox/images/emblem-unsyncable.png differ
diff --git a/GADGETS/dropbox/images/emblem-uptodate.png 
b/GADGETS/dropbox/images/emblem-uptodate.png
new file mode 100644
index 0000000..d0e5a11
Binary files /dev/null and b/GADGETS/dropbox/images/emblem-uptodate.png differ

-- 


Reply via email to