commit 7546eccf050e4b0ff311b542e108c4f18098b209
parent e99e9a6e3635b7eb1b0a6ca7f9d4600bbc5ac957
Author: Suraj N. Kurapati <sunaku@gmail.com>
Date: Wed, 20 Sep 2006 20:50:09 -0700
[project @ bf86df1262574c8e44b5beaefdb21048c8acccca]
[project @ 115]
moved file test methods from Ixp into Ruby-IXP
changed Ixp::Node's method_missing behavior: nodes are only dereferenced when suffix is !
made show_menu() return nil if nothing chosen... this simplifies handling of result
Diffstat:
fs.rb | | | 110 | ++++++++++++++++++++++++++----------------------------------------------------- |
rc.rb | | | 36 | +++++++++++++++++------------------- |
wm.rb | | | 113 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
wmiirc-config.rb | | | 22 | ++++++++++++---------- |
4 files changed, 121 insertions(+), 160 deletions(-)
diff --git a/fs.rb b/fs.rb
@@ -23,143 +23,105 @@ require 'ixp'
# Encapsulates access to the IXP file system.
module Ixp
- @@ixp = IXP::Client.new unless defined? @@ixp
-
- # Creates a file at the given path.
- def self.create aPath
- @@ixp.create aPath
- end
-
- # Deletes the given path.
- def self.remove aPath
- @@ixp.remove aPath
- end
-
- # Writes the given content to the given path.
- def self.write aPath, aContent
- open(aPath) do |f|
- f.write aContent.to_s
- end
- end
-
- # Reads from the given path and returns the content. If the path is a directory, then the names of all files in that directory are returned.
- def self.read aPath
- open(aPath) do |f|
- if f.is_a? IXP::Directory
- names = []
-
- while i = f.next
- names << i.name
- end
-
- names
-
- else # read file contents
- f.read
- end
- end
- end
-
- # Tests if the given path is a file.
- def self.file? aPath
- open(aPath) {|f| f.instance_of? IXP::File} rescue false
- end
-
- # Tests if the given path is a directory.
- def self.directory? aPath
- open(aPath) {|f| f.instance_of? IXP::Directory} rescue false
- end
-
- # Tests if the given path exists.
- def self.exist? aPath
- open(aPath) {true} rescue false
- end
-
- # Opens the given path for reading and writing and passes it to the given block.
- def self.open aPath, &aBlock # :yields: IO
- @@ixp.open aPath, &aBlock
- end
+ Client = IXP::Client.new
# An entry in the IXP file system.
class Node
attr_reader :path
- # Obtains the IXP node at the given path. If aCreateIt is asserted, then the given path is created unless it already exists.
+ # Obtains the IXP node at the given path. Unless it already exists, the given path is created when aCreateIt is asserted.
def initialize aPath, aCreateIt = false
@path = aPath.to_s.squeeze('/')
create! if aCreateIt && !exist?
end
# Open this node for IO operation.
- def open &aBlock # :yields: IO
- Ixp.open @path, &aBlock
+ def open *aArgs, &aBlock # :yields: IO
+ Client.open @path, *aArgs, &aBlock
end
# Creates this node.
def create!
- Ixp.create @path
+ Client.create @path
end
# Deletes this node.
def remove!
- Ixp.remove @path
+ Client.remove @path
end
# Writes the given content to this node.
def write! aContent
- Ixp.write @path, aContent
+ Client.write @path, aContent
end
- # Returns the contents of this node or the names of all sub-nodes if this is a directory.
+ # Returns the contents of this node or the names of all entries if this is a directory.
def read
- Ixp.read @path
+ cont = Client.read(@path)
+
+ if cont.respond_to? :to_ary
+ cont.map {|stat| stat.name}
+ else
+ cont
+ end
end
# Tests if this node is a file.
def file?
- Ixp.file? @path
+ Client.file? @path
end
# Tests if this node is a directory.
def directory?
- Ixp.directory? @path
+ Client.directory? @path
end
# Tests if this node exists in the file system.
def exist?
- Ixp.exist? @path
+ Client.exist? @path
end
+ # Returns the basename of this file's path.
def basename
File.basename @path
end
+ # Returns the dirname of this file's path.
def dirname
File.dirname @path
end
- # Accesses the given sub-path. When aDeref is asserted, then the contents of the sub-path are returned if it is a file.
- def [] aSubPath, aDeref = true
- child = Ixp::Node.new("#{@path}/#{aSubPath}")
+ # Accesses the given sub-path and dereferences it (reads its contents) if specified.
+ def [] aSubPath, aDeref = false
+ child = Node.new("#{@path}/#{aSubPath}")
- if aDeref && child.file?
+ if aDeref
child.read
else
child
end
end
- # Writes to the given sub-path.
+ # Writes the given content to the given sub-path.
def []= aSubPath, aContent
- Ixp::Node.new("#{@path}/#{aSubPath}").write! aContent
+ self[aSubPath].write! aContent
end
- # Provides easy access to sub-nodes.
+ # Provides access to sub-nodes through method calls.
+ #
+ # :call-seq:
+ # node.child = value -> value
+ # node.child -> Node
+ # node.child! -> child.read
+ #
def method_missing aMeth, *aArgs
case aMeth.to_s
when /=$/
self[$`] = *aArgs
+ when /!$/
+ self[$`, true]
+
else
self[aMeth]
end
diff --git a/rc.rb b/rc.rb
@@ -32,33 +32,32 @@ def find_programs *aPaths
end
end
- list.uniq.sort
+ list.uniq!
+ list.sort!
+ list
end
-# Shows a menu with the given items and returns the chosen item. If nothing was chosen, an empty string is returned.
+# Shows a menu with the given items and returns the chosen item. If nothing was chosen, *nil* is returned.
def show_menu *aChoices
- aChoices.flatten!
- output = nil
-
IO.popen('wmiimenu', 'r+') do |menu|
- menu.write aChoices.join("\n")
+ menu.puts aChoices
menu.close_write
- output = menu.read
+ if (choice = menu.read).empty?
+ nil
+ else
+ choice
+ end
end
-
- output
end
# Focuses the client chosen from a menu.
def focus_client_from_menu
choices = Wmii.clients.map do |c|
- format "%d. [%s] %s", c.index, c.tags.join(' '), c.name.downcase
+ format "%d. [%s] %s", c.index!, c.tags!, c.name!.downcase
end
- target = show_menu(choices)
-
- unless target.empty?
+ if target = show_menu(choices)
Wmii.focus_client target.scan(/\d+/).first
end
end
@@ -67,9 +66,8 @@ end
# The {+tag -tag idea}[http://zimbatm.oree.ch/articles/2006/06/15/wmii-3-and-ruby] is from Jonas Pfenniger.
def change_tag_from_menu
choices = Wmii.tags.map {|t| [t, "+#{t}", "-#{t}"]}.flatten
- target = show_menu(choices)
- unless target.empty?
+ if target = show_menu(choices)
Wmii.selected_clients.each do |c|
case target
when /^\+/
@@ -89,7 +87,7 @@ end
# Send selected clients to temporary view or switch back again.
def toggle_temp_view
- curTag = Wmii.current_view.name
+ curTag = Wmii.current_view.name!
if curTag =~ /~\d+$/
Wmii.selected_clients.each do |c|
@@ -116,7 +114,7 @@ end
# Puts focus on an adjacent view (:left or :right).
def cycle_view aTarget
tags = Wmii.tags
- curTag = Wmii.current_view.name
+ curTag = Wmii.current_view.name!
curIndex = tags.index(curTag)
newIndex =
@@ -138,12 +136,12 @@ end
# Toggles maximization of the currently focused client.
def toggle_maximize
src = Wmii.current_client
- srcId = src.index
+ srcId = src.index!
src.ctl = 'sendto toggle'
dst = Wmii.current_view[0].sel
- if dst.index == srcId
+ if dst.index! == srcId
dst.geom = '0 0 east south'
end
end
diff --git a/wm.rb b/wm.rb
@@ -45,7 +45,7 @@ module Wmii
# Returns the current set of tags.
def Wmii.tags
- Ixp.read('/tags').split
+ Ixp::Client.read('/tags').split
end
# Returns the current set of views.
@@ -74,21 +74,21 @@ module Wmii
needle = Wmii.get_client(aClientId)
if needle.exist?
- areas = []
+ haystack = []
if aArea && aArea.exist?
- areas << aArea
+ haystack << aArea
elsif aView && aView.exist?
- areas.concat aView.areas
+ haystack.concat aView.areas
else
needle.tags.map {|t| get_view t}.each do |v|
- areas.concat v.areas
+ haystack.concat v.areas
end
end
- areas.each do |a|
+ haystack.each do |a|
if a.indices.detect {|i| i == aClientId}
return a[aClientId]
end
@@ -233,14 +233,11 @@ module Wmii
super Area, Ixp::Node, :select, *aArgs
end
- undef index # it prevents access to ./index file
-
-
TAG_DELIMITER = "+"
# Returns the tags associated with this client.
def tags
- self[:tags].split(TAG_DELIMITER)
+ self[:tags, true].split(TAG_DELIMITER)
end
# Modifies the tags associated with this client.
@@ -372,39 +369,41 @@ module Wmii
end
end
+
private
- # Updates the path of this area for proper insertion and inserts the given client.
- def setup_for_insertion! aFirstClient
- raise ArgumentError, 'nonexistent client' unless aFirstClient.exist?
-
- dstIdx = self.index
- maxIdx = parent.indices.last
-
- if dstIdx > maxIdx
- # move *near* final destination
- clientId = aFirstClient.index
- aFirstClient.ctl = "sendto #{maxIdx}"
-
- # recalculate b/c sendto can be destructive
- maxIdx = parent.indices.last
- maxCol = parent[maxIdx]
-
- aFirstClient = Wmii.find_client(clientId, maxCol)
-
- # move *into* final destination
- if maxCol.indices.length > 1
- aFirstClient.ctl = "sendto next"
- dstIdx = maxIdx + 1
- else
- dstIdx = maxIdx
- end
- @path = "#{dirname}/#{dstIdx}"
+ # Updates the path of this area for proper insertion and inserts the given client.
+ def setup_for_insertion! aFirstClient
+ raise ArgumentError, 'nonexistent client' unless aFirstClient.exist?
- else
- aFirstClient.ctl = "sendto #{dstIdx}"
- end
+ dstIdx = self.index
+ maxIdx = parent.indices.last
+
+ if dstIdx > maxIdx
+ # move *near* final destination
+ clientId = aFirstClient.index!
+ aFirstClient.ctl = "sendto #{maxIdx}"
+
+ # recalculate b/c sendto can be destructive
+ maxIdx = parent.indices.last
+ maxCol = parent[maxIdx]
+
+ aFirstClient = Wmii.find_client(clientId, maxCol)
+
+ # move *into* final destination
+ if maxCol.indices.length > 1
+ aFirstClient.ctl = "sendto next"
+ dstIdx = maxIdx + 1
+ else
+ dstIdx = maxIdx
+ end
+
+ @path = "#{dirname}/#{dstIdx}"
+
+ else
+ aFirstClient.ctl = "sendto #{dstIdx}"
end
+ end
end
class View < Node
@@ -530,17 +529,19 @@ module Wmii
end
end
+
private
- # Returns the number of clients in the non-floating areas of this view.
- def num_grounded_clients
- if ground = areas[1..-1]
- ground.inject(0) do |count, area|
- count + area.length
- end
- else
- 0
+
+ # Returns the number of clients in the non-floating areas of this view.
+ def num_grounded_clients
+ if ground = areas[1..-1]
+ ground.inject(0) do |count, area|
+ count + area.length
end
+ else
+ 0
end
+ end
end
end
@@ -549,22 +550,20 @@ class Array
# Supports destructive operations on each client in this array.
def each
- return unless block_given?
-
- original_each do |c|
- if c.is_a? Wmii::Client
+ if block_given?
+ original_each do |c|
# resolve stale paths caused by destructive operations
- unless c.exist?
- puts "\n trying to resolve nonexistent client: #{c.inspect}" if $DEBUG
+ if c.is_a?(Wmii::Client) && !c.exist?
+ puts "\n trying to resolve nonexistent client: #{c.path}" if $DEBUG
c = Wmii.find_client(c.basename, nil, Wmii.current_view)
next unless c
- puts "resolution OK: #{c.inspect}" if $DEBUG
- end
- end
+ puts "resolution OK: #{c.path}" if $DEBUG
+ end
- yield c
+ yield c
+ end
end
end
end
diff --git a/wmiirc-config.rb b/wmiirc-config.rb
@@ -170,7 +170,9 @@ SHORTCUTS = {
# maximize the floating area's focused client
"#{LAYOUT_SEQ}z" => lambda do
- Wmii.current_view[0].sel.geom = '0 0 east south'
+ if (client = Wmii.current_view[0].sel).exist?
+ client.geom = '0 0 east south'
+ end
end,
@@ -234,7 +236,7 @@ SHORTCUTS = {
# focus any view by choosing from a menu
"#{MENU_SEQ}u" => lambda do
- unless (choice = show_menu(Wmii.tags)).empty?
+ if choice = show_menu(Wmii.tags)
Wmii.focus_view choice
end
end,
@@ -278,11 +280,11 @@ SHORTCUTS = {
"#{SEND_SEQ}Delete" => lambda do
# reverse b/c client indices are reassigned upon deletion.
# ex: imagine you have these clients: [1, 2, 3]
- # you delete the first client (index 1).
- # now, wmii reorders the client indices: [1, 2]
+ # you delete the second client (id 2).
+ # now, wmii reorders the remaining clients [1, 3] as: [1, 2]
# that is why we must go in reverse!
Wmii.selected_clients.sort_by do |c|
- c.index.to_i
+ c.index!.to_i
end.reverse.each do |c|
c.ctl = 'kill'
end
@@ -294,7 +296,7 @@ SHORTCUTS = {
# remove currently focused view from current selection's tags
"#{SEND_SEQ}Shift-minus" => lambda do
- curTag = Wmii.current_view.name
+ curTag = Wmii.current_view.name!
Wmii.selected_clients.each do |c|
c.untag! curTag
@@ -393,12 +395,12 @@ SHORTCUTS = {
end
# jump to view whose name begins with the pressed key
-('a'..'z').each do |char|
- SHORTCUTS["#{MENU_SEQ}v,#{char}"] = lambda do
+('a'..'z').each do |key|
+ SHORTCUTS["#{MENU_SEQ}v,#{key}"] = lambda do
choices = Wmii.tags
- choices.delete Wmii.current_view.name
+ choices.delete Wmii.current_view.name!
- if view = choices.select {|t| t =~ /^#{char}/i}.first
+ if view = choices.select {|t| t =~ /^#{key}/i}.first
Wmii.focus_view view
end
end