[gedit-plugins] Add new Dashboard plugin



commit 378e483929775e1abde6a92010ada5c2192aed1e
Author: Seif Lotfy <seif lotfy com>
Date:   Sat Nov 12 13:08:51 2011 +0100

    Add new Dashboard plugin
    
    Add new Dashboard plugin that displays a grid of recently/most used
    files upon opening a new tab. It also allows searching via Zeitgeist.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=663466

 README                                           |    2 +
 configure.ac                                     |    9 +-
 plugins/dashboard/Makefile.am                    |   14 +
 plugins/dashboard/dashboard.plugin.desktop.in.in |   10 +
 plugins/dashboard/dashboard/Makefile.am          |   10 +
 plugins/dashboard/dashboard/__init__.py          |  101 +++++
 plugins/dashboard/dashboard/dashboard.py         |  474 ++++++++++++++++++++++
 plugins/dashboard/dashboard/utils.py             |  155 +++++++
 po/POTFILES.in                                   |    2 +
 9 files changed, 774 insertions(+), 3 deletions(-)
---
diff --git a/README b/README
index 70eef59..276f90b 100644
--- a/README
+++ b/README
@@ -23,6 +23,8 @@ codecomment		Comment and uncomment blocks of code.
 colorpicker		Pick a color from a dialog and insert its hexadecimal 
 			representation.
 commander		Command gedit from a command line like interface
+dashboard		A Dashboard created in every new tab for quick finding of
+			recently and frequently used items, with an ability to search
 drawspaces		Draw spaces and tabs.
 joinlines		Join several lines or split long ones.
 multiedit		Edit document in multiple places at once
diff --git a/configure.ac b/configure.ac
index 2677449..0001ccf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -93,9 +93,9 @@ ALL_PLUGINS="bookmarks drawspaces wordcompletion taglist"
 USEFUL_PLUGINS="bookmarks drawspaces wordcompletion taglist"
 DEFAULT_PLUGINS="bookmarks drawspaces wordcompletion taglist"
 
-PYTHON_ALL_PLUGINS="bracketcompletion charmap codecomment colorpicker commander joinlines multiedit textsize sessionsaver smartspaces terminal synctex"
-PYTHON_USEFUL_PLUGINS="bracketcompletion charmap codecomment colorpicker commander joinlines multiedit textsize sessionsaver smartspaces terminal synctex"
-PYTHON_DEFAULT_PLUGINS="bracketcompletion charmap codecomment colorpicker commander joinlines multiedit textsize sessionsaver smartspaces terminal synctex"
+PYTHON_ALL_PLUGINS="bracketcompletion charmap codecomment colorpicker commander dashboard joinlines multiedit textsize sessionsaver smartspaces terminal synctex"
+PYTHON_USEFUL_PLUGINS="bracketcompletion charmap codecomment colorpicker commander dashboard joinlines multiedit textsize sessionsaver smartspaces terminal synctex"
+PYTHON_DEFAULT_PLUGINS="bracketcompletion charmap codecomment colorpicker commander dashboard joinlines multiedit textsize sessionsaver smartspaces terminal synctex"
 
 DIST_PLUGINS="$ALL_PLUGINS $PYTHON_ALL_PLUGINS"
 
@@ -310,6 +310,9 @@ plugins/commander/commander/Makefile
 plugins/commander/Makefile
 plugins/commander/modules/find/Makefile
 plugins/commander/modules/Makefile
+plugins/dashboard/dashboard/Makefile
+plugins/dashboard/dashboard.plugin.desktop.in
+plugins/dashboard/Makefile
 plugins/drawspaces/drawspaces.plugin.desktop.in
 plugins/drawspaces/Makefile
 plugins/drawspaces/org.gnome.gedit.plugins.drawspaces.gschema.xml.in
