commit 54fdc9acc543effb3a49523d0db3ce154f3da9bc
parent e9cbb030dd1fc7562c629f2e56b5a5423f0f6d80
Author: Suraj N. Kurapati <sunaku@gmail.com>
Date: Sun, 10 Sep 2006 20:19:34 -0700
[project @ fd5711a8865a4d265b6de7b62bd6a8f2678387be]
[project @ 52]
up Wmii extends IxpNode
Diffstat:
fs.rb | | | 13 | ++++--------- |
wm.rb | | | 579 | ++++++++++++++++++++++++++++++++++++++++--------------------------------------- |
wmiirc | | | 2 | +- |
3 files changed, 296 insertions(+), 298 deletions(-)
diff --git a/fs.rb b/fs.rb
@@ -84,8 +84,7 @@ module IxpFs
open(aPath) {true}
end
-
- def self.open aPath # :yields: IXP stream
+ def self.open aPath # :yields: IO
if block_given?
begin
@@ixp.open(aPath) do |f|
@@ -136,6 +135,7 @@ class IxpNode
IxpFs.exist? @path
end
+ # Accesses the given sub-path.
def [] aSubPath
child = IxpNode.new("#{@path}/#{aSubPath}")
@@ -146,15 +146,10 @@ class IxpNode
end
end
+ # Writes to the given sub-path.
def []= aSubPath, aContent
child = IxpNode.new("#{@path}/#{aSubPath}")
-
- if child.file?
- child.write! aContent
- else
- #raise IOError,
- warn "cannot write to non-file: #{child.path}"
- end
+ child.write! aContent if child.file?
end
# Provides easy access to sub-nodes.
diff --git a/wm.rb b/wm.rb
@@ -18,299 +18,13 @@
require 'fs'
-# Encapsulates a graphical region in the window manager.
-class Container < IxpNode
- def initialize aParentClass, aChildClass, *aArgs
- @parentClass = aParentClass
- @childClass = aChildClass
- super(*aArgs)
- end
-
- # Returns a child with the given sub-path.
- def [] *args
- child = super
-
- if child.respond_to? :path
- child = @childClass.new(child.path)
- end
-
- child
- end
-
- # Returns the parent of this region.
- def parent
- @parentClass.new File.dirname(@path)
- end
-
- # Returns the index of this region in the parent.
- def index
- File.basename(@path).to_i
- end
-
- # Returns the next region in the parent.
- def next
- parent[self.index + 1]
- end
-
- # Returns a list of indices of items in this region.
- def indices
- if list = self.read
- list.grep(/^\d+$/)
- else
- []
- end
- end
-
- # Returns a list of items in this region.
- def children
- indices.map {|i| @childClass.new "#{@path}/#{i}"}
- end
-
- # Adds all clients in this region to the selection.
- def select!
- children.each do |s|
- s.select!
- end
- end
-
- # Removes all clients in this region from the selection.
- def unselect!
- children.each do |s|
- s.unselect!
- end
- end
-
- # Inverts the selection of clients in this region.
- def invert_selection!
- children.each do |s|
- s.invert_selection!
- end
- end
-
- # Puts focus on this region.
- def focus!
- ['select', 'view'].each do |cmd|
- parent.ctl = "#{cmd} #{File.basename @path}"
- end
- end
-end
-
-# Represents a running, graphical program.
-class Client < Container
- def initialize *aArgs
- super Area, IxpNode, *aArgs
- end
-
- TAG_DELIMITER = "+"
-
- # Returns the tags associated with this client.
- def tags
- self['tags'].split(TAG_DELIMITER)
- end
-
- # Modifies the tags associated with this client.
- def tags= *aTags
- t = aTags.flatten.uniq
- self['tags'] = t.join(TAG_DELIMITER) unless t.empty?
- end
-
- # Evaluates the given block within the context of this client's list of tags.
- def with_tags &aBlock
- t = self.tags
- t.instance_eval(&aBlock)
- self.tags = t
- end
-
- # Checks if this client is included in the current selection.
- def selected?
- tags.include? Wmii::SELECTION_TAG
- end
-
- def select!
- with_tags do
- unshift Wmii::SELECTION_TAG
- end
- end
-
- def unselect!
- with_tags do
- delete Wmii::SELECTION_TAG
- end
- end
-
- def invert_selection!
- if selected?
- unselect!
- else
- select!
- end
- end
-end
-
-class Area < Container
- def initialize *aArgs
- super View, Client, *aArgs
- end
-
- alias clients children
-
- # Inserts the given clients at the bottom of this area.
- def push! *aClients
- aClients.flatten!
- return if aClients.empty?
-
- unless (list = clients).empty?
- list.last.focus!
- end
-
- insert! aClients
- end
-
- # Inserts the given clients after the currently focused client in this area.
- def insert! *aClients
- aClients.flatten!
- return if aClients.empty?
-
- dstIdx = setup_for_insertion(aClients.shift)
-
- aClients.each do |c|
- c.ctl = "sendto #{dstIdx}"
- end
- end
-
- # Inserts the given clients at the top of this area.
- def unshift! *aClients
- aClients.flatten!
- return if aClients.empty?
-
- unless (list = clients).empty?
- list.first.focus!
- end
-
- dstIdx = setup_for_insertion(aClients.shift)
- parent[dstIdx].sel.ctl = 'swap up'
-
- aClients.each do |c|
- c.ctl = "sendto #{dstIdx}"
- end
- end
-
- # Concatenates the given area to the bottom of this area.
- def concat! aArea
- push! aArea.clients
- end
-
- private
- # Sets up this area for insertion and returns the area ID into which insertion is performed.
- def setup_for_insertion aFirstClient
- dstIdx = self.index
- maxIdx = parent.indices.length - 1
-
- if dstIdx > maxIdx
- aFirstClient.ctl = "sendto #{maxIdx}"
-
- parent[maxIdx].sel.ctl = "sendto next"
- dstIdx = maxIdx.next
- else
- aFirstClient.ctl = "sendto #{dstIdx}"
- end
-
- dstIdx
- end
-end
-
-class View < Container
- def initialize *aArgs
- super IxpNode, Area, *aArgs
- end
-
- alias areas children
-
- # Applies wmii-2 style tiling layout to this view while maintaining its order of clients. Only the first client in the primary column is kept; all others are evicted to the *top* of the secondary column. Any subsequent columns are squeezed into the *bottom* of the secondary column.
- def tile!
- numAreas = self.indices.length
-
- if numAreas > 1
- priCol, secCol, extCol = self[1], self[2], self[3]
-
- # keep only the first client in primary column
- priClient, *rest = priCol.clients
- secCol.unshift! rest
-
- # squeeze extra columns into secondary column
- if numAreas > 3
- (numAreas - 2).times do
- secCol.concat! extCol
- end
- end
-
- secCol.mode = 'default'
- # priCol.mode = 'max'
- priClient.focus!
- end
- end
-
- # Applies wmii-2 style grid layout to this view while maintaining its order of clients. If the maximum number of clients per column, the distribution of clients among the columns is calculated according to wmii-2 style. Only the first client in the primary column is kept; all others are evicted to the *top* of the secondary column. Any teritiary, quaternary, etc. columns are squeezed into the *bottom* of the secondary column.
- def grid! aMaxClientsPerColumn = nil
- # determine client distribution
- unless aMaxClientsPerColumn
- numClients = self.areas[1..-1].inject(0) do |count, area|
- count + area.clients.length
- end
-
- return unless numClients > 1
-
- numColumns = Math.sqrt(numClients)
- aMaxClientsPerColumn = (numClients / numColumns).round
- end
-
- # distribute the clients
- if aMaxClientsPerColumn <= 0
- # squeeze all clients into a single column
- areaList = self.areas
-
- (areaList.length - 2).times do
- areaList[1].concat! areaList[2]
- end
-
- else
- i = 1
-
- until i >= (areaList = self.areas).length
- a = areaList[i]
-
-
- a.mode = 'default'
- clientList = a.clients
-
- if clientList.length > aMaxClientsPerColumn
- # evict excess clients to next column
- a.next.unshift! clientList[aMaxClientsPerColumn..-1]
-
- elsif clientList.length < aMaxClientsPerColumn
- # import clients from next column
- until (diff = aMaxClientsPerColumn - a.clients.length) == 0
- immigrants = a.next.clients[0...diff]
- break if immigrants.empty?
-
- a.push! immigrants
- end
- end
-
-
- i += 1
- end
- end
- end
-end
-
# Ruby interface to WMII.
-class Wmii < Container
+class Wmii < IxpNode
SELECTION_TAG = 'SEL'
DETACHED_TAG = 'status'
def initialize
- super IxpNode, IxpNode, '/'
+ super '/'
end
@@ -452,4 +166,293 @@ class Wmii < Container
end
end
end
+
+
+ ## sub classes
+
+ # Encapsulates a graphical region in the window manager.
+ class Container < IxpNode
+ def initialize aParentClass, aChildClass, *aArgs
+ @parentClass = aParentClass
+ @childClass = aChildClass
+ super(*aArgs)
+ end
+
+ # Returns a child with the given sub-path.
+ def [] *args
+ child = super
+
+ if child.respond_to? :path
+ child = @childClass.new(child.path)
+ end
+
+ child
+ end
+
+ # Returns the parent of this region.
+ def parent
+ @parentClass.new File.dirname(@path)
+ end
+
+ # Returns the index of this region in the parent.
+ def index
+ File.basename(@path).to_i
+ end
+
+ # Returns the next region in the parent.
+ def next
+ parent[self.index + 1]
+ end
+
+ # Returns a list of indices of items in this region.
+ def indices
+ if list = self.read
+ list.grep(/^\d+$/)
+ else
+ []
+ end
+ end
+
+ # Returns a list of items in this region.
+ def children
+ indices.map {|i| @childClass.new "#{@path}/#{i}"}
+ end
+
+ # Adds all clients in this region to the selection.
+ def select!
+ children.each do |s|
+ s.select!
+ end
+ end
+
+ # Removes all clients in this region from the selection.
+ def unselect!
+ children.each do |s|
+ s.unselect!
+ end
+ end
+
+ # Inverts the selection of clients in this region.
+ def invert_selection!
+ children.each do |s|
+ s.invert_selection!
+ end
+ end
+
+ # Puts focus on this region.
+ def focus!
+ ['select', 'view'].each do |cmd|
+ parent.ctl = "#{cmd} #{File.basename @path}"
+ end
+ end
+ end
+
+ # Represents a running, graphical program.
+ class Client < Container
+ def initialize *aArgs
+ super Area, IxpNode, *aArgs
+ end
+
+ TAG_DELIMITER = "+"
+
+ # Returns the tags associated with this client.
+ def tags
+ self['tags'].split(TAG_DELIMITER)
+ end
+
+ # Modifies the tags associated with this client.
+ def tags= *aTags
+ t = aTags.flatten.uniq
+ self['tags'] = t.join(TAG_DELIMITER) unless t.empty?
+ end
+
+ # Evaluates the given block within the context of this client's list of tags.
+ def with_tags &aBlock
+ t = self.tags
+ t.instance_eval(&aBlock)
+ self.tags = t
+ end
+
+ # Checks if this client is included in the current selection.
+ def selected?
+ tags.include? SELECTION_TAG
+ end
+
+ def select!
+ with_tags do
+ unshift SELECTION_TAG
+ end
+ end
+
+ def unselect!
+ with_tags do
+ delete SELECTION_TAG
+ end
+ end
+
+ def invert_selection!
+ if selected?
+ unselect!
+ else
+ select!
+ end
+ end
+ end
+
+ class Area < Container
+ def initialize *aArgs
+ super View, Client, *aArgs
+ end
+
+ alias clients children
+
+ # Inserts the given clients at the bottom of this area.
+ def push! *aClients
+ aClients.flatten!
+ return if aClients.empty?
+
+ unless (list = clients).empty?
+ list.last.focus!
+ end
+
+ insert! aClients
+ end
+
+ # Inserts the given clients after the currently focused client in this area.
+ def insert! *aClients
+ aClients.flatten!
+ return if aClients.empty?
+
+ dstIdx = setup_for_insertion(aClients.shift)
+
+ aClients.each do |c|
+ c.ctl = "sendto #{dstIdx}"
+ end
+ end
+
+ # Inserts the given clients at the top of this area.
+ def unshift! *aClients
+ aClients.flatten!
+ return if aClients.empty?
+
+ unless (list = clients).empty?
+ list.first.focus!
+ end
+
+ dstIdx = setup_for_insertion(aClients.shift)
+ parent[dstIdx].sel.ctl = 'swap up'
+
+ aClients.each do |c|
+ c.ctl = "sendto #{dstIdx}"
+ end
+ end
+
+ # Concatenates the given area to the bottom of this area.
+ def concat! aArea
+ push! aArea.clients
+ end
+
+ private
+ # Sets up this area for insertion and returns the area ID into which insertion is performed.
+ def setup_for_insertion aFirstClient
+ dstIdx = self.index
+ maxIdx = parent.indices.length - 1
+
+ if dstIdx > maxIdx
+ aFirstClient.ctl = "sendto #{maxIdx}"
+
+ parent[maxIdx].sel.ctl = "sendto next"
+ dstIdx = maxIdx.next
+ else
+ aFirstClient.ctl = "sendto #{dstIdx}"
+ end
+
+ dstIdx
+ end
+ end
+
+ class View < Container
+ def initialize *aArgs
+ super IxpNode, Area, *aArgs
+ end
+
+ alias areas children
+
+ # Applies wmii-2 style tiling layout to this view while maintaining its order of clients. Only the first client in the primary column is kept; all others are evicted to the *top* of the secondary column. Any subsequent columns are squeezed into the *bottom* of the secondary column.
+ def tile!
+ numAreas = self.indices.length
+
+ if numAreas > 1
+ priCol, secCol, extCol = self[1], self[2], self[3]
+
+ # keep only the first client in primary column
+ priClient, *rest = priCol.clients
+ secCol.unshift! rest
+
+ # squeeze extra columns into secondary column
+ if numAreas > 3
+ (numAreas - 2).times do
+ secCol.concat! extCol
+ end
+ end
+
+ secCol.mode = 'default'
+ # priCol.mode = 'max'
+ priClient.focus!
+ end
+ end
+
+ # Applies wmii-2 style grid layout to this view while maintaining its order of clients. If the maximum number of clients per column, the distribution of clients among the columns is calculated according to wmii-2 style. Only the first client in the primary column is kept; all others are evicted to the *top* of the secondary column. Any teritiary, quaternary, etc. columns are squeezed into the *bottom* of the secondary column.
+ def grid! aMaxClientsPerColumn = nil
+ # determine client distribution
+ unless aMaxClientsPerColumn
+ numClients = self.areas[1..-1].inject(0) do |count, area|
+ count + area.clients.length
+ end
+
+ return unless numClients > 1
+
+ numColumns = Math.sqrt(numClients)
+ aMaxClientsPerColumn = (numClients / numColumns).round
+ end
+
+ # distribute the clients
+ if aMaxClientsPerColumn <= 0
+ # squeeze all clients into a single column
+ areaList = self.areas
+
+ (areaList.length - 2).times do
+ areaList[1].concat! areaList[2]
+ end
+
+ else
+ i = 1
+
+ until i >= (areaList = self.areas).length
+ a = areaList[i]
+
+
+ a.mode = 'default'
+ clientList = a.clients
+
+ if clientList.length > aMaxClientsPerColumn
+ # evict excess clients to next column
+ a.next.unshift! clientList[aMaxClientsPerColumn..-1]
+
+ elsif clientList.length < aMaxClientsPerColumn
+ # import clients from next column
+ until (diff = aMaxClientsPerColumn - a.clients.length) == 0
+ immigrants = a.next.clients[0...diff]
+ break if immigrants.empty?
+
+ a.push! immigrants
+ end
+ end
+
+
+ i += 1
+ end
+ end
+ end
+ end
end
diff --git a/wmiirc b/wmiirc
@@ -440,7 +440,7 @@ begin
client, button = arg.split
if button.to_i == 3
- Client.new("/client/#{client}").invert_selection!
+ Wmii::Client.new("/client/#{client}").invert_selection!
end
when 'Key'