diff options
author | Pierre GRANDIN <grandinp@altern.org> | 2016-01-20 10:15:55 -0800 |
---|---|---|
committer | Pierre GRANDIN <grandinp@altern.org> | 2016-01-20 10:15:55 -0800 |
commit | 0a9e9f8e3080ebaba6368dcf3b831d57dd57785d (patch) | |
tree | 775981ea1ea75be01973cfd96345793f615983ef | |
parent | 86df00e1313d5b0351452f0e0ee7a7750fd06223 (diff) | |
download | navit-0a9e9f8e3080ebaba6368dcf3b831d57dd57785d.tar.gz |
Add:gui_internal:Added Googleplaces search from the internal gui
-rwxr-xr-x | CMakeLists.txt | 10 | ||||
-rw-r--r-- | cmake/FindJansson.cmake | 59 | ||||
-rw-r--r-- | config.h.cmake | 2 | ||||
-rw-r--r-- | navit/CMakeLists.txt | 2 | ||||
-rw-r--r-- | navit/gui/internal/CMakeLists.txt | 11 | ||||
-rw-r--r-- | navit/gui/internal/gui_internal_command.c | 7 | ||||
-rw-r--r-- | navit/gui/internal/gui_internal_googlesearch.c | 298 | ||||
-rw-r--r-- | navit/gui/internal/gui_internal_googlesearch.h | 2 | ||||
-rw-r--r-- | navit/network.c | 148 | ||||
-rw-r--r-- | navit/network.h | 15 | ||||
-rw-r--r-- | navit/xpm/googleplaces.svg | 146 |
11 files changed, 697 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a08365d1f..ad5435724 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,6 +148,7 @@ find_package(DBusGLib) find_package(PythonLibs) find_package(OpenSSL) find_package(Threads) +find_package(Jansson) libfind_pkg_check_modules(FONTCONFIG fontconfig) #Qt detection if (NOT DISABLE_QT) @@ -437,6 +438,15 @@ add_feature(USE_ROUTING "default" TRUE) add_feature(USE_SVG "default" TRUE) add_feature(SVG2PNG "default" TRUE) add_feature(SAMPLE_MAP "default" TRUE) +add_feature(USE_GOOGLEPLACES "default" FALSE) + +if (JANSSON_FOUND) + set_with_reason(USE_GOOGLEPLACES "jansson found" FALSE) + list(APPEND NAVIT_LIBS "-ljansson -lcurl") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -u fetch_url_to_string ") +else (JANSSON_FOUND) + set_with_reason(USE_GOOGLEPLACES "jansson not found" FALSE) +endif (JANSSON_FOUND) IF(NOT svg2png_scaling) IF(NOT ANDROID) diff --git a/cmake/FindJansson.cmake b/cmake/FindJansson.cmake new file mode 100644 index 000000000..32259232f --- /dev/null +++ b/cmake/FindJansson.cmake @@ -0,0 +1,59 @@ +# - Try to find Jansson +# Once done this will define +# +# JANSSON_FOUND - system has Jansson +# JANSSON_INCLUDE_DIRS - the Jansson include directory +# JANSSON_LIBRARIES - Link these to use Jansson +# +# Copyright (c) 2011 Lee Hambley <lee.hambley@gmail.com> +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +if (JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS) + # in cache already + set(JANSSON_FOUND TRUE) +else (JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS) + find_path(JANSSON_INCLUDE_DIR + NAMES + jansson.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ) + +find_library(JANSSON_LIBRARY + NAMES + jansson + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ) + +set(JANSSON_INCLUDE_DIRS + ${JANSSON_INCLUDE_DIR} + ) + +if (JANSSON_LIBRARY) + set(JANSSON_LIBRARIES + ${JANSSON_LIBRARIES} + ${JANSSON_LIBRARY} + ) +endif (JANSSON_LIBRARY) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Jansson DEFAULT_MSG + JANSSON_LIBRARIES JANSSON_INCLUDE_DIRS) + + # show the JANSSON_INCLUDE_DIRS and JANSSON_LIBRARIES variables only in the advanced view + mark_as_advanced(JANSSON_INCLUDE_DIRS JANSSON_LIBRARIES) + +endif (JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS) + + diff --git a/config.h.cmake b/config.h.cmake index abc3d0ebc..e4aeb597a 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -92,3 +92,5 @@ #cmakedefine HAVE_SHMEM 1 #cmakedefine HAVE_IMLIB2 1 + +#cmakedefine USE_GOOGLEPLACES 1 diff --git a/navit/CMakeLists.txt b/navit/CMakeLists.txt index 5333c9ff4..db51d5d7a 100644 --- a/navit/CMakeLists.txt +++ b/navit/CMakeLists.txt @@ -6,7 +6,7 @@ include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/support") # navit cre set(NAVIT_SRC announcement.c atom.c attr.c cache.c callback.c command.c config_.c coord.c country.c data_window.c debug.c - event.c file.c geom.c graphics.c gui.c item.c layout.c log.c main.c map.c maps.c + event.c file.c geom.c graphics.c gui.c item.c layout.c log.c main.c map.c maps.c network.c linguistics.c mapset.c maptype.c menu.c messages.c bookmarks.c navit.c navit_nls.c navigation.c osd.c param.c phrase.c plugin.c popup.c profile.c profile_option.c projection.c roadprofile.c route.c script.c search.c speech.c start_real.c sunriset.c transform.c track.c search_houseno_interpol.c util.c vehicle.c vehicleprofile.c xmlconfig.c ) diff --git a/navit/gui/internal/CMakeLists.txt b/navit/gui/internal/CMakeLists.txt index 230e315d6..2bc656e23 100644 --- a/navit/gui/internal/CMakeLists.txt +++ b/navit/gui/internal/CMakeLists.txt @@ -1 +1,10 @@ -module_add_library(gui_internal gui_internal.c gui_internal_bookmark.c gui_internal_command.c gui_internal_gesture.c gui_internal_html.c gui_internal_menu.c gui_internal_poi.c gui_internal_search.c gui_internal_widget.c gui_internal_keyboard.c) +if(USE_GOOGLEPLACES) + set(api_LIBS "-ljansson -lpthread") + set(googleplaces_sources gui_internal_googlesearch.c ) +endif(USE_GOOGLEPLACES) +set(gui_internal_LIBS ${api_LIBS}) +module_add_library(gui_internal ${googleplaces_sources} + gui_internal.c gui_internal_bookmark.c + gui_internal_command.c gui_internal_gesture.c gui_internal_html.c + gui_internal_menu.c gui_internal_poi.c gui_internal_search.c + gui_internal_widget.c gui_internal_keyboard.c) diff --git a/navit/gui/internal/gui_internal_command.c b/navit/gui/internal/gui_internal_command.c index 98a736fb8..45461cccf 100644 --- a/navit/gui/internal/gui_internal_command.c +++ b/navit/gui/internal/gui_internal_command.c @@ -36,6 +36,9 @@ #include "gui_internal_search.h" #include "gui_internal_poi.h" #include "gui_internal_command.h" +#ifdef USE_GOOGLEPLACES +#include "gui_internal_googlesearch.h" +#endif extern char *version; @@ -1226,7 +1229,9 @@ static struct command_table commands[] = { {"waypoints",command_cast(gui_internal_cmd2)}, {"write",command_cast(gui_internal_cmd_write)}, {"about",command_cast(gui_internal_cmd2)}, - +#ifdef USE_GOOGLEPLACES + {"googlesearch_search", command_cast (gui_internal_googlesearch_search)}, +#endif }; void diff --git a/navit/gui/internal/gui_internal_googlesearch.c b/navit/gui/internal/gui_internal_googlesearch.c new file mode 100644 index 000000000..2099a48af --- /dev/null +++ b/navit/gui/internal/gui_internal_googlesearch.c @@ -0,0 +1,298 @@ +/* vim: set tabstop=8 expandtab: */ +#include <glib.h> +#include <navit/main.h> +#include <navit/debug.h> +#include <navit/point.h> +#include <navit/navit.h> +#include <navit/callback.h> +#include <navit/color.h> +#include <navit/event.h> +#include <navit/command.h> +#include <navit/config_.h> +#include <navit/transform.h> + +#include "gui_internal.h" +#include "coord.h" +#include "math.h" +#include "gui_internal_menu.h" +#include "gui_internal_widget.h" +#include "gui_internal_priv.h" +#include "gui_internal_googlesearch.h" +#include "network.h" +#include <time.h> // Benchmarking the multithreading code, to be removed +#include <pthread.h> + +#include "jansson.h" + + +/* + * Implements searching from Googleplaces. + * To use : + * - add your own googleplaces API key below + * - add something like <img src='googleplaces' onclick='googlesearch_search()'><text>GooglePlaces</text></img> to your navit.xml GUI declaration + */ +char *googleplaces_apikey = ""; + +struct googleplace { + char * id; + char * name; + struct pcoord c; + struct coord_geo g; + struct gui_priv *gui_priv; + struct widget *wm; +}; + +/** + * @brief Fetches the details about a given place + * @param[in] id - the googleplace id + * + * @return googleplace - an object containing the details about the place + * + * Fetches the details about a given place + * + */ +struct googleplace +fetch_googleplace_details(char * id) +{ + char url[256]; + strcpy (url, g_strdup_printf ("https://maps.googleapis.com/maps/api/place/details/json?key=%s&placeid=%s",googleplaces_apikey,id)); + dbg(lvl_error,"Url %s\n", url); + + json_t *root; + json_error_t error; + char * item_js= fetch_url_to_string(url); + dbg(lvl_info,"%s\n",item_js); + root = json_loads (item_js, 0, &error); + free(item_js); + if(!root) + { + dbg(lvl_error,"Invalid json for url %s, giving up for place id %s\n",url,id); + json_decref (root); + return; + } + + struct googleplace gp; + json_t *result, *geometry, *location, *name; + result = json_object_get (root, "result"); + geometry = json_object_get (result, "geometry"); + name = json_object_get (result, "name"); + location = json_object_get (geometry, "location"); + gp.g.lat = json_real_value (json_object_get (location, "lat")); + gp.g.lng = json_real_value (json_object_get (location, "lng")); + gp.c.pro=projection_mg; + struct coord c; + transform_from_geo (projection_mg, &gp.g, &c); + gp.c.x=c.x; + gp.c.y=c.y; + dbg(lvl_error, "Item %s as at : %4.16f x %4.16f [ %x x %x ]\n", json_string_value(name), gp.g.lat, gp.g.lng, gp.c.x, gp.c.y); + gp.name=g_strdup(json_string_value(name)); + json_decref (root); + return gp; +} + +/** + * @brief Set the destination to a given place, read from a widget + * @param[in] this - the current gui_priv object + * wm - the widget containing the place reference + * data - container for extra datas. Not used here + * + * @return nothing + * + * Set the destination to a given place, read from a widget + * + */ +static void +googlesearch_set_destination (struct gui_priv *this, struct widget *wm, void *data) +{ + struct googleplace gp=fetch_googleplace_details(wm->name); + dbg(lvl_error, "%s c=%d:0x%x,0x%x [ %4.16f x %4.16f }\n", gp.name, gp.c.pro, gp.c.x, gp.c.y, gp.g.lat, gp.g.lng); + navit_set_destination (this->nav, &gp.c, gp.name, 1); + gui_internal_prune_menu (this, NULL); +} + +/** + * @brief Updates the search results list, autocompletion + * @param[in] this - the current gui_priv object + * wm - the widget containing the text input + * data - container for extra datas. Not used here + * + * @return nothing + * + * Updates the search results list, refining the search as the user types + * + */ +static void +gui_internal_cmd_googlesearch_filter_do(struct gui_priv *this, struct widget *wm, void *data) +{ + struct widget *w=data; + + if(!w->text) + return; + + char *prefix = 0; + char track_icon[64]; + struct coord_geo g; + + struct transformation *trans; + trans = navit_get_trans (this->nav); + struct coord c; + // We want the search to be relative to where we clicked on the map + c.x = this->clickp.x; + c.y = this->clickp.y; + + transform_to_geo (transform_get_projection (trans), &c, &g); + + dbg(lvl_info, "googlesearch called for %d x %d, converted to %4.16f x %4.16f\n", wm->c.x, wm->c.y, g.lat, g.lng); + + char *baseurl = "https://maps.googleapis.com/maps/api/place/autocomplete/json?"; + char url[256]; + char lat_string[50]; + snprintf (lat_string, 50, "%f", g.lat); + char lng_string[50]; + snprintf (lng_string, 50, "%f", g.lng); + char *radius="20000"; + + strcpy (url, g_strdup_printf ("%slocation=%s,%s&key=%s&input=%s&radius=%s", baseurl, lat_string, lng_string, googleplaces_apikey, w->text, radius)); + + char * js=fetch_url_to_string(url); + + json_t *root; + json_error_t error; + root = json_loads (js, 0, &error); + + json_t *response, *venues; + response = json_object_get (root, "response"); + venues = json_object_get (root, "predictions"); + + struct timespec now, tmstart; + clock_gettime(CLOCK_REALTIME, &tmstart); + + int i = 0; + + // The autocomplete API returns max 5 results + pthread_t thread_id[5]; + // struct googleplace gp[5]; + for (i = 0; i < json_array_size (venues); i++) + { + json_t *venue, *description, *id; + venue = json_array_get (venues, i); + description = json_object_get (venue, "description"); + id = json_object_get (venue, "place_id"); + + dbg(lvl_info, "Found [%i] %s with id %s\n", i, json_string_value (description), json_string_value (id)); + strcpy (track_icon, "default"); +#if 0 + /* Threads were not efficient on a mobile connection. They have been deactivated */ + gp[i].id=g_strdup(json_string_value (id)); + gp[i].description=g_strdup(json_string_value (description)); + gp[i].gui_priv=this; + gp[i].wm=wm; + pthread_create( &thread_id[i], NULL, &fetch_googleplace_details_as_thread, &gp[i] ); +#endif + + struct widget *wtable=gui_internal_menu_data(this)->search_list; + struct widget *wc; + struct widget *row; + gui_internal_widget_append (wtable, row = + gui_internal_widget_table_row_new (this, + gravity_left | orientation_horizontal | flags_fill)); + gui_internal_widget_append (row, wc = + gui_internal_button_new_with_callback (this, + json_string_value(description), + image_new_xs (this, "gui_active"), + gravity_left_center | orientation_horizontal | flags_fill, + googlesearch_set_destination, + NULL)); + + wc->selection_id = wm->selection_id; + wc->name = g_strdup (json_string_value(id)); + wc->c.pro = projection_mg; + wc->prefix = g_strdup (wm->prefix); + } +#if 0 + int j; + for(j=0; j < json_array_size (venues); j++) + { + dbg(lvl_info,"Checking thread #%i with p:%p\n",j,thread_id[j]); + pthread_join( thread_id[j], NULL); + } +#endif + clock_gettime(CLOCK_REALTIME, &now); + + double seconds = (double)((now.tv_sec+now.tv_nsec*1e-9) - (double)(tmstart.tv_sec+tmstart.tv_nsec*1e-9)); + dbg(lvl_error,"wall time %fs\n", seconds); + gui_internal_menu_render(this); + g_free (prefix); + json_decref (root); +} + +/** + * @brief Callback called when the user types in the search box + * @param[in] this - the current gui_priv object + * wm - the parent widget + * data - container for extra datas. Not used here + * + * @return nothing + * + * This callback is called everytime the user types in the search box + * + */ +static void +gui_internal_cmd_google_filter_changed(struct gui_priv *this, struct widget *wm, void *data) +{ +// if (wm->text && wm->reason==gui_internal_reason_keypress_finish) { +// gui_internal_cmd_googlesearch_filter_do(this, wm, wm); +// } + if (wm->text) { + gui_internal_widget_table_clear(this, gui_internal_menu_data(this)->search_list); + gui_internal_cmd_googlesearch_filter_do(this, wm, wm); + } +} + +/** + * @brief Builds the Googleplaces search menu + * @param[in] this - the current gui_priv object + * wm - the parent widget + * data - container for extra datas. Not used here + * + * @return nothing + * + * Builds the Googleplaces search menu + * + */ +void +gui_internal_googlesearch_search(struct gui_priv *this, struct widget *wm, void *data) +{ + struct widget *wb, *w, *wr, *wk, *we, *wl; + int keyboard_mode; + keyboard_mode=2+gui_internal_keyboard_init_mode(getenv("LANG")); + wb=gui_internal_menu(this,"Search"); + w=gui_internal_box_new(this, gravity_center|orientation_vertical|flags_expand|flags_fill); + gui_internal_widget_append(wb, w); + wr=gui_internal_box_new(this, gravity_top_center|orientation_vertical|flags_expand|flags_fill); + gui_internal_widget_append(w, wr); + we=gui_internal_box_new(this, gravity_left_center|orientation_horizontal|flags_fill); + gui_internal_widget_append(wr, we); + + gui_internal_widget_append(we, wk=gui_internal_label_new(this, NULL)); + wk->state |= STATE_EDIT|STATE_EDITABLE; + wk->func=gui_internal_cmd_google_filter_changed; + wk->background=this->background; + wk->flags |= flags_expand|flags_fill; + wk->name=g_strdup("POIsFilter"); + wk->c=wm->c; + dbg(lvl_error, "googlesearch filter called for %d x %d\n", this->clickp.x, this->clickp.y); + gui_internal_widget_append(we, wb=gui_internal_image_new(this, image_new_xs(this, "gui_active"))); + wb->state |= STATE_SENSITIVE; + wb->func = gui_internal_cmd_googlesearch_filter_do; + wb->name=g_strdup("NameFilter"); + wb->c=this->clickp; + wb->data=wk; + wl=gui_internal_widget_table_new(this,gravity_left_top | flags_fill | flags_expand |orientation_vertical,1); + gui_internal_widget_append(wr, wl); + gui_internal_menu_data(this)->search_list=wl; + + if (this->keyboard) + gui_internal_widget_append(w, gui_internal_keyboard(this,keyboard_mode)); + gui_internal_menu_render(this); +} diff --git a/navit/gui/internal/gui_internal_googlesearch.h b/navit/gui/internal/gui_internal_googlesearch.h new file mode 100644 index 000000000..88372cbdc --- /dev/null +++ b/navit/gui/internal/gui_internal_googlesearch.h @@ -0,0 +1,2 @@ +void googlesearch_navit_init(); +void gui_internal_googlesearch_search(struct gui_priv *this, struct widget *wm, void *data); diff --git a/navit/network.c b/navit/network.c new file mode 100644 index 000000000..3683dd5de --- /dev/null +++ b/navit/network.c @@ -0,0 +1,148 @@ +#include "curl/curl.h" +#include <navit/debug.h> +#include "unistd.h" +#include "stddef.h" + +struct string { + char *ptr; + size_t len; +}; + + /** + * @brief Initialize a "string" object + * + * @param string the struct string pointer + * @returns nothing + */ +void +init_string(struct string *s) +{ + s->len = 0; + s->ptr = malloc(s->len + 1); + if (s->ptr == NULL) { + dbg(lvl_error, "malloc() failed\n"); + } + s->ptr[0] = '\0'; +} + + /** + * @brief store the incoming data into a dynamically + * growing allocated buffer. Typical when using libcurl. + * + * @param ptr pointer to the delivered data + * @param size size of the delivered data + * @param nmemb number of mem blocks + * @param s CURLOPT_WRITEDATA option + * @returns the size of the buffer after expansion + */ +size_t +writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) +{ + size_t new_len = s->len + size * nmemb; + s->ptr = realloc(s->ptr, new_len + 1); + if (s->ptr == NULL) { + dbg(lvl_error, "realloc() failed\n"); + } + memcpy(s->ptr + s->len, ptr, size * nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + + return size * nmemb; +} + + /** + * @brief Replace all occurrences of the search string with the replacement string + * From http://creativeandcritical.net/str-replace-c/ + * + * @param str the string to replace in + * @param old the content to replace + * @param new the new content + * @returns the updated string + */ +char * +replace_str(const char *str, const char *old, const char *new) +{ + char *ret, *r; + const char *p, *q; + size_t oldlen = strlen(old); + size_t count, retlen, newlen = strlen(new); + + if (oldlen != newlen) { + for (count = 0, p = str; (q = strstr(p, old)) != NULL; + p = q + oldlen) + count++; + /* this is undefined if p - str > PTRDIFF_MAX */ + retlen = p - str + strlen(p) + count * (newlen - oldlen); + } else + retlen = strlen(str); + + if ((ret = malloc(retlen + 1)) == NULL) + return NULL; + + for (r = ret, p = str; (q = strstr(p, old)) != NULL; + p = q + oldlen) { + /* this is undefined if q - p > PTRDIFF_MAX */ + ptrdiff_t l = q - p; + memcpy(r, p, l); + r += l; + memcpy(r, new, newlen); + r += newlen; + } + strcpy(r, p); + + return ret; +} + + + /** + * @brief fetch the content from a url + * + * @param url the url to fetch content from + * @returns the content of the webpage at the given url + */ +char * +fetch_url_to_string(char *url) +{ + struct string s; + char *sanitized_url = replace_str(url, " ", "%20"); + init_string(&s); + CURL *curl; + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, sanitized_url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + dbg(lvl_info, "url %s gave %s\n", sanitized_url, s.ptr); + dbg(lvl_error, "url %s gave js of size %i\n", sanitized_url, + strlen(s.ptr)); + free(sanitized_url); + return strdup(s.ptr); +} + + /** + * @brief perform a http/post and returns the result + * + * @param url the url, including the encoded parameters + * @returns the html response + */ +char * +post_to_string(char *url) +{ + struct string s; + init_string(&s); + CURL *curl; + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_perform(curl); + free(s.ptr); + curl_easy_cleanup(curl); + return g_strdup(s.ptr); +} diff --git a/navit/network.h b/navit/network.h new file mode 100644 index 000000000..147f8f94c --- /dev/null +++ b/navit/network.h @@ -0,0 +1,15 @@ +#ifndef NAVIT_NETWORK_H +#define NAVIT_NETWORK_H + +#ifdef __cplusplus +extern "C" { +#endif + +char * fetch_url_to_string(char * url); +char * post_to_string(char * url); +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/navit/xpm/googleplaces.svg b/navit/xpm/googleplaces.svg new file mode 100644 index 000000000..3b1062065 --- /dev/null +++ b/navit/xpm/googleplaces.svg @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="256" + height="256" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.47 r22583" + sodipodi:docname="google.svg" + version="1.0" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + id="linearGradient2555"> + <stop + style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" + offset="0" + id="stop2557" /> + <stop + style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" + offset="1" + id="stop2559" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2555" + id="linearGradient2449" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.5914583,0,0,0.5914584,210.0216,142.2324)" + x1="-344.15295" + y1="274.711" + x2="-395.84943" + y2="425.39993" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.35" + inkscape:cx="351.23943" + inkscape:cy="155.4078" + inkscape:document-units="px" + inkscape:current-layer="layer1" + inkscape:window-width="853" + inkscape:window-height="674" + inkscape:window-x="1" + inkscape:window-y="281" + showgrid="false" + inkscape:window-maximized="0" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + <dc:creator> + <cc:Agent> + <dc:title>User:ZyMOS</dc:title> + </cc:Agent> + </dc:creator> + <dc:subject> + <rdf:Bag /> + </dc:subject> + <cc:license + rdf:resource="http://creativecommons.org/licenses/publicdomain/" /> + <dc:description /> + <dc:contributor> + <cc:Agent> + <dc:title /> + </cc:Agent> + </dc:contributor> + <dc:publisher> + <cc:Agent> + <dc:title>Open Icon Library</dc:title> + </cc:Agent> + </dc:publisher> + </cc:Work> + <cc:License + rdf:about="http://creativecommons.org/licenses/publicdomain/"> + <cc:permits + rdf:resource="http://creativecommons.org/ns#Reproduction" /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#Distribution" /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-373.642,-318.344)"> + <rect + inkscape:export-ydpi="7.7063322" + inkscape:export-xdpi="7.7063322" + inkscape:export-filename="C:\Documents and Settings\Molumen\Desktop\path3511111.png" + transform="scale(-1,1)" + ry="35.487503" + rx="35.487503" + y="328.84921" + x="-619.14587" + height="234.98955" + width="235.00784" + id="rect1942" + style="fill:#194fc8;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.87500000000000000;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.87500000000000000, 1.75000000000000000;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:export-ydpi="7.7063322" + inkscape:export-xdpi="7.7063322" + inkscape:export-filename="C:\Documents and Settings\Molumen\Desktop\path3511111.png" + sodipodi:nodetypes="ccccsssc" + id="path1950" + d="M 557.05665,338.89518 L 446.22721,338.89518 C 416.89033,338.89518 393.27256,362.70492 393.27256,392.28025 L 393.27256,500.40761 C 394.22216,523.49366 397.87485,508.89915 404.82758,483.3329 C 412.90814,453.61975 439.22406,427.65003 471.27219,408.1872 C 495.73352,393.33195 523.11328,383.84595 572.95174,382.94353 C 601.21656,382.43177 598.72124,346.26062 557.05665,338.89518 z" + style="opacity:1;fill:url(#linearGradient2449);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.87500000000000000;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.87500000000000000, 1.75000000000000000;stroke-dashoffset:0;stroke-opacity:1" /> + <text + xml:space="preserve" + style="font-size:232.84083557px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:3.00000024;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Arial;-inkscape-font-specification:Arial" + x="441.18173" + y="480.17572" + id="text4746" + sodipodi:linespacing="125%" + transform="scale(1.0013237,0.99867803)"><tspan + sodipodi:role="line" + id="tspan4748" + x="441.18173" + y="480.17572" + style="font-size:232.84083557px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:3.00000024;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Georgia;-inkscape-font-specification:Georgia">g</tspan></text> + </g> +</svg> |