diff --git a/plugins/dashboard/Makefile.am b/plugins/dashboard/Makefile.am
new file mode 100644
index 0000000..2abc1c1
--- /dev/null
+++ b/plugins/dashboard/Makefile.am
@@ -0,0 +1,14 @@
+# Dashboard
+SUBDIRS = dashboard
+plugindir = $(GEDIT_PLUGINS_LIBS_DIR)
+plugin_in_files = dashboard.plugin.desktop.in
+%.plugin: %.plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+plugin_DATA = $(plugin_in_files:.plugin.desktop.in=.plugin)
+
+EXTRA_DIST = $(plugin_in_files)
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/dashboard/dashboard.plugin.desktop.in.in b/plugins/dashboard/dashboard.plugin.desktop.in.in
new file mode 100644
index 0000000..8150515
--- /dev/null
+++ b/plugins/dashboard/dashboard.plugin.desktop.in.in
@@ -0,0 +1,10 @@
+[Plugin]
+Loader=python
+Module=dashboard
+IAge=3
+_Name=Dashboard
+_Description=A Dashboard for new tabs
+Authors=Seif Lotfy <seif lotfy com>
+Copyright=Copyright  2011 Collabora Ltd.
+Website=http://www.gedit.org
+Version= VERSION@
diff --git a/plugins/dashboard/dashboard/Makefile.am b/plugins/dashboard/dashboard/Makefile.am
new file mode 100644
index 0000000..2a7d68f
--- /dev/null
+++ b/plugins/dashboard/dashboard/Makefile.am
@@ -0,0 +1,10 @@
+# Dashboard
+
+plugindir = $(GEDIT_PLUGINS_LIBS_DIR)/dashboard
+
+plugin_PYTHON = \
+	__init__.py \
+	dashboard.py \
+	utils.py
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/dashboard/dashboard/__init__.py b/plugins/dashboard/dashboard/__init__.py
new file mode 100644
index 0000000..b02625f
--- /dev/null
+++ b/plugins/dashboard/dashboard/__init__.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# -- coding: utf-8 --
+#
+# Copyright  2011 Collabora Ltd.
+#            By Seif Lotfy <seif lotfy com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 2.1 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 Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from gi.repository import GObject, Gedit
+from dashboard import Dashboard
+
+class DashboardWindowActivatable(GObject.Object, Gedit.WindowActivatable):
+
+    window = GObject.property(type=Gedit.Window)
+
+    def __init__(self):
+        GObject.Object.__init__(self)
+
+    def do_activate(self):
+        self.status_bar = self.window.get_statusbar()
+        self._handlers = [self.window.connect_after("tab-added", self._add_tab),
+            self.window.connect_after("active-tab-changed", self._toggle_status_bar),
+            self.window.connect_after("destroy", lambda x: self.status_bar.show())]
+
+    def _toggle_status_bar(self, window, tab):
+        self.status_bar.show()
+        for w in tab.get_children():
+            if type(w) == Dashboard:
+                self.status_bar.hide()
+                break
+
+    def _add_tab(self, window, tab):
+
+        notebook = tab.get_parent()
+
+        doc = tab.get_document()
+        uri = doc.get_uri_for_display()
+
+        if not uri.startswith("/"):
+            notebook.get_tab_label(tab).get_children()\
+                    [0].get_children()[0].get_children()[1].hide()
+            self.status_bar.hide()
+            d = None
+            if str(type(notebook.get_tab_label(tab))) == \
+                "<class 'gi.types.__main__.GeditTabLabel'>":
+                label = notebook.get_tab_label(tab).get_children()\
+                    [0].get_children()[0].get_children()[2]
+                text = label.get_text()
+                label.set_text("Getting Started")
+                def show_doc():
+                    notebook.get_tab_label(tab).get_children()\
+                        [0].get_children()[0].get_children()[1].show()
+                    tab.remove(d)
+                    tab.get_children()[0].show()
+                    tab.grab_focus()
+                    if label.get_text() == "Getting Started":
+                        label.set_text(text)
+                    self.status_bar.show()
+                tab.get_children()[0].hide()
+                d = Dashboard(self.window, show_doc)
+                tab.pack_start(d, True, True, 0)
+                doc.connect("loaded", lambda x, y: show_doc())
+                d.search.set_receives_default(True)
+                self.window.set_default(d.search)
+                self.window.activate_default()
+                d.search.grab_focus()
+        self._is_adding = False
+
+    def do_deactivate(self):
+        tab = self.window.get_active_tab()
+        if tab:
+            notebook = tab.get_parent()
+            for tab in notebook.get_children():
+                for w in tab.get_children():
+                    if type(w) == Dashboard:
+                        tab.remove(w)
+                    else:
+                        w.show()
+                doc = tab.get_document()
+                label = notebook.get_tab_label(tab).get_children()\
+                    [0].get_children()[0].get_children()[2]
+                label.set_text(doc.get_short_name_for_display())
+
+        for handler in self._handlers:
+           self.window.disconnect(handler)
+        self.window = None
+
+# ex:ts=4:et:
diff --git a/plugins/dashboard/dashboard/dashboard.py b/plugins/dashboard/dashboard/dashboard.py
new file mode 100644
index 0000000..183b719
--- /dev/null
+++ b/plugins/dashboard/dashboard/dashboard.py
@@ -0,0 +1,474 @@
+#!/usr/bin/env python
+# -- coding: utf-8 --
+#
+# Copyright  2011 Collabora Ltd.
+#            By Seif Lotfy <seif lotfy com>
+#
+#  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.
+
+from gi.repository import GObject, Gedit, Gtk, Gio, GLib, GdkPixbuf, GtkSource
+from zeitgeist.client import ZeitgeistClient
+from zeitgeist.datamodel import Event, TimeRange
+from utils import *
+
+import time
+import os
+import urllib
+import dbus
+import datetime
+
+try:
+    BUS = dbus.SessionBus()
+except Exception:
+    BUS = None
+
+CLIENT = ZeitgeistClient()
+version = [int(x) for x in CLIENT.get_version()]
+MIN_VERSION = [0, 8, 0, 0]
+if version < MIN_VERSION:
+    print "PLEASE USE ZEITGEIST 0.8.0 or above"
+
+class Item(Gtk.Button):
+
+    def __init__(self, subject):
+
+        Gtk.Button.__init__(self)
+        self._file_object = Gio.file_new_for_uri(subject.uri)
+        self._file_info =\
+            self._file_object.query_info("standard::content-type",
+            Gio.FileQueryInfoFlags.NONE, None)
+        self.subject = subject
+        SIZE_LARGE = (256, 160)
+        self.thumb = create_text_thumb(self, SIZE_LARGE, 1)
+        self.set_size_request(224, 196)
+        vbox = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
+        vbox.pack_start(self.thumb, True, True, 0)
+        self.label = Gtk.Label()
+        self.label.set_markup("<span size='large'><b>%s</b></span>"\
+            %subject.text)
+        vbox.pack_start(self.label, False, False, 6)
+        self.add(vbox)
+
+    @property
+    def mime_type(self):
+        return self._file_info.get_attribute_string("standard::content-type")
+
+    def get_content(self):
+        f = open(self._file_object.get_path())
+        try:
+            content = f.read()
+        finally:
+            f.close()
+        return content
+
+
+class StockButton(Gtk.Button):
+
+    def __init__(self, stock, label):
+        Gtk.Button.__init__(self)
+        self.set_size_request(256, 196)
+        vbox = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
+        self.label = Gtk.Label()
+        self.label.set_markup("<span size='large'><b>%s</b></span>"%label)
+        vbox.pack_end(self.label, False, False, 6)
+        self.add(vbox)
+
+
+class DashView(Gtk.Box):
+
+    def __init__(self, show_doc, window):
+        Gtk.Box.__init__(self, orientation = Gtk.Orientation.VERTICAL)
+        hbox = Gtk.Box()
+        self._window = window
+        self.pack_start(Gtk.Label(""), True, True, 0)
+        self.pack_start(hbox, False, False, 0)
+        self.pack_start(Gtk.Label(""), True, True, 0)
+
+        self.grid = Gtk.Grid()
+        self.grid.set_row_homogeneous(True)
+        self.grid.set_column_homogeneous(True)
+
+        self.grid.set_column_spacing(15)
+        self.grid.set_row_spacing(15)
+        hbox.pack_start(Gtk.Label(""), True, True, 0)
+        hbox.pack_start(self.grid, False, False, 0)
+        hbox.pack_start(Gtk.Label(""), True, True, 0)
+
+        self.new_button = StockButton(Gtk.STOCK_NEW, _("Empty Document"))
+        self.new_button.connect("clicked", lambda x: show_doc())
+
+    def populate_grid(self, events):
+        for child in self.grid.get_children():
+            self.grid.remove(child)
+        self.hide()
+        subjects = []
+
+        self.grid.add(self.new_button)
+        self.show_all()
+
+        for event in events:
+            for subject in event.subjects:
+                if uri_exists(subject.uri):
+                    subjects.append(subject)
+                if len(subjects) == GRID_ITEM_COUNT:
+                    break
+            if len(subjects) == GRID_ITEM_COUNT:
+                break
+
+        for i, subject in enumerate(subjects):
+            item = Item(subjects[i])
+            item.connect("clicked", self.open)
+            self.grid.attach(item, (i + 1) % 4, (i + 1) / 4, 1, 1)
+        self.show_all()
+
+    def open(self, item):
+        Gedit.commands_load_location(self._window,
+            item._file_object, None, -1, -1)
+
+
+class DashPanelButton (Gtk.Box):
+
+    def __init__(self, label):
+        Gtk.Box.__init__(self, orientation = Gtk.Orientation.VERTICAL)
+        vbox = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
+        self.text = label
+        self.label = Gtk.Label()
+        self.label.set_markup("<b>%s</b>"%label)
+        self.label.set_alignment(0.5, 0.5)
+        self.button = Gtk.Button()
+        self.button.add(vbox)
+        self.button.set_relief(2)
+        self.pack_start(self.button, True, True, 0)
+        self.set_size_request(-1, 42)
+        self._active = False
+        self.connect("style-updated", self.change_style)
+        self.button.set_can_focus(False)
+        self.line = Gtk.EventBox()
+        self.line.set_size_request(-1, 3)
+        box = Gtk.Box()
+        box.pack_start(self.line, True, True, 4)
+        vbox.pack_start(Gtk.Box(), False, False, 6) #This is for styling
+        vbox.pack_start(self.label, False, False, 0)
+        vbox.pack_start(box, False, False, 1)
+
+    def set_active(self, active):
+        self._active = active
+        if self._active == True:
+            self.line.show()
+        else:
+            self.line.hide()
+
+    def change_style(self, widget):
+        self.style = self.get_style_context()
+        color = self.style.get_background_color(Gtk.StateFlags.SELECTED)
+        self.line.override_background_color(Gtk.StateFlags.NORMAL, color)
+
+
+class ZeitgeistFTS(object):
+
+    result_type_relevancy = 100
+
+    def __init__(self):
+        self.template = Event()
+        self.template.actor = "application://gedit.desktop"
+        self._fts = BUS.get_object('org.gnome.zeitgeist.Engine',
+            '/org/gnome/zeitgeist/index/activity')
+        self.fts = dbus.Interface(self._fts, 'org.gnome.zeitgeist.Index')
+
+    def search(self, text, callback):
+        events, count = self.fts.Search(text + "*", TimeRange.always(),
+            [self.template], 0, 100, 2)
+        callback(map(Event, events), count)
+
+ZG_FTS = ZeitgeistFTS()
+
+
+class SearchEntry(Gtk.Entry):
+
+    __gsignals__ = {
+        "clear": (GObject.SIGNAL_RUN_FIRST,
+                   GObject.TYPE_NONE,
+                   ()),
+        "search": (GObject.SIGNAL_RUN_FIRST,
+                    GObject.TYPE_NONE,
+                    (GObject.TYPE_STRING,)),
+        "close": (GObject.SIGNAL_RUN_FIRST,
+                   GObject.TYPE_NONE,
+                   ()),
+    }
+
+    search_timeout = 0
+
+    def __init__(self, accel_group = None):
+        Gtk.Entry.__init__(self)
+        self.set_width_chars(40)
+        self.set_placeholder_text(_("Type here to search..."))
+        self.connect("changed", lambda w: self._queue_search())
+
+        search_icon =\
+            Gio.ThemedIcon.new_with_default_fallbacks("edit-find-symbolic")
+        self.set_icon_from_gicon(Gtk.EntryIconPosition.PRIMARY, search_icon)
+        clear_icon =\
+            Gio.ThemedIcon.new_with_default_fallbacks("edit-clear-symbolic")
+        self.set_icon_from_gicon(Gtk.EntryIconPosition.SECONDARY, clear_icon)
+        self.connect("icon-press", self._icon_press)
+        self.show_all()
+
+    def _icon_press(self, widget, pos, event):
+        if event.button == 1 and pos == 1:
+            self.set_text("")
+            self.emit("clear")
+
+    def _queue_search(self):
+        if self.search_timeout != 0:
+            GObject.source_remove(self.search_timeout)
+            self.search_timeout = 0
+
+        if len(self.get_text()) == 0:
+            self.emit("clear")
+        else:
+            self.search_timeout = GObject.timeout_add(200,
+                self._typing_timeout)
+
+    def _typing_timeout(self):
+        if len(self.get_text()) > 0:
+            self.emit("search", self.get_text())
+
+        self.search_timeout = 0
+        return False
+
+
+class ListView(Gtk.TreeView):
+
+    def __init__(self):
+        Gtk.TreeView.__init__(self)
+        self.icontheme = Gtk.IconTheme.get_default()
+        self.model = Gtk.ListStore(str, GdkPixbuf.Pixbuf, str, str,
+            GObject.TYPE_PYOBJECT)
+
+        renderer_text = Gtk.CellRendererText()
+        column_text = Gtk.TreeViewColumn("Text", renderer_text, markup=0)
+        self.append_column(column_text)
+
+        renderer_pixbuf = Gtk.CellRendererPixbuf()
+        column_pixbuf = Gtk.TreeViewColumn("Image", renderer_pixbuf, pixbuf=1)
+        self.append_column(column_pixbuf)
+
+        renderer_text = Gtk.CellRendererText()
+        renderer_text.set_alignment(0.0, 0.5)
+        renderer_text.set_property('ellipsize', 1)
+        column_text = Gtk.TreeViewColumn("Text", renderer_text, markup=2)
+        column_text.set_expand(True)
+        self.append_column(column_text)
+
+        renderer_text = Gtk.CellRendererText()
+        renderer_text.set_alignment(1.0, 0.5)
+        column_text = Gtk.TreeViewColumn("Text", renderer_text, markup=3)
+        self.append_column(column_text)
+
+        self.style_get_property("horizontal-separator", 9)
+        self.set_headers_visible(False)
+        self.set_model(self.model)
+        self.home_path = "file://%s" %os.getenv("HOME")
+        self.now = time.time()
+
+    def get_time_string(self, timestamp):
+        timestamp = int(timestamp)/1000
+        diff = int(self.now - timestamp)
+        if diff < 60:
+            return "   few moment ago   "
+        diff = int(diff / 60)
+        if diff < 60:
+            if diff == 1:
+                return "   %i minute ago   " %int(diff)
+            return "   %i minutes ago   " %int(diff)
+        diff = int(diff / 60)
+        if diff < 24:
+            if diff == 1:
+                return "   %i hour ago   " %int(diff)
+            return "   %i hours ago   " %int(diff)
+        diff = int(diff / 24)
+        if diff < 4:
+            if diff == 1:
+                return "   Yesterday   "
+            return "   %i days ago   " %int(diff)
+
+        return datetime.datetime.fromtimestamp(timestamp)\
+            .strftime('   %d %B %Y   ')
+
+    def clear(self):
+        self.model.clear()
+        self.now = time.time()
+
+    def insert_results(self, events, match):
+        self.clear()
+        for event in events:
+            self.add_item(event, 48)
+
+    def add_item(self, event, icon_size):
+        item = event.subjects[0]
+        if uri_exists(item.uri):
+            uri = item.uri.replace(self.home_path, "Home").split("/")
+            uri = u" â ".join(uri[0:len(uri) - 1])
+            text = """<span size='large'><b>
+               %s\n</b></span><span color='darkgrey'>   %s
+               </span>"""%(item.text, uri)
+            iter = self.model.append(["   ", no_pixbuf, text,
+                "<span color='darkgrey'>   %s</span>"\
+                %self.get_time_string(event.timestamp), item])
+
+            def callback(icon):
+                self.model[iter][1] = icon if icon is not None else no_pixbuf
+            get_icon(item, callback, icon_size)
+
+
+class Dashboard (Gtk.Box):
+
+    def __init__(self, window, show_doc):
+        Gtk.Box.__init__(self, orientation = Gtk.Orientation.VERTICAL)
+        self._window = window
+        self._show_doc = show_doc
+        self.last_used_button = None
+        self.frequently_used_toggle_button = DashPanelButton(_("Most Used"))
+        self.recently_used_toggle_button = DashPanelButton(_("Recently Used"))
+        self._init_done = False
+
+        hbox1 = Gtk.Box()
+        hbox1.pack_start(self.recently_used_toggle_button, False, False, 0)
+        hbox1.pack_start(self.frequently_used_toggle_button, False, False, 0)
+
+        self.frequently_used_toggle_button.button.connect("clicked",
+            lambda x: self._toggle_view(self.frequently_used_toggle_button))
+        self.recently_used_toggle_button.button.connect("clicked",
+            lambda x: self._toggle_view(self.recently_used_toggle_button))
+
+        self.dash_panel_buttons = [self.frequently_used_toggle_button,
+            self.recently_used_toggle_button]
+
+        self.view = DashView(self._show_doc, window)
+
+        hbox = Gtk.Box()
+        hbox.pack_start(self.view, True, True, 9)
+
+        self.scrolledwindow = scrolledwindow = Gtk.ScrolledWindow()
+        scrolledwindow.add_with_viewport(hbox)
+        self.connect("style-updated", self.change_style)
+        self.search = SearchEntry()
+        hbox = Gtk.Box()
+        self.toolbar = toolbar = Gtk.Toolbar()
+        toolitem = Gtk.ToolItem()
+        toolitem.set_expand(True)
+        toolbar.insert(toolitem, 0)
+
+        vbox = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
+        vbox.pack_start(self.search, True, True, 6)
+
+        hbox.pack_start(Gtk.Label(""), True, True, 24) # purely cosmetic
+        hbox.pack_start(hbox1, True, True, 0)
+        hbox.pack_end(vbox, False, False, 9)
+
+        vbox = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
+        vbox.pack_start(hbox, False, False, 1)
+        separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
+        vbox.pack_start(separator, False, False, 1)
+        vbox.pack_start(Gtk.Box(), False, False, 0)
+        self.pack_start(vbox, False, False, 0)
+
+        self.dash_box = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
+        self.dash_box.pack_start(scrolledwindow, True, True, 0)
+        self.pack_start(self.dash_box, True, True, 0)
+
+        self.tree_view = ListView()
+        self.search_results_scrolled_window = Gtk.ScrolledWindow()
+        self.search_results_scrolled_window.add(self.tree_view)
+
+        self.search_result_box = Gtk.Box(orientation =\
+            Gtk.Orientation.VERTICAL)
+        self.search_result_box.pack_start(self.search_results_scrolled_window,
+            True, True, 0)
+        self.pack_end(self.search_result_box, True, True, 0)
+
+        self.get_recent()
+
+        self.search.connect("search", self._on_search)
+        self.search.connect("clear", self._on_clear)
+
+        self.show_all()
+        self.recently_used_toggle_button.set_active(True)
+        self.frequently_used_toggle_button.set_active(False)
+        self.last_used_button = self.recently_used_toggle_button
+
+        def open_uri(widget, row, cell):
+            Gedit.commands_load_location(self._window,
+                Gio.file_new_for_uri(widget.model[row][4].uri), None, 0, 0)
+        self.tree_view.connect("row-activated", open_uri)
+        self.search_result_box.hide()
+
+    def grab_focus(self):
+        self.search.grab_focus()
+
+    def _on_search(self, widget, query):
+        self.search_result_box.show()
+        self.dash_box.hide()
+        print "Dashboard search for:", query
+        ZG_FTS.search(query, self.tree_view.insert_results)
+        self.last_used_button.set_active(False)
+
+    def _on_clear(self, widget):
+        self.search_result_box.hide()
+        self.dash_box.show()
+        self.tree_view.clear()
+        self.last_used_button.set_active(True)
+
+    def change_style(self, widget):
+        self.style = self.get_style_context()
+        viewport = self.scrolledwindow.get_children()[0]
+        color = self.style.get_color(Gtk.StateFlags.SELECTED)
+        viewport.override_background_color(Gtk.StateFlags.NORMAL, color)
+        if self._init_done == False:
+            self.search.grab_focus()
+            self._init_done = True
+
+    def get_recent(self):
+        template = Event()
+        template.actor = "application://gedit.desktop"
+        CLIENT.find_events_for_templates([template], self.view.populate_grid,
+            num_events = 100, result_type = 2)
+
+    def get_frequent(self):
+        template = Event()
+        template.actor = "application://gedit.desktop"
+        now = time.time() * 1000
+        # 14 being the amount of days
+        # and 86400000 the amount of milliseconds per day
+        two_weeks_in_ms = 14 * 86400000
+        CLIENT.find_events_for_templates([template], self.view.populate_grid,
+            [now - two_weeks_in_ms, now], num_events = 100, result_type = 4)
+
+    def _toggle_view(self, widget):
+        for button in self.dash_panel_buttons:
+            if button == widget:
+                button.set_active(True)
+                self.last_used_button = button
+                self.search.set_text("")
+            else:
+                button.set_active(False)
+        if self.frequently_used_toggle_button._active:
+            self.get_frequent()
+        elif self.recently_used_toggle_button._active:
+            self.get_recent()
+
+# ex:ts=4:et:
diff --git a/plugins/dashboard/dashboard/utils.py b/plugins/dashboard/dashboard/utils.py
new file mode 100644
index 0000000..5661430
--- /dev/null
+++ b/plugins/dashboard/dashboard/utils.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+# -- coding: utf-8 --
+#
+# Copyright  2011 Collabora Ltd.
+#            By Seif Lotfy <seif lotfy com>
+#
+#  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.
+
+from gi.repository import GObject, Gtk, Gio, GLib, GdkPixbuf, GtkSource
+import os
+import urllib
+
+thumb_cache = {}
+pixbuf_cache = {}
+
+ICON_SIZES = SIZE_NORMAL, SIZE_LARGE = ((128, 128), (256, 256))
+
+# Thumbview and Timelineview sizes
+SIZE_THUMBVIEW = [(48, 36), (96, 72), (150, 108), (256, 192)]
+SIZE_TIMELINEVIEW = [(16, 12), (32, 24), (64, 48), (128, 96)]
+SIZE_TEXT_TIMELINEVIEW = ["small", "medium", "large", "x-large"]
+SIZE_TEXT_THUMBVIEW = ["x-small", "small", "large", "xx-large"]
+
+GRID_ITEM_COUNT = 7
+
+no_pixbuf = Gtk.IconTheme.get_default().load_icon("gtk-file", 48, 0)
+
+
+def uri_exists(uri):
+    return uri.startswith("note://") or \
+        os.path.exists(urllib.unquote(str(uri[7:])))
+
+
+def make_icon_frame(pixbuf, blend=False, border=1, color=0x000000ff):
+    """creates a new Pixmap which contains 'pixbuf' with a border at given
+    size around it."""
+    if not pixbuf:
+        return pixbuf
+    result = GdkPixbuf.Pixbuf.new(pixbuf.get_colorspace(), True,
+        pixbuf.get_bits_per_sample(), pixbuf.get_width(),
+        pixbuf.get_height())
+    result.fill(color)
+    pixbuf.copy_area(border, border, pixbuf.get_width() - (border * 2),
+        pixbuf.get_height() - (border * 2), result, border, border)
+    return result
+
+
+def create_text_thumb(gfile, size=None, threshold=2):
+    """ tries to use source_view to get a thumbnail of a text file """
+    content = gfile.get_content().split("\n")
+    new_content = []
+    last_line = ""
+    for line in content:
+        x = line.strip()
+        if not (x.startswith("/*") or x.startswith("*")\
+            or x.startswith("#") or x.startswith("import ")\
+            or x.startswith("from ") or x.startswith("#include ")\
+            or x.startswith("#define") or x.startswith("using")\
+            or x == "") and not last_line.endswith("\\"):
+            new_content.append(line)
+        else:
+            last_line = line
+    missing_lines = 30 - len(new_content)
+    if missing_lines > 0:
+        for i in xrange(missing_lines):
+            new_content.append(" \n")
+    content = "\n".join(new_content)
+    lang_manager = GtkSource.LanguageManager.get_default()
+    lang = lang_manager.guess_language(gfile.subject.uri[7:], None)
+    if lang:
+        buf = GtkSource.Buffer.new_with_language(lang)
+    else:
+        buf = GtkSource.Buffer()
+    buf.set_highlight_syntax(True)
+    buf.set_text(content)
+    source_view = GtkSource.View.new_with_buffer(buf)
+    overlay = Gtk.Overlay()
+    overlay.add(Gtk.EventBox())
+    overlay.add_overlay(source_view)
+    event_box = Gtk.EventBox()
+    event_box.add(overlay)
+    return event_box
+
+
+def __crop_pixbuf(pixbuf, x, y, size=SIZE_LARGE):
+    """ returns a part of the given pixbuf as new one """
+    result = GdkPixbuf.Pixbuf.new(pixbuf.get_colorspace(),
+        True,
+        pixbuf.get_bits_per_sample(),
+        size[0], size[1])
+    pixbuf.copy_area(x, y, x + size[0], y + size[1], result, 0, 0)
+    return result
+
+
+def get_pixbuf_from_cache(gicon, callback, size=48):
+    name = gicon.to_string()
+    if name in pixbuf_cache:
+        callback(pixbuf_cache[name])
+    else:
+        theme = Gtk.IconTheme.get_default()
+        # blocks: do something with it
+        gtk_icon = theme.lookup_by_gicon(gicon, size,
+            Gtk.IconLookupFlags.FORCE_SIZE)
+        if gtk_icon:
+            icon = gtk_icon.load_icon()
+        else:
+            icon = None
+        pixbuf_cache[name] = icon
+        callback(icon)
+
+
+def get_icon(item, callback, size=48):
+    uri = item.uri
+    f = Gio.file_new_for_uri(uri)
+
+    def query_info_callback(gfile, result, cancellable):
+        info = f.query_info_finish(result)
+
+        def pixbuf_loaded_cb(pixbuf):
+            if pixbuf is None:
+                return
+            icon = pixbuf.scale_simple(size * pixbuf.get_width()\
+                / pixbuf.get_height(), size, 2)
+            callback(icon)
+
+
+        gicon = info.get_icon()
+        get_pixbuf_from_cache(gicon, pixbuf_loaded_cb, size)
+
+
+    if f.is_native():
+        attr = Gio.FILE_ATTRIBUTE_STANDARD_ICON + ","\
+            + Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH
+
+        def cancel_cb():
+            pass
+
+        f.query_info_async(attr, Gio.FileQueryInfoFlags.NONE,
+            GLib.PRIORITY_DEFAULT, None, query_info_callback, None)
+
+
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 159430a..fed09ae 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -8,6 +8,8 @@ plugins/codecomment/codecomment.py
 plugins/colorpicker/colorpicker.plugin.desktop.in.in
 plugins/colorpicker/colorpicker.py
 plugins/commander/commander.plugin.desktop.in.in
+plugins/dashboard/dashboard.plugin.desktop.in.in
+plugins/dashboard/dashboard/dashboard.py
 plugins/drawspaces/drawspaces.plugin.desktop.in.in
 [type: gettext/glade]plugins/drawspaces/gedit-drawspaces-plugin.ui
 plugins/drawspaces/gedit-drawspaces-plugin.c



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]