← Back to team overview

cairo-dock-team team mailing list archive

[Merge] lp:~eduardo-mucelli/cairo-dock-plug-ins-extras/WebSearch into lp:cairo-dock-plug-ins-extras

 

Eduardo Mucelli Rezende Oliveira has proposed merging lp:~eduardo-mucelli/cairo-dock-plug-ins-extras/WebSearch into lp:cairo-dock-plug-ins-extras.

Requested reviews:
  Cairo-Dock Team (cairo-dock-team)

For more details, see:
https://code.launchpad.net/~eduardo-mucelli/cairo-dock-plug-ins-extras/WebSearch/+merge/90973

Finally fixing Youtube search. Fixing a problem when opening URLs.
-- 
https://code.launchpad.net/~eduardo-mucelli/cairo-dock-plug-ins-extras/WebSearch/+merge/90973
Your team Cairo-Dock Team is requested to review the proposed merge of lp:~eduardo-mucelli/cairo-dock-plug-ins-extras/WebSearch into lp:cairo-dock-plug-ins-extras.
=== modified file 'WebSearch/Changelog.txt'
--- WebSearch/Changelog.txt	2011-11-17 21:49:26 +0000
+++ WebSearch/Changelog.txt	2012-01-31 23:03:24 +0000
@@ -1,6 +1,7 @@
-1.4.2: (July/13/2010: Simplifying the dependencies. Fixing Wikipedia search.
-1.4.0: (July/13/2010: WebSearch now keeps a history of recently searched terms.
-1.3.1: (June/29/2010: URL encoding was not being done. Fixed it.
+1.4.3: (January/31/2012): Fixing Youtube search. Fixing a problem when opening URLs.
+1.4.2: (November/17/2011): Simplifying the dependencies. Fixing Wikipedia search.
+1.4.0: (July/13/2010): WebSearch now keeps a history of recently searched terms.
+1.3.1: (June/29/2010): URL encoding was not being done. Fixed it.
 1.3.0: (May/28/2010): WebSearch now fetch results from Digg. Changing the thumb download directory handling.
 1.2.1: (May/23/2010): Working around a bug in Ruby-dbus to construct list of sub-icons which description contains encoded characters.
 1.2.0: (May/21/2010): WebSearch now fetch results from Twitter. Fixing a bug that I introduced in the code refactoring, forget that some engines use page, and some use offset. Modularizing the engines. Shortening the description the same way as the url because some tweets are very long. Engine class now uses a neat way for lazy-loading and engine instantiation.

=== modified file 'WebSearch/WebSearch'
--- WebSearch/WebSearch	2010-07-13 16:02:17 +0000
+++ WebSearch/WebSearch	2012-01-31 23:03:24 +0000
@@ -17,362 +17,371 @@
 
 # This applet provides an interface to some search engines such as
 # Google, Bing, Teoma, Yahoo!, Youtube, Webshots, Flickr, Wikipedia, ImageShack, and Twitter.
-#	To choose the search engine you can
-#	 	(1) Right-click on the main icon -> WebSearch -> (Choose the engine)
-#	 	(2) Right-click -> Configure this applet -> Configuration -> Search engine
-#	 	(3) Scroll up or down over the icon (applicable only for the first search)
-#	 You can search in two ways
-#		(1) Middle-click on the main icon
-#		(2) Left-click on main icon (right after choosing a new engine)
+#  To choose the search engine you can
+#     (1) Right-click on the main icon -> WebSearch -> (Choose the engine)
+#     (2) Right-click -> Configure this applet -> Configuration -> Search engine
+#     (3) Scroll up or down over the icon (applicable only for the first search)
+#   You can search in two ways
+#    (1) Middle-click on the main icon
+#    (2) Left-click on main icon (right after choosing a new engine)
 #    Type your query and validate. Each result will be shown as an sub-icon.
-#	 Left-click to open the the result in the default web browser
-# 	 Middle-click on the sub-icon of any result to show its description
+#   Left-click to open the the result in the default web browser
+#    Middle-click on the sub-icon of any result to show its description
 #    Scroll up to fetch the next results
-#	 Scroll down to fetch the previous results
-#	 Left-click on the main icon to show search stats
+#   Scroll down to fetch the previous results
+#   Left-click on the main icon to show search stats
 
-%w{rubygems open-uri nokogiri dbus parseconfig launchy}.each { |x| require x }			# requirements
+%w{rubygems open-uri nokogiri dbus parseconfig launchy}.each { |x| require x }    # requirements
 
 class Array
-	def fifth;self[4];end																# defining the method "fifth" just for code readability
-	def third;self[2];end																# defining the method "third" just for code readability
+  def fifth;self[4];end                                                           # defining the method "fifth" just for code readability
+  def third;self[2];end                                                           # defining the method "third" just for code readability
 end
 
 class String
-	def to_b;"true" == downcase;end														# string to boolean
+  def to_b;"true" == downcase;end                                                 # string to boolean
+  def starts_with?(prefix)
+    prefix = prefix.to_s
+    self[0, prefix.length] == prefix
+  end
 end
 
 module WebSearch
 
-	def self.name
-		File.basename(Dir.getwd)														# nome do applet, neste caso é o mesmo do diretorio
-	end
-
-	def self.start
-		bus = DBus::SessionBus.instance													# TODO: a module to encapsulate DBus-Dock connection
-		applet_service = bus.service("org.cairodock.CairoDock")
-		applet_path = "/org/cairodock/CairoDock/#{WebSearch.name}"						# caminho onde o objeto está guardado no bus
-
-		applet_object = applet_service.object(applet_path)
-		applet_object.introspect
-		applet_object.default_iface = 'org.cairodock.CairoDock.applet'					# list of icons contained in our sub-dock, or in our desklet
-
-		applet_sub_icons_object = applet_service.object("#{applet_path}/sub_icons")
-		applet_sub_icons_object.introspect
-		applet_sub_icons_object.default_iface = 'org.cairodock.CairoDock.subapplet' 	# list of icons contained in our sub-dock, or in our desklet
-
-		applet = Applet.new applet_object, applet_sub_icons_object
-		applet.start
-		loop = DBus::Main.new
-		loop << bus
-		loop.run
-	end
-
-	class Applet
-
-		require './lib/Engine.rb'
-		require './lib/History.rb'
-
-		attr_accessor 	:engine, :engines, :query, :history,
-						:number_of_fetched_links, :number_of_displayed_links, :page_of_displayed_links,
-						:show_current_page, :show_description_instead_url, :show_thumbnail_preview,
-						:scroll_engine_index
-
-		DialogActiveTime = 5															# time in seconds that the dialog window will be active
-
-		def initialize applet, sub_icons
-			self.query = ""
-			self.engines = Engines.list												    # ./lib/Engines.rb
-            self.history = History.entries                                              # ./lib/History.rb
-			self.engine = Engine.new
-			self.scroll_engine_index = 0												# current index when scrolling through search engines
-			@icon = applet
-			@sub_icons = sub_icons
-			reset_search_settings
-			set_configuration_parameters
-		end
-
-		def set_configuration_parameters
-			conf = ParseConfig.new(File.expand_path("~/.config/cairo-dock/current_theme/plug-ins/#{WebSearch.name}/#{WebSearch.name}.conf"))
-			# for parameters within a list, the value is the position in the options list, not the value by itself
-			self.number_of_fetched_links = [10, 20, 30, 50, 100].at(conf.params['Configuration']['number of fetched links'].to_i)
-			self.engine.name = self.engines.at(conf.params['Configuration']['engine'].to_i)
-			inform_current_search_engine												# inform in bottom of the icon what is the new engine		
-			self.number_of_displayed_links = conf.params['Configuration']['number of displayed links'].to_i # number of sub-icons to be shown
-			self.show_current_page = conf.params['Configuration']['show current page'].to_b
-			self.show_description_instead_url = conf.params['Configuration']['show description instead url'].to_b
-			self.show_thumbnail_preview = conf.params['Configuration']['show thumbnail preview'].to_b
-		end
-	
-		def start
-			verify_user_action
-		end
-
-		# Signal handling
-		def verify_user_action
-			@icon.on_signal("on_build_menu") do |param|									# right click signal
-				action_on_build_menu
-			end
-			@icon.on_signal("on_menu_select") do |selected_menu|
-				action_on_menu_select selected_menu
-			end
-			@icon.on_signal("on_answer") do |answer|
-				action_on_answer answer
-			end
-			@icon.on_signal("on_scroll") do |scroll_up|									# when the user scroll the mouse up or down on the icon
-				action_on_scroll scroll_up												# scroll down param = false, scroll up param = true
-			end
-			@icon.on_signal("on_middle_click") do |param|
-				ask_for_search_query
-			end
-			@icon.on_signal("on_click") do |param|
-				action_on_click
-			end
-			@icon.on_signal("on_reload_module") do |config_has_changed|
-				action_on_reload_module config_has_changed
-			end
-			@sub_icons.on_signal("on_click_sub_icon") do |param, sub_icon_id|
-				action_on_click_sub_icon sub_icon_id
-			end
-			@sub_icons.on_signal("on_middle_click_sub_icon") do |sub_icon_id|
-				action_on_middle_click_sub_icon sub_icon_id
-			end
-		end
+  def self.name
+    File.basename(Dir.getwd)                                                      # applet's name, the same name as the directory
+  end
+
+  def self.start
+    bus = DBus::SessionBus.instance                                               # TODO: a module to encapsulate DBus-Dock connection
+    applet_service = bus.service("org.cairodock.CairoDock")
+    applet_path = "/org/cairodock/CairoDock/#{WebSearch.name}"                    # caminho onde o objeto está guardado no bus
+
+    applet_object = applet_service.object(applet_path)
+    applet_object.introspect
+    applet_object.default_iface = 'org.cairodock.CairoDock.applet'                # list of icons contained in our sub-dock, or in our desklet
+
+    applet_sub_icons_object = applet_service.object("#{applet_path}/sub_icons")
+    applet_sub_icons_object.introspect
+    applet_sub_icons_object.default_iface = 'org.cairodock.CairoDock.subapplet'   # list of icons contained in our sub-dock, or in our desklet
+
+    applet = Applet.new applet_object, applet_sub_icons_object
+    applet.start
+    loop = DBus::Main.new
+    loop << bus
+    loop.run
+  end
+
+  class Applet
+
+    require './lib/Engine.rb'
+    require './lib/History.rb'
+
+    attr_accessor :engine, :engines, :query, :history,
+                  :number_of_fetched_links, :number_of_displayed_links, :page_of_displayed_links,
+                  :show_current_page, :show_description_instead_url, :show_thumbnail_preview,
+                  :scroll_engine_index
+
+    DialogActiveTime = 5                                                          # time in seconds that the dialog window will be active
+
+    def initialize applet, sub_icons
+      self.query = ""
+      self.engines = Engines.list                                                 # ./lib/Engines.rb
+      self.history = History.entries                                              # ./lib/History.rb
+      self.engine = Engine.new
+      self.scroll_engine_index = 0                                                # current index when scrolling through search engines
+      @icon = applet
+      @sub_icons = sub_icons
+      reset_search_settings
+      set_configuration_parameters
+    end
+
+    def set_configuration_parameters
+      conf = ParseConfig.new(File.expand_path("~/.config/cairo-dock/current_theme/plug-ins/#{WebSearch.name}/#{WebSearch.name}.conf"))
+      # for parameters within a list, the value is the position in the options list, not the value by itself
+      self.number_of_fetched_links = [10, 20, 30, 50, 100].at(conf.params['Configuration']['number of fetched links'].to_i)
+      self.engine.name = self.engines.at(conf.params['Configuration']['engine'].to_i)
+      inform_current_search_engine                        # inform in bottom of the icon what is the new engine    
+      self.number_of_displayed_links = conf.params['Configuration']['number of displayed links'].to_i # number of sub-icons to be shown
+      self.show_current_page = conf.params['Configuration']['show current page'].to_b
+      self.show_description_instead_url = conf.params['Configuration']['show description instead url'].to_b
+      self.show_thumbnail_preview = conf.params['Configuration']['show thumbnail preview'].to_b
+    end
+  
+    def start
+      verify_user_action
+    end
+
+    # Signal handling
+    def verify_user_action
+      @icon.on_signal("on_build_menu") do |param|                                 # right click signal
+        action_on_build_menu
+      end
+      @icon.on_signal("on_menu_select") do |selected_menu|
+        action_on_menu_select selected_menu
+      end
+      @icon.on_signal("on_answer") do |answer|
+        action_on_answer answer
+      end
+      @icon.on_signal("on_scroll") do |scroll_up|                                 # when the user scroll the mouse up or down on the icon
+        action_on_scroll scroll_up                                                # scroll down param = false, scroll up param = true
+      end
+      @icon.on_signal("on_middle_click") do |param|
+        ask_for_search_query
+      end
+      @icon.on_signal("on_click") do |param|
+        action_on_click
+      end
+      @icon.on_signal("on_reload_module") do |config_has_changed|
+        action_on_reload_module config_has_changed
+      end
+      @sub_icons.on_signal("on_click_sub_icon") do |param, sub_icon_id|
+        action_on_click_sub_icon sub_icon_id
+      end
+      @sub_icons.on_signal("on_middle_click_sub_icon") do |sub_icon_id|
+        action_on_middle_click_sub_icon sub_icon_id
+      end
+    end
         
         # Building context menu
-		def action_on_build_menu
-			begin
-				@icon.AddMenuItems(build_menu_for_engines)
-                @icon.AddMenuItems(build_menu_for_history)
-			rescue NoMethodError														# Cairo-Dock < 2.1.4-0beta0
-				WebSearch.log "AddMenuItems method is not available"
-				@icon.PopulateMenu(self.engines)
-			end
-		end
-
-        def build_menu_for_engines
-            items = []
-			self.engines.each_with_index do |engine, i|									# each property must do be string and never use Symbol
-			    item = {}
-				item['type'] = 0
-				item['label'] = engine
-				item['menu'] = 1
-				item['id'] = i
-				item['icon'] = File.expand_path("./images/data/#{engine}.png")
-				items.push item
-			end
-            items
-        end
-
-        def build_menu_for_history
-            # The engines' index (id) of the WebSearch sub-menu ranges from 0 to self.engines.size-1 consequently, the first id of
-            # the history menu is the self.engines.size. Even if the number of engines change, the self.engine size will keep track of it
-            history_sub_menu_index = self.engines.size
-            history_sub_menu_icon = File.expand_path("./images/data/history.png")
-            history_sub_menu = [{'type' => 1, 'label' => 'History', 'menu' => 0, 'id' => history_sub_menu_index, 'icon' => history_sub_menu_icon}]
-            history_sub_menu_entry_index = history_sub_menu_index                       # subsequent indexes are used in the history entries
-            self.history.each do |entry|                                                # construct the history sub-menu entries
-                item = {}
-                item['type'] = 0
-				item['label'] = entry.chop
-				item['menu'] = history_sub_menu_index
-                history_sub_menu_entry_index += 1
-				item['id'] = history_sub_menu_entry_index
-                item['icon'] = history_sub_menu_icon
-				history_sub_menu.push item
-            end
-            history_sub_menu
-        end
-
-		def ask_for_search_query
-			@icon.AskText("Search for:", "#{self.query}")                               # the value in the text field is the previous used term
-		end
-
-		def action_on_answer answer
-			unless answer.empty?
-				reset_search_settings unless self.query.empty?
-                History.save_new_entry answer                                           # append (if necessary) the query term in the history file
-                self.history = History.entries                                          # refresh local entries of the historyu
-				self.query = answer
-				begin
-					self.engine = self.engine.connect									# only when the fetch is imminent the engine connection occurs
-				rescue Exceptions::UnknownEngineException => e
-					WebSearch.log e.message
-				else
-					fetch_next_resulting_page
-				end
-			end
-		end
-
-		def reset_search_settings
-			self.engine.links =[]														# .clear cant be used yet
-			self.engine.stats = ""
-			self.page_of_displayed_links = 0											# current pagination of displayed links
-			Link.reset_next_id
-			ThumbnailedLink.reset_next_image_id
-			@sub_icons.RemoveSubIcon("any")
-		end
-
-		def action_on_click_sub_icon sub_icon_id
-			Launchy.open self.engine.links.at(sub_icon_id.to_i-1).url
-		end
-
-		def action_on_middle_click_sub_icon sub_icon_id
-			text = ""
-			if self.show_description_instead_url										# sub-icons are entitled by description ...
-				text = self.engine.links.at(sub_icon_id.to_i-1).url						# so URL will be shown in dialog
-			else																		# sub-icons are entitled by url ...
-				text = self.engine.links.at(sub_icon_id.to_i-1).description				# so description will be shown in dialog
-			end					
-			@icon.ShowDialog(text, DialogActiveTime)
-		end
-
-		def action_on_click
-			if self.engine.stats.empty?
-				ask_for_search_query
-			else
-				@icon.ShowDialog(self.engine.stats, DialogActiveTime) 
-			end
-		end
-
-        # There is a sequential index in the context menu and it is divided in two sections
-        #   Section I - Indexes for search engines ranging from 0 to self.engines.size - 1
-        #   Section II - Indexes for history terms' entries ranging from self.engines.size + 1 to max number of entries in the history (NumberOfEntries see ./lib/History.rb)
-        #   Note - The index self.engines.size is the one used to place the "History" label actually creating the sub-menu. See first lines in  build_menu_for_history method
-        # This method treats both the selection of search engine, as trigger a search with a term coming from the history
-		def action_on_menu_select selected_menu_index
-            # an index that came from a click in one of the engines from WebSearch sub-menu
-            if selected_menu_index < self.engines.size
-    			switch_search_engine selected_menu_index
-            else    # user can be only clicked on the History sub-menu
-                # shift the history menu ids to make them range from 0 to self.history-1 (History.entries from ./lib/History.rb)
-                history_entry_term = self.history[selected_menu_index - self.engines.size - 1]
-                action_on_answer history_entry_term
-            end
-		end
-
-		def action_on_reload_module config_has_changed
-			set_configuration_parameters if config_has_changed
-		end
-
-		# Scrolling can switch the search engine, or fetch another resulting page
-		def action_on_scroll scroll_up
-			if self.query.empty?														# before the first query it is possible scroll through engines
-				if scroll_up
-					switch_search_engine self.scroll_engine_index +=1					# drawback: user scrolls a lot for up/down and this variable
-				else																	# gets a value far from (0..self.engines.size-1) limits.
-					switch_search_engine self.scroll_engine_index -=1					# user need to scroll back a lot to get in these limits again
-				end
-			else																		# later the first query scroll through the resulting pages
-				if scroll_up
-					fetch_next_resulting_page
-				else
-					fetch_previous_resulting_page
-				end
-			end
-		end
-
-		def switch_search_engine index
-			index = 0 if index < 0														# keep the lower limit
-			index = self.engines.size - 1 if index > self.engines.size - 1			# keep the upper limit
-			self.engine.name = self.engines.at(index)
-			reset_search_settings														# clean the previous search when choosing a new one
-			inform_current_search_engine												# inform in the bottom of the icon what is the new engine
-		end
-
-		def fetch_next_resulting_page
-			offset = self.page_of_displayed_links * self.number_of_displayed_links		# the position of the first link in the self.engine.links array
-			if self.engine.links.size <= offset											# user already scrolled by the fetched links, fetch more
-				inform_start_of_waiting_process
-                # some engines use the concept of offset which is the first index of an interval of links/images to be shown
-				# but there is those that use a sequential page (1,2,3, ...) which has an amount of links/images, etc
-                if Engines.paginated_by_page?(self.engine.name)                         # see ./lib/Engines.rb
-                    self.engine.links = self.engine.retrieve_links(self.query, self.page_of_displayed_links + 1)
-                else																	# paginated by offset
-                    self.engine.links = self.engine.retrieve_links(self.query, offset)
-                end
-				inform_end_of_waiting_process
-		    end
-			self.page_of_displayed_links += 1											# sequential page identification, lets go to the next
-			sub_icon_list = construct_sub_icon_list(offset)
-			refresh_sub_icon_list (sub_icon_list)
-			inform_current_page
-		end
-
-		# Since the previous results are already stored in self.engine.links, it is necessary just to 
-		# select the its interval that starts with the first link of the previous page.
-		# An easier approach would be to query the engine again with page-1 but it would result
-		# more queries to the page, consequently it has some drawbacks such as increasing the 
-		# probability of forbidden mechanized access, more bandwith, etc.
-		def fetch_previous_resulting_page
-			if self.page_of_displayed_links > 1											# there is no previous page from the first one
-				self.page_of_displayed_links -= 1										# one page back
-				inicio = (self.page_of_displayed_links-1) * self.number_of_displayed_links	# the first position of the link in the previous page
-				sub_icon_list = construct_sub_icon_list(inicio)
-				refresh_sub_icon_list (sub_icon_list)
-			end
-			inform_current_page
-		end
-
-		# Construct the menu using a set of fetched links
-		# Links can have thumbnails
-		def construct_sub_icon_list inicio
-			sub_icon_list =[]
-			inform_start_of_waiting_process
-			threads =[]
-			self.engine.links[inicio, self.number_of_displayed_links].each do |link|	# first let's download the thumbs if necessary
-				if link.instance_of?(ThumbnailedLink) and not link.downloaded_thumb?	# class that provides thumbs and a not yet downloaded thumb
-					if self.show_thumbnail_preview 										# user want to see thumbs, so let's get it
-						threads << Thread.new {
-							link.download_thumbnail
-						}
-					end
-				end
-			end
-			threads.each {|t| t.join}
-			self.engine.links[inicio, self.number_of_displayed_links].each do |link|	# later, get the rest of sub-icons data
-				if self.show_description_instead_url
-					sub_icon_list << link.description									# user prefer see description with the sub-icon
-				else
-					sub_icon_list << link.shortened_url									# user prefer see shortened url with the sub-icon
-				end
-				sub_icon_list << link.icon												# the icon
-				sub_icon_list << link.id.to_s											# the sequential id
-			end
-			inform_end_of_waiting_process
-			sub_icon_list
-		end
-
-		def refresh_sub_icon_list sub_icon_list
-			@sub_icons.RemoveSubIcon("any")												# remove all rendered sub-icons
-			@sub_icons.AddSubIcons(sub_icon_list)
-		end
-
-		def inform_start_of_waiting_process
-			@icon.SetQuickInfo("...")
-		end
-
-		def inform_end_of_waiting_process
-			@icon.SetQuickInfo("")
-		end
-
-		def inform_current_page
-			if self.show_current_page
-				@icon.SetQuickInfo("#{self.page_of_displayed_links}")
-			else
-				@icon.SetQuickInfo("")
-			end
-		end
-	
-		# Inform in the bottom of the icon what is new search engine
-		def inform_current_search_engine
-	 		@icon.SetQuickInfo(self.engine.name)
-		end
-	end
-
-    def self.log(msg)
-		$stderr.puts "WEBSEARCH_DEBUG: #{msg}"
-    end
+    def action_on_build_menu
+      begin
+        @icon.AddMenuItems(build_menu_for_engines)
+        @icon.AddMenuItems(build_menu_for_history)
+      rescue NoMethodError                            # Cairo-Dock < 2.1.4-0beta0
+        WebSearch.log "AddMenuItems method is not available"
+        @icon.PopulateMenu(self.engines)
+      end
+    end
+
+    def build_menu_for_engines
+      items = []
+      self.engines.each_with_index do |engine, i|                                 # each property must do be string and never use Symbol
+        item = {}
+        item['type'] = 0
+        item['label'] = engine
+        item['menu'] = 1
+        item['id'] = i
+        item['icon'] = File.expand_path("./images/data/#{engine}.png")
+        items.push item
+      end
+      items
+    end
+
+    def build_menu_for_history
+      # The engines' index (id) of the WebSearch sub-menu ranges from 0 to self.engines.size-1 consequently, the first id of
+      # the history menu is the self.engines.size. Even if the number of engines change, the self.engine size will keep track of it
+      history_sub_menu_index = self.engines.size
+      history_sub_menu_icon = File.expand_path("./images/data/history.png")
+      history_sub_menu = [{'type' => 1, 'label' => 'History', 'menu' => 0, 'id' => history_sub_menu_index, 'icon' => history_sub_menu_icon}]
+      history_sub_menu_entry_index = history_sub_menu_index                       # subsequent indexes are used in the history entries
+      self.history.each do |entry|                                                # construct the history sub-menu entries
+        item = {}
+        item['type'] = 0
+        item['label'] = entry.chop
+        item['menu'] = history_sub_menu_index
+        history_sub_menu_entry_index += 1
+        item['id'] = history_sub_menu_entry_index
+        item['icon'] = history_sub_menu_icon
+        history_sub_menu.push item
+      end
+      history_sub_menu
+    end
+
+    def ask_for_search_query
+      @icon.AskText("Search for:", "#{self.query}")                               # the value in the text field is the previous used term
+    end
+
+    def action_on_answer answer
+      unless answer.empty?
+        reset_search_settings unless self.query.empty?
+        History.save_new_entry answer                                             # append (if necessary) the query term in the history file
+        self.history = History.entries                                            # refresh local entries of the historyu
+        self.query = answer
+        begin
+          self.engine = self.engine.connect                                       # only when the fetch is imminent the engine connection occurs
+        rescue Exceptions::UnknownEngineException => e
+          WebSearch.log e.message
+        else
+          fetch_next_resulting_page
+        end
+      end
+    end
+
+    def reset_search_settings
+      self.engine.links =[]                                                       # .clear cant be used yet
+      self.engine.stats = ""
+      self.page_of_displayed_links = 0                                            # current pagination of displayed links
+      Link.reset_next_id
+      ThumbnailedLink.reset_next_image_id
+      @sub_icons.RemoveSubIcon("any")
+    end
+
+    def action_on_click_sub_icon sub_icon_id
+      url = self.engine.links.at(sub_icon_id.to_i-1).url
+      unless url.starts_with?("http://";)
+        Launchy.open("http://#{url}";)
+      else
+        Launchy.open url
+      end
+    end
+
+    def action_on_middle_click_sub_icon sub_icon_id
+      text = ""
+      if self.show_description_instead_url                                        # sub-icons are entitled by description ...
+        text = self.engine.links.at(sub_icon_id.to_i-1).url                       # so URL will be shown in dialog
+      else                                                                        # sub-icons are entitled by url ...
+        text = self.engine.links.at(sub_icon_id.to_i-1).description               # so description will be shown in dialog
+      end          
+      @icon.ShowDialog(text, DialogActiveTime)
+    end
+
+    def action_on_click
+      if self.engine.stats.empty?
+        ask_for_search_query
+      else
+        @icon.ShowDialog(self.engine.stats, DialogActiveTime) 
+      end
+    end
+
+    # There is a sequential index in the context menu and it is divided in two sections
+    #   Section I - Indexes for search engines ranging from 0 to self.engines.size - 1
+    #   Section II - Indexes for history terms' entries ranging from self.engines.size + 1 to max number of entries in the history (NumberOfEntries see ./lib/History.rb)
+    #   Note - The index self.engines.size is the one used to place the "History" label actually creating the sub-menu. See first lines in  build_menu_for_history method
+    # This method treats both the selection of search engine, as trigger a search with a term coming from the history
+    def action_on_menu_select selected_menu_index
+      # an index that came from a click in one of the engines from WebSearch sub-menu
+      if selected_menu_index < self.engines.size
+        switch_search_engine selected_menu_index
+      else    # user can be only clicked on the History sub-menu
+        # shift the history menu ids to make them range from 0 to self.history-1 (History.entries from ./lib/History.rb)
+        history_entry_term = self.history[selected_menu_index - self.engines.size - 1]
+        action_on_answer history_entry_term
+      end
+    end
+
+    def action_on_reload_module config_has_changed
+      set_configuration_parameters if config_has_changed
+    end
+
+    # Scrolling can switch the search engine, or fetch another resulting page
+    def action_on_scroll scroll_up
+      if self.query.empty?                                                        # before the first query it is possible scroll through engines
+        if scroll_up
+          switch_search_engine self.scroll_engine_index +=1                       # drawback: user scrolls a lot for up/down and this variable
+        else                                                                      # gets a value far from (0..self.engines.size-1) limits.
+          switch_search_engine self.scroll_engine_index -=1                       # user need to scroll back a lot to get in these limits again
+        end
+      else                                                                        # later the first query scroll through the resulting pages
+        if scroll_up
+          fetch_next_resulting_page
+        else
+          fetch_previous_resulting_page
+        end
+      end
+    end
+
+    def switch_search_engine index
+      index = 0 if index < 0                                                      # keep the lower limit
+      index = self.engines.size - 1 if index > self.engines.size - 1              # keep the upper limit
+      self.engine.name = self.engines.at(index)
+      reset_search_settings                                                       # clean the previous search when choosing a new one
+      inform_current_search_engine                                                # inform in the bottom of the icon what is the new engine
+    end
+
+    def fetch_next_resulting_page
+      offset = self.page_of_displayed_links * self.number_of_displayed_links      # the position of the first link in the self.engine.links array
+      if self.engine.links.size <= offset                                         # user already scrolled by the fetched links, fetch more
+        inform_start_of_waiting_process
+        # some engines use the concept of offset which is the first index of an interval of links/images to be shown
+        # but there is those that use a sequential page (1,2,3, ...) which has an amount of links/images, etc
+        if Engines.paginated_by_page?(self.engine.name)                           # see ./lib/Engines.rb
+          self.engine.links = self.engine.retrieve_links(self.query, self.page_of_displayed_links + 1)
+        else                                  # paginated by offset
+          self.engine.links = self.engine.retrieve_links(self.query, offset)
+        end
+        inform_end_of_waiting_process
+        end
+      self.page_of_displayed_links += 1                                           # sequential page identification, lets go to the next
+      sub_icon_list = construct_sub_icon_list(offset)
+      refresh_sub_icon_list (sub_icon_list)
+      inform_current_page
+    end
+
+    # Since the previous results are already stored in self.engine.links, it is necessary just to 
+    # select the its interval that starts with the first link of the previous page.
+    # An easier approach would be to query the engine again with page-1 but it would result
+    # more queries to the page, consequently it has some drawbacks such as increasing the 
+    # probability of forbidden mechanized access, more bandwith, etc.
+    def fetch_previous_resulting_page
+      if self.page_of_displayed_links > 1                                         # there is no previous page from the first one
+        self.page_of_displayed_links -= 1                                         # one page back
+        inicio = (self.page_of_displayed_links-1) * self.number_of_displayed_links  # the first position of the link in the previous page
+        sub_icon_list = construct_sub_icon_list(inicio)
+        refresh_sub_icon_list (sub_icon_list)
+      end
+      inform_current_page
+    end
+
+    # Construct the menu using a set of fetched links
+    # Links can have thumbnails
+    def construct_sub_icon_list inicio
+      sub_icon_list =[]
+      inform_start_of_waiting_process
+      threads =[]
+      self.engine.links[inicio, self.number_of_displayed_links].each do |link|    # first let's download the thumbs if necessary
+        if link.instance_of?(ThumbnailedLink) and not link.downloaded_thumb?      # class that provides thumbs and a not yet downloaded thumb
+          if self.show_thumbnail_preview                                          # user want to see thumbs, so let's get it
+            threads << Thread.new {
+              link.download_thumbnail
+            }
+          end
+        end
+      end
+      threads.each {|t| t.join}
+      self.engine.links[inicio, self.number_of_displayed_links].each do |link|    # later, get the rest of sub-icons data
+        if self.show_description_instead_url
+          sub_icon_list << link.description                                       # user prefer see description with the sub-icon
+        else
+          sub_icon_list << link.shortened_url                                     # user prefer see shortened url with the sub-icon
+        end
+        sub_icon_list << link.icon                                                # the icon
+        sub_icon_list << link.id.to_s                                             # the sequential id
+      end
+      inform_end_of_waiting_process
+      sub_icon_list
+    end
+
+    def refresh_sub_icon_list sub_icon_list
+      @sub_icons.RemoveSubIcon("any")                                             # remove all rendered sub-icons
+      @sub_icons.AddSubIcons(sub_icon_list)
+    end
+
+    def inform_start_of_waiting_process
+      @icon.SetQuickInfo("...")
+    end
+
+    def inform_end_of_waiting_process
+      @icon.SetQuickInfo("")
+    end
+
+    def inform_current_page
+      if self.show_current_page
+        @icon.SetQuickInfo("#{self.page_of_displayed_links}")
+      else
+        @icon.SetQuickInfo("")
+      end
+    end
+  
+    # Inform in the bottom of the icon what is new search engine
+    def inform_current_search_engine
+      @icon.SetQuickInfo(self.engine.name)
+    end
+  end
+
+  def self.log(msg)
+    $stderr.puts "WEBSEARCH_DEBUG: #{msg}"
+  end
 
 end
 

=== modified file 'WebSearch/WebSearch.conf'
--- WebSearch/WebSearch.conf	2011-11-17 21:49:26 +0000
+++ WebSearch/WebSearch.conf	2012-01-31 23:03:24 +0000
@@ -1,4 +1,4 @@
-#1.4.2
+#1.4.3
 
 #[gtk-about]
 [Icon]

=== modified file 'WebSearch/auto-load.conf'
--- WebSearch/auto-load.conf	2011-11-17 21:49:26 +0000
+++ WebSearch/auto-load.conf	2012-01-31 23:03:24 +0000
@@ -10,4 +10,4 @@
 category = 3
 
 # Version of the applet; change it everytime you change something in the config file. Don't forget to update the version both in this file and in the config file.
-version = 1.4.2
+version = 1.4.3

=== modified file 'WebSearch/lib/Youtube.rb'
--- WebSearch/lib/Youtube.rb	2010-07-13 16:02:17 +0000
+++ WebSearch/lib/Youtube.rb	2012-01-31 23:03:24 +0000
@@ -18,7 +18,7 @@
 	def retrieve_links(query, page = 1)
 		youtube = Nokogiri::HTML(open(URI.encode("#{self.query_url}#{query}&page=#{page}")))
 		self.stats = retrieve_youtube_result_stats(youtube, query)
-		(youtube/"a[@id^='video-long-title-']").each do |res|							# 'a' tag has id which starts with "video-long-title-"
+		(youtube/"h3[@id^='video-long-title-']/a").each do |res|							# 'a' tag has id which starts with "video-long-title-"
 			url = res['href']
 			description = res.inner_text
 			video_id = url.split('=').last												# /watch?v=WwojCsQ3Fa8 => WwojCsQ3Fa8 => video_id
@@ -29,7 +29,7 @@
 	end
 
 	def retrieve_youtube_result_stats (youtube, query)
-		total = youtube.at("div[@class='name']").inner_text.split.last
+		total = youtube.at("p[@class='num-results']/strong").inner_text
 		"Search for #{query} returned #{total} results"
 	end
 end


Follow ups