aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/concat/CHANGELOG27
-rw-r--r--modules/concat/README.markdown103
-rwxr-xr-xmodules/concat/files/concatfragments.sh123
-rw-r--r--modules/concat/files/null/.gitignore0
-rw-r--r--modules/concat/manifests/fragment.pp51
-rw-r--r--modules/concat/manifests/init.pp164
-rw-r--r--modules/concat/manifests/setup.pp36
7 files changed, 0 insertions, 504 deletions
diff --git a/modules/concat/CHANGELOG b/modules/concat/CHANGELOG
deleted file mode 100644
index 2f8aecc3..00000000
--- a/modules/concat/CHANGELOG
+++ /dev/null
@@ -1,27 +0,0 @@
-KNOWN ISSUES:
-- In 0.24.8 you will see inintended notifies, if you build a file
- in a run, the next run will also see it as changed. This is due
- to how 0.24.8 does the purging of unhandled files, this is improved
- in 0.25.x and we cannot work around it in our code.
-
-CHANGELOG:
-- 2010/02/19 - initial release
-- 2010/03/12 - add support for 0.24.8 and newer
- - make the location of sort configurable
- - add the ability to add shell comment based warnings to
- top of files
- - add the ablity to create empty files
-- 2010/04/05 - fix parsing of WARN and change code style to match rest
- of the code
- - Better and safer boolean handling for warn and force
- - Don't use hard coded paths in the shell script, set PATH
- top of the script
- - Use file{} to copy the result and make all fragments owned
- by root. This means we can chnage the ownership/group of the
- resulting file at any time.
- - You can specify ensure => "/some/other/file" in concat::fragment
- to include the contents of a symlink into the final file.
-- 2010/04/16 - Add more cleaning of the fragment name - removing / from the $name
-- 2010/05/22 - Improve documentation and show the use of ensure =>
-- 2010/07/14 - Add support for setting the filebucket behavior of files
-- 2010/10/04 - Make the warning message configurable
diff --git a/modules/concat/README.markdown b/modules/concat/README.markdown
deleted file mode 100644
index 3f325097..00000000
--- a/modules/concat/README.markdown
+++ /dev/null
@@ -1,103 +0,0 @@
-What is it?
-===========
-
-A Puppet module that can construct files from fragments.
-
-Please see the comments in the various .pp files for details
-as well as posts on my blog at http://www.devco.net/
-
-Released under the Apache 2.0 licence
-
-Usage:
-------
-
-If you wanted a /etc/motd file that listed all the major modules
-on the machine. And that would be maintained automatically even
-if you just remove the include lines for other modules you could
-use code like below, a sample /etc/motd would be:
-
-<pre>
-Puppet modules on this server:
-
- -- Apache
- -- MySQL
-</pre>
-
-Local sysadmins can also append to the file by just editing /etc/motd.local
-their changes will be incorporated into the puppet managed motd.
-
-<pre>
-# class to setup basic motd, include on all nodes
-class motd {
- include concat::setup
- $motd = "/etc/motd"
-
- concat{$motd,
- owner => root,
- group => root,
- mode => 644
- }
-
- concat::fragment{"motd_header":
- target => $motd,
- content => "\nPuppet modules on this server:\n\n",
- order => 01,
- }
-
- # local users on the machine can append to motd by just creating
- # /etc/motd.local
- concat::fragment{"motd_local":
- target => $motd,
- ensure => "/etc/motd.local",
- order => 15
- }
-}
-
-# used by other modules to register themselves in the motd
-define motd::register($content="", $order=10) {
- if $content == "" {
- $body = $name
- } else {
- $body = $content
- }
-
- concat::fragment{"motd_fragment_$name":
- target => "/etc/motd",
- content => " -- $body\n"
- }
-}
-
-# a sample apache module
-class apache {
- include apache::install, apache::config, apache::service
-
- motd::register{"Apache": }
-}
-</pre>
-
-Known Issues:
--------------
-* In 0.24.8 you will see inintended notifies, if you build a file
- in a run, the next run will also see it as changed. This is due
- to how 0.24.8 does the purging of unhandled files, this is improved
- in 0.25.x and we cannot work around it in our code.
-
-Contributors:
--------------
-**Paul Elliot**
-
- * Provided 0.24.8 support, shell warnings and empty file creation support.
-
-**Chad Netzer**
-
- * Various patches to improve safety of file operations
- * Symlink support
-
-**David Schmitt**
-
- * Patch to remove hard coded paths relying on OS path
- * Patch to use file{} to copy the resulting file to the final destination. This means Puppet client will show diffs and that hopefully we can change file ownerships now
-
-Contact:
---------
-You can contact me on rip@devco.net or follow my blog at http://www.devco.net I am also on twitter as ripienaar
diff --git a/modules/concat/files/concatfragments.sh b/modules/concat/files/concatfragments.sh
deleted file mode 100755
index b486047d..00000000
--- a/modules/concat/files/concatfragments.sh
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/bin/bash
-
-# Script to concat files to a config file.
-#
-# Given a directory like this:
-# /path/to/conf.d
-# |-- fragments
-# | |-- 00_named.conf
-# | |-- 10_domain.net
-# | `-- zz_footer
-#
-# The script supports a test option that will build the concat file to a temp location and
-# use /usr/bin/cmp to verify if it should be run or not. This would result in the concat happening
-# twice on each run but gives you the option to have an unless option in your execs to inhibit rebuilds.
-#
-# Without the test option and the unless combo your services that depend on the final file would end up
-# restarting on each run, or in other manifest models some changes might get missed.
-#
-# OPTIONS:
-# -o The file to create from the sources
-# -d The directory where the fragments are kept
-# -t Test to find out if a build is needed, basically concats the files to a temp
-# location and compare with what's in the final location, return codes are designed
-# for use with unless on an exec resource
-# -w Add a shell style comment at the top of the created file to warn users that it
-# is generated by puppet
-# -f Enables the creation of empty output files when no fragments are found
-# -n Sort the output numerically rather than the default alpha sort
-#
-# the command:
-#
-# concatfragments.sh -o /path/to/conffile.cfg -d /path/to/conf.d
-#
-# creates /path/to/conf.d/fragments.concat and copies the resulting
-# file to /path/to/conffile.cfg. The files will be sorted alphabetically
-# pass the -n switch to sort numerically.
-#
-# The script does error checking on the various dirs and files to make
-# sure things don't fail.
-
-OUTFILE=""
-WORKDIR=""
-TEST=""
-FORCE=""
-WARN=""
-SORTARG="-z"
-
-PATH=/sbin:/usr/sbin:/bin:/usr/bin
-
-while getopts "o:s:d:tnw:f" options; do
- case $options in
- o ) OUTFILE=$OPTARG;;
- d ) WORKDIR=$OPTARG;;
- n ) SORTARG="-zn";;
- w ) WARNMSG="$OPTARG";;
- f ) FORCE="true";;
- t ) TEST="true";;
- * ) echo "Specify output file with -o and fragments directory with -d"
- exit 1;;
- esac
-done
-
-# do we have -o?
-if [ x${OUTFILE} = "x" ]; then
- echo "Please specify an output file with -o"
- exit 1
-fi
-
-# do we have -d?
-if [ x${WORKDIR} = "x" ]; then
- echo "Please fragments directory with -d"
- exit 1
-fi
-
-# can we write to -o?
-if [ -a ${OUTFILE} ]; then
- if [ ! -w ${OUTFILE} ]; then
- echo "Cannot write to ${OUTFILE}"
- exit 1
- fi
-else
- if [ ! -w `dirname ${OUTFILE}` ]; then
- echo "Cannot write to `dirname ${OUTFILE}` to create ${OUTFILE}"
- exit 1
- fi
-fi
-
-# do we have a fragments subdir inside the work dir?
-if [ ! -d "${WORKDIR}/fragments" ] && [ ! -x "${WORKDIR}/fragments" ]; then
- echo "Cannot access the fragments directory"
- exit 1
-fi
-
-# are there actually any fragments?
-if [ ! "$(ls -A ${WORKDIR}/fragments)" ]; then
- if [ x${FORCE} = "x" ]; then
- echo "The fragments directory is empty, cowardly refusing to make empty config files"
- exit 1
- fi
-fi
-
-cd ${WORKDIR}
-
-if [ x${WARNMSG} = "x" ]; then
- : > "fragments.concat"
-else
- echo -e "$WARNMSG" > "fragments.concat"
-fi
-
-# find all the files in the fragments directory, sort them numerically and concat to fragments.concat in the working dir
-find fragments/ -type f -follow -print0 |sort ${SORTARG}|xargs -0 cat >>"fragments.concat"
-
-if [ x${TEST} = "x" ]; then
- # This is a real run, copy the file to outfile
- cp fragments.concat ${OUTFILE}
- RETVAL=$?
-else
- # Just compare the result to outfile to help the exec decide
- cmp ${OUTFILE} fragments.concat
- RETVAL=$?
-fi
-
-exit $RETVAL
diff --git a/modules/concat/files/null/.gitignore b/modules/concat/files/null/.gitignore
deleted file mode 100644
index e69de29b..00000000
--- a/modules/concat/files/null/.gitignore
+++ /dev/null
diff --git a/modules/concat/manifests/fragment.pp b/modules/concat/manifests/fragment.pp
deleted file mode 100644
index 890d43a4..00000000
--- a/modules/concat/manifests/fragment.pp
+++ /dev/null
@@ -1,51 +0,0 @@
-# Puts a file fragment into a directory previous setup using concat
-#
-# OPTIONS:
-# - target The file that these fragments belong to
-# - content If present puts the content into the file
-# - source If content was not specified, use the source
-# - order By default all files gets a 10_ prefix in the directory
-# you can set it to anything else using this to influence the
-# order of the content in the file
-# - ensure Present/Absent or destination to a file to include another file
-# - mode Mode for the file
-# - owner Owner of the file
-# - group Owner of the file
-# - backup Controls the filebucketing behavior of the final file and
-# see File type reference for its use. Defaults to 'puppet'
-define concat::fragment($target, $content='', $source='', $order=10, $ensure = "present", $mode = 0644, $owner = root, $group = root, $backup = "puppet") {
- $safe_name = regsubst($name, '/', '_', 'G')
- $safe_target_name = regsubst($target, '/', '_', 'G')
- $concatdir = $concat::setup::concatdir
- $fragdir = "${concatdir}/${safe_target_name}"
-
- # if content is passed, use that, else if source is passed use that
- # if neither passed, but $ensure is in symlink form, make a symlink
- case $content {
- "": {
- case $source {
- "": {
- case $ensure {
- "", "absent", "present", "file", "directory": {
- crit("No content, source or symlink specified")
- }
- }
- }
- default: { File{ source => $source } }
- }
- }
- default: { File{ content => $content } }
- }
-
- file{"${fragdir}/fragments/${order}_${safe_name}":
- mode => $mode,
- owner => $owner,
- group => $group,
- ensure => $ensure,
- backup => $backup,
- alias => "concat_fragment_${name}",
- notify => Exec["concat_${target}"]
- }
-}
-
-# vi:tabstop=4:expandtab:ai
diff --git a/modules/concat/manifests/init.pp b/modules/concat/manifests/init.pp
deleted file mode 100644
index b94411c2..00000000
--- a/modules/concat/manifests/init.pp
+++ /dev/null
@@ -1,164 +0,0 @@
-# A system to construct files using fragments from other files or templates.
-#
-# This requires at least puppet 0.25 to work correctly as we use some
-# enhancements in recursive directory management and regular expressions
-# to do the work here.
-#
-# USAGE:
-# The basic use case is as below:
-#
-# concat{"/etc/named.conf":
-# notify => Service["named"]
-# }
-#
-# concat::fragment{"foo.com_config":
-# target => "/etc/named.conf",
-# order => 10,
-# content => template("named_conf_zone.erb")
-# }
-#
-# # add a fragment not managed by puppet so local users
-# # can add content to managed file
-# concat::fragment{"foo.com_user_config":
-# target => "/etc/named.conf",
-# order => 12,
-# ensure => "/etc/named.conf.local"
-# }
-#
-# This will use the template named_conf_zone.erb to build a single
-# bit of config up and put it into the fragments dir. The file
-# will have an number prefix of 10, you can use the order option
-# to control that and thus control the order the final file gets built in.
-#
-# SETUP:
-# The class concat::setup defines a variable $concatdir - you should set this
-# to a directory where you want all the temporary files and fragments to be
-# stored. Avoid placing this somewhere like /tmp since you should never
-# delete files here, puppet will manage them.
-#
-# There's some regular expression magic to figure out the puppet version but
-# if you're on an older 0.24 version just set $puppetversion = 24
-#
-# Before you can use any of the concat features you should include the
-# class concat::setup somewhere on your node first.
-#
-# DETAIL:
-# We use a helper shell script called concatfragments.sh that gets placed
-# in /usr/local/bin to do the concatenation. While this might seem more
-# complex than some of the one-liner alternatives you might find on the net
-# we do a lot of error checking and safety checks in the script to avoid
-# problems that might be caused by complex escaping errors etc.
-#
-# LICENSE:
-# Apache Version 2
-#
-# LATEST:
-# http://github.com/ripienaar/puppet-concat/
-#
-# CONTACT:
-# R.I.Pienaar <rip@devco.net>
-# Volcane on freenode
-# @ripienaar on twitter
-# www.devco.net
-
-
-# Sets up so that you can use fragments to build a final config file,
-#
-# OPTIONS:
-# - mode The mode of the final file
-# - owner Who will own the file
-# - group Who will own the file
-# - force Enables creating empty files if no fragments are present
-# - warn Adds a normal shell style comment top of the file indicating
-# that it is built by puppet
-# - backup Controls the filebucketing behavior of the final file and
-# see File type reference for its use. Defaults to 'puppet'
-#
-# ACTIONS:
-# - Creates fragment directories if it didn't exist already
-# - Executes the concatfragments.sh script to build the final file, this script will create
-# directory/fragments.concat. Execution happens only when:
-# * The directory changes
-# * fragments.concat != final destination, this means rebuilds will happen whenever
-# someone changes or deletes the final file. Checking is done using /usr/bin/cmp.
-# * The Exec gets notified by something else - like the concat::fragment define
-# - Copies the file over to the final destination using a file resource
-#
-# ALIASES:
-# - The exec can notified using Exec["concat_/path/to/file"] or Exec["concat_/path/to/directory"]
-# - The final file can be referened as File["/path/to/file"] or File["concat_/path/to/file"]
-define concat($mode = 0644, $owner = "root", $group = "root", $warn = "false", $force = "false", $backup = "puppet") {
- $safe_name = regsubst($name, '/', '_', 'G')
- $concatdir = $concat::setup::concatdir
- $version = $concat::setup::majorversion
- $fragdir = "${concatdir}/${safe_name}"
- $concat_name = "fragments.concat.out"
- $default_warn_message = '# This file is managed by Puppet. DO NOT EDIT.'
-
- case $warn {
- 'true',true,yes,on: { $warnmsg = "$default_warn_message" }
- 'false',false,no,off: { $warnmsg = "" }
- default: { $warnmsg = "$warn" }
- }
-
- $warnmsg_escaped = regsubst($warnmsg, "'", "'\\\\''", 'G')
- $warnflag = $warnmsg_escaped ? {
- '' => '',
- default => "-w '$warnmsg_escaped'"
- }
-
- case $force {
- 'true',true,yes,on: { $forceflag = "-f" }
- 'false',false,no,off: { $forceflag = "" }
- default: { fail("Improper 'force' value given to concat: $force") }
- }
-
- File{
- owner => root,
- group => root,
- mode => $mode,
- backup => $backup
- }
-
- file{$fragdir:
- ensure => directory;
-
- "${fragdir}/fragments":
- ensure => directory,
- recurse => true,
- purge => true,
- force => true,
- ignore => [".svn", ".git", ".gitignore"],
- source => $version ? {
- 24 => "puppet:///concat/null",
- default => undef,
- },
- notify => Exec["concat_${name}"];
-
- "${fragdir}/fragments.concat":
- ensure => present;
-
- "${fragdir}/${concat_name}":
- ensure => present;
-
- $name:
- source => "${fragdir}/${concat_name}",
- owner => $owner,
- group => $group,
- checksum => md5,
- mode => $mode,
- ensure => present,
- alias => "concat_${name}";
- }
-
- exec{"concat_${name}":
- user => root,
- group => root,
- notify => File[$name],
- subscribe => File[$fragdir],
- alias => "concat_${fragdir}",
- require => [ File["/usr/local/bin/concatfragments.sh"], File[$fragdir], File["${fragdir}/fragments"], File["${fragdir}/fragments.concat"] ],
- unless => "/usr/local/bin/concatfragments.sh -o ${fragdir}/${concat_name} -d ${fragdir} -t ${warnflag} ${forceflag}",
- command => "/usr/local/bin/concatfragments.sh -o ${fragdir}/${concat_name} -d ${fragdir} ${warnflag} ${forceflag}",
- }
-}
diff --git a/modules/concat/manifests/setup.pp b/modules/concat/manifests/setup.pp
deleted file mode 100644
index 9676fb66..00000000
--- a/modules/concat/manifests/setup.pp
+++ /dev/null
@@ -1,36 +0,0 @@
-# Sets up the concat system.
-#
-# $concatdir should point to a place where you wish the fragments to
-# live. This should not be somewhere like /tmp since ideally these files
-# should not be deleted ever, puppet should always manage them
-#
-# $puppetversion should be either 24 or 25 to enable a 24 compatible
-# mode, in 24 mode you might see phantom notifies this is a side effect
-# of the method we use to clear the fragments directory.
-#
-# The regular expression below will try to figure out your puppet version
-# but this code will only work in 0.24.8 and newer.
-#
-# It also copies out the concatfragments.sh file to /usr/local/bin
-class concat::setup {
- $concatdir = "/var/lib/puppet/concat"
- $majorversion = regsubst($puppetversion, '^[0-9]+[.]([0-9]+)[.][0-9]+$', '\1')
-
- file{"/usr/local/bin/concatfragments.sh":
- owner => root,
- group => root,
- mode => 755,
- source => $majorversion ? {
- 24 => "puppet:///concat/concatfragments.sh",
- default => "puppet:///modules/concat/concatfragments.sh"
- };
-
- $concatdir:
- ensure => directory,
- owner => root,
- group => root,
- mode => 755;
- }
-}
-
-# vi:tabstop=4:expandtab:ai
/span>; }); if ($e->{help}) { gtkset_tip($tips, $w, ref($e->{help}) eq 'HASH' ? $e->{help}{$txt} : ref($e->{help}) eq 'CODE' ? $e->{help}($txt) : $e->{help}); } } $e->{list}, \@radios; $boxradio, sub { my ($v, $full_struct) = @_; mapn { $_[0]->set_active($_[1] eq $v); $full_struct->{focus_w} = $_[0] if $_[1] eq $v; } \@radios, $e->{list}; }, $radios[0]; } sub create_treeview_list { my ($e, $may_go_to_next, $changed, $double_click) = @_; my $curr; my $list = Gtk2::ListStore->new("Glib::String"); my $list_tv = Gtk2::TreeView->new_with_model($list); $list_tv->set_headers_visible(0); $list_tv->get_selection->set_mode('browse'); my $textcolumn = Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0); $list_tv->append_column($textcolumn); my $select = sub { $list_tv->set_cursor($_[0], undef, 0); $list_tv->scroll_to_cell($_[0], undef, 1, 0.5, 0); }; my ($starting_word, $start_reg) = ('', '^'); my $timeout; $list_tv->signal_connect(key_press_event => sub { my ($_w, $event) = @_; my $c = chr($event->keyval & 0xff); Glib::Source->remove($timeout) if $timeout; $timeout = ''; if ($event->keyval >= 0x100) { &$may_go_to_next if member($event->keyval, ($Gtk2::Gdk::Keysyms{Return}, $Gtk2::Gdk::Keysyms{KP_Enter})); $starting_word = '' if !member($event->keyval, ($Gtk2::Gdk::Keysyms{Control_L}, $Gtk2::Gdk::Keysyms{Control_R})); } else { if (member('control-mask', @{$event->state})) { $c eq 's' or return 1; $start_reg and $start_reg = '', return 1; $curr++; } else { &$may_go_to_next if $c eq ' '; $curr++ if $starting_word eq '' || $starting_word eq $c; $starting_word .= $c unless $starting_word eq $c; } my @l = @{$e->{formatted_list}}; my $word = quotemeta $starting_word; my $j; for ($j = 0; $j < @l; $j++) { $l[($j + $curr) % @l] =~ /$start_reg$word/i and last; } if ($j == @l) { $starting_word = ''; } else { $select->(Gtk2::TreePath->new_from_string(($j + $curr) % @l)); } $timeout = Glib::Timeout->add($forgetTime, sub { $timeout = $starting_word = ''; 0 }); } 0; }); $list_tv->show; $list->append_set([ 0 => $_ ]) foreach @{$e->{formatted_list}}; $list_tv->get_selection->signal_connect(changed => sub { my ($model, $iter) = $_[0]->get_selected; $model && $iter or return; my $row = $model->get_path_str($iter); ${$e->{val}} = $e->{list}[$curr = $row]; &$changed; }); $list_tv->signal_connect(button_press_event => $double_click) if $double_click; $list_tv, sub { my ($v) = @_; eval { my $nb = find_index { $_ eq $v } @{$e->{list}}; my ($old_path) = $list_tv->get_cursor; if (!$old_path || $nb != $old_path->to_string) { $select->(Gtk2::TreePath->new_from_string($nb)); } undef $old_path if $old_path; }; }; } sub create_treeview_tree { my ($e, $may_go_to_next, $changed, $double_click, $tree_expanded) = @_; $tree_expanded = to_bool($tree_expanded); #- to reduce "Use of uninitialized value", especially when debugging my $sep = quotemeta $e->{separator}; my $tree_model = Gtk2::TreeStore->new("Glib::String", "Gtk2::Gdk::Pixbuf", "Glib::String"); my $tree = Gtk2::TreeView->new_with_model($tree_model); $tree->get_selection->set_mode('browse'); $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererPixbuf->new, 'pixbuf' => 1)); $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 2)); $tree->set_headers_visible(0); my ($build_value, $clean_image); if (exists $e->{image2f}) { my $to_unref; $build_value = sub { my ($text, $image) = $e->{image2f}->($_[0]); [ $text ? (0 => $text) : @{[]}, $image ? (1 => $to_unref = gtkcreate_pixbuf($image)) : @{[]} ]; }; $clean_image = sub { undef $to_unref }; } else { $build_value = sub { [ 0 => $_[0] ] }; $clean_image = sub {}; } my (%wtree, %wleaves, $size, $selected_via_click); my $parent; $parent = sub { if (my $w = $wtree{"$_[0]$e->{separator}"}) { return $w } my $s = ''; foreach (split $sep, $_[0]) { $wtree{"$s$_$e->{separator}"} ||= $tree_model->append_set($s ? $parent->($s) : undef, $build_value->($_)); $clean_image->(); $size++ if !$s; $s .= "$_$e->{separator}"; } $wtree{$s}; }; #- do some precomputing to not slowdown selection change and key press my (%precomp, @ordered_keys); mapn { my ($root, $leaf) = $_[0] =~ /(.*)$sep(.+)/ ? ($1, $2) : ('', $_[0]); my $iter = $tree_model->append_set($parent->($root), $build_value->($leaf)); $clean_image->(); my $pathstr = $tree_model->get_path_str($iter); $precomp{$pathstr} = { value => $leaf, fullvalue => $_[0], listvalue => $_[1] }; push @ordered_keys, $pathstr; $wleaves{$_[0]} = $pathstr; } $e->{formatted_list}, $e->{list}; undef $_ foreach values %wtree; undef %wtree; my $select = sub { my ($path_str) = @_; $tree->expand_to_path(Gtk2::TreePath->new_from_string($path_str)); my $path = Gtk2::TreePath->new_from_string($path_str); $tree->set_cursor($path, undef, 0); gtkflush(); #- workaround gtk2 bug not honouring centering on the given row if node was closed $tree->scroll_to_cell($path, undef, 1, 0.5, 0); }; my $curr = $tree_model->get_iter_first; #- default value $tree->expand_all if $tree_expanded; $tree->get_selection->signal_connect(changed => sub { my ($model, $iter) = $_[0]->get_selected; $model && $iter or return; undef $curr if ref $curr; my $path = $tree_model->get_path($curr = $iter); if (!$tree_model->iter_has_child($iter)) { ${$e->{val}} = $precomp{$path->to_string}{listvalue}; &$changed; } else { $tree->expand_row($path, 0) if $selected_via_click; } }); my ($starting_word, $start_reg) = ('', "^"); my $timeout; my $toggle = sub { if ($tree_model->iter_has_child($curr)) { $tree->toggle_expansion($tree_model->get_path($curr), 0); } else { &$may_go_to_next; } }; $tree->signal_connect(key_press_event => sub { my ($_w, $event) = @_; $selected_via_click = 0; my $c = chr($event->keyval & 0xff); $curr or return; Glib::Source->remove($timeout) if $timeout; $timeout = ''; if ($event->keyval >= 0x100) { &$toggle if member($event->keyval, ($Gtk2::Gdk::Keysyms{Return}, $Gtk2::Gdk::Keysyms{KP_Enter})); $starting_word = '' if !member($event->keyval, ($Gtk2::Gdk::Keysyms{Control_L}, $Gtk2::Gdk::Keysyms{Control_R})); } else { my $next; if (member('control-mask', @{$event->state})) { $c eq "s" or return 1; $start_reg and $start_reg = '', return 1; $next = 1; } else { &$toggle if $c eq ' '; $next = 1 if $starting_word eq '' || $starting_word eq $c; $starting_word .= $c unless $starting_word eq $c; } my $word = quotemeta $starting_word; my ($after, $best); my $currpath = $tree_model->get_path_str($curr); foreach my $v (@ordered_keys) { $next &&= !$after; $after ||= $v eq $currpath; if ($precomp{$v}{value} =~ /$start_reg$word/i) { if ($after && !$next) { ($best, $after) = ($v, 0); } else { $best ||= $v; } } } if (defined $best) { $select->($best); } else { $starting_word = ''; } $timeout = Glib::Timeout->add($forgetTime, sub { $timeout = $starting_word = ''; 0 }); } 0; }); $tree->signal_connect(button_press_event => sub { $selected_via_click = 1; &$double_click if $curr && !$tree_model->iter_has_child($curr) && $double_click; }); $tree, sub { my $v = may_apply($e->{format}, $_[0]); my ($model, $iter) = $tree->get_selection->get_selected; $select->($wleaves{$v} || return) if !$model || $wleaves{$v} ne $model->get_path_str($iter); undef $iter if ref $iter; }; } sub create_list { my ($e, $may_go_to_next, $changed, $double_click) = @_; my $l = $e->{list}; my $list = Gtk2::List->new; $list->set_selection_mode('browse'); my $select = sub { $list->select_item($_[0]); }; my $tips = Gtk2::Tooltips->new; each_index { my $item = Gtk2::ListItem->new(may_apply($e->{format}, $_)); $item->signal_connect(key_press_event => sub { my ($_w, $event) = @_; my $c = chr($event->keyval & 0xff); &$may_go_to_next if $event->keyval < 0x100 ? $c eq ' ' : $c eq "\r" || $c eq "\x8d"; 0; }); $list->append_items(gtkshow($item)); if ($e->{help}) { gtkset_tip($tips, $item, ref($e->{help}) eq 'HASH' ? $e->{help}{$_} : ref($e->{help}) eq 'CODE' ? $e->{help}($_) : $e->{help}); } $item->grab_focus if ${$e->{val}} && $_ eq ${$e->{val}}; } @$l; #- signal_connect'ed after append_items otherwise it is called and destroys the default value $list->signal_connect(select_child => sub { my ($_w, $row) = @_; ${$e->{val}} = $l->[$list->child_position($row)]; &$changed; }); $list->signal_connect(button_press_event => $double_click) if $double_click; $list, sub { my ($v) = @_; eval { $select->(find_index { $_ eq $v } @$l); }; }; } #- $actions is a ref list of $action #- $action is a { kind => $kind, action => sub { ... }, button => Gtk2::Button->new(...) } #- where $kind is one of '', 'modify', 'remove', 'add' sub add_modify_remove_action { my ($button, $buttons, $e, $treelist) = @_; if (member($button->{kind}, 'modify', 'remove')) { @{$e->{list}} or return; } my $r = $button->{action}->(${$e->{val}}); defined $r or return; if ($button->{kind} eq 'add') { ${$e->{val}} = $r; } elsif ($button->{kind} eq 'remove') { ${$e->{val}} = $e->{list}[0]; } ugtk2::gtk_set_treelist($treelist, [ map { may_apply($e->{format}, $_) } @{$e->{list}} ]); add_modify_remove_sensitive($buttons, $e); 1; } sub add_modify_remove_sensitive { my ($buttons, $e) = @_; $_->{button}->set_sensitive(@{$e->{list}} != ()) foreach grep { member($_->{kind}, 'modify', 'remove') } @$buttons; } sub ask_fromW { my ($o, $common, $l, $l2) = @_; my $ignore = 0; #-to handle recursivity my $mainw = ugtk2->new($common->{title}, %$o, modal => 1, if__($::main_window, transient => $::main_window)); #-the widgets my (@widgets, @widgets_always, @widgets_advanced, $advanced); my $tooltips = Gtk2::Tooltips->new; my $ok_clicked = sub { !$mainw->{ok} || $mainw->{ok}->get_property('sensitive') or return; $mainw->{retval} = 1; Gtk2->main_quit; }; my $set_all = sub { $ignore = 1; $_->{set}->(${$_->{e}{val}}, $_) foreach @widgets_always, @widgets_advanced; $_->{real_w}->set_sensitive(!$_->{e}{disabled}()) foreach @widgets_always, @widgets_advanced; $mainw->{ok}->set_sensitive(!$common->{callbacks}{ok_disabled}()) if $common->{callbacks}{ok_disabled}; $ignore = 0; }; my $get_all = sub { ${$_->{e}{val}} = $_->{get}->() foreach @widgets_always, @widgets_advanced; }; my $update = sub { my ($f) = @_; return if $ignore; $get_all->(); $f->(); $set_all->(); }; my $label_sizegrp = Gtk2::SizeGroup->new('horizontal'); my $realw_sizegrp = Gtk2::SizeGroup->new('horizontal'); my $create_widget = sub { my ($e, $ind) = @_; my $may_go_to_next = sub { my (undef, $event) = @_; if (!$event || ($event->keyval & 0x7f) == 0xd) { if ($ind == $#widgets) { @widgets == 1 ? $ok_clicked->() : $mainw->{ok}->grab_focus; } else { $widgets[$ind+1]{focus_w}->grab_focus; } return 1; #- prevent an action on the just grabbed focus } }; my $changed = sub { $update->(sub { $common->{callbacks}{changed}($ind) }) }; my ($w, $real_w, $focus_w, $set, $get, $grow); if ($e->{type} eq 'iconlist') { $w = Gtk2::Button->new; $set = sub { gtkdestroy($e->{icon}); my $f = $e->{icon2f}->($_[0]); $e->{icon} = -e $f ? gtkcreate_img($f) : Gtk2::WrappedLabel->new(may_apply($e->{format}, $_[0])); $w->add(gtkshow($e->{icon})); }; $w->signal_connect(clicked => sub { $set->(${$e->{val}} = next_val_in_array(${$e->{val}}, $e->{list})); $changed->(); }); $real_w = gtkpack_(Gtk2::HBox->new(0,10), 1, Gtk2::HBox->new(0,0), 0, $w, 1, Gtk2::HBox->new(0,0)); } elsif ($e->{type} eq 'bool') { if ($e->{image}) { $w = gtkadd(Gtk2::CheckButton->new, gtkshow(gtkcreate_img($e->{image}))); } else { #- warn "\"text\" member should have been used instead of \"label\" one at:\n", common::backtrace(), "\n" if $e->{label} && !$e->{text}; $w = Gtk2::CheckButton->new_with_label($e->{text}); } $w->signal_connect(clicked => $changed); $set = sub { $w->set_active($_[0]) }; $get = sub { $w->get_active }; } elsif ($e->{type} eq 'label') { $w = Gtk2::WrappedLabel->new(${$e->{val}}); $set = sub { $w->set($_[0]) }; } elsif ($e->{type} eq 'button') { $w = Gtk2::Button->new_with_label(''); $w->signal_connect(clicked => sub { $get_all->(); $mainw->{rwindow}->hide; if (my $v = $e->{clicked_may_quit}()) { $mainw->{retval} = $v; Gtk2->main_quit; } $mainw->{rwindow}->show; $set_all->(); }); $set = sub { $w->child->set_label(may_apply($e->{format}, $_[0])) }; } elsif ($e->{type} eq 'range') { my $want_scale = !$::expert; my $adj = Gtk2::Adjustment->new(${$e->{val}}, $e->{min}, $e->{max} + ($want_scale ? 1 : 0), 1, ($e->{max} - $e->{min}) / 10, 1); $adj->signal_connect(value_changed => $changed); $w = $want_scale ? Gtk2::HScale->new($adj) : Gtk2::SpinButton->new($adj, 10, 0); $w->set_size_request($want_scale ? 200 : 100, -1); $w->set_digits(0); $w->signal_connect(key_press_event => $may_go_to_next); $set = sub { $adj->set_value($_[0]) }; $get = sub { $adj->get_value }; } elsif ($e->{type} =~ /list/) { $e->{formatted_list} = [ map { may_apply($e->{format}, $_) } @{$e->{list}} ]; if (my $actions = $e->{add_modify_remove}) { my @buttons = map { { kind => lc $_, action => $actions->{$_}, button => Gtk2::Button->new(translate($_)) }; } N_("Add"), N_("Modify"), N_("Remove"); my $modify = find { $_->{kind} eq 'modify' } @buttons; my $do_action = sub { my ($button) = @_; add_modify_remove_action($button, \@buttons, $e, $w) and $changed->(); }; ($w, $set, $focus_w) = create_treeview_list($e, $may_go_to_next, $changed, sub { $do_action->($modify) if $_[1]->type =~ /^2/ }); $e->{saved_default_val} = ${$e->{val}}; foreach my $button (@buttons) { $button->{button}->signal_connect(clicked => sub { $do_action->($button) }); } add_modify_remove_sensitive(\@buttons, $e); $real_w = gtkpack_(Gtk2::HBox->new(0,0), 1, create_scrolled_window($w), 0, gtkpack__(Gtk2::VBox->new(0,0), map { $_->{button} } @buttons)); $grow = 1; } else { my $quit_if_double_click = #- i'm the only one, double click means accepting @$l == 1 || $e->{quit_if_double_click} ? sub { $_[1]->type =~ /^2/ && $ok_clicked->() } : ''; my @para = ($e, $may_go_to_next, $changed, $quit_if_double_click); my $use_boxradio = exists $e->{gtk}{use_boxradio} ? $e->{gtk}{use_boxradio} : @{$e->{list}} <= 8; if ($e->{help}) { #- used only when needed, as key bindings are dropped by List (ListStore does not seems to accepts Tooltips). ($w, $set, $focus_w) = $use_boxradio ? create_boxradio(@para) : create_list(@para); } elsif ($e->{type} eq 'treelist') { ($w, $set) = create_treeview_tree(@para, $e->{tree_expanded}); $e->{saved_default_val} = ${$e->{val}}; #- during realization, signals will mess up the default val :( } else { if ($use_boxradio) { ($w, $set, $focus_w) = create_boxradio(@para); } else { ($w, $set, $focus_w) = create_treeview_list(@para); $e->{saved_default_val} = ${$e->{val}}; } } if (@{$e->{list}} > 10) { $real_w = create_scrolled_window($w); $grow = 1; } } } else { if ($e->{type} eq "combo") { my @formatted_list = map { may_apply($e->{format}, $_) } @{$e->{list}}; my @l = sort { $b <=> $a } map { length } @formatted_list; my $width = $l[@l / 16]; # take the third octile (think quartile) if ($e->{not_edit} && $width < 160) { #- ComboBoxes do not have an horizontal scroll-bar. This can cause havoc for long strings (eg: diskdrake Create dialog box in expert mode) $w = Gtk2::ComboBox->new_text; } else { $w = Gtk2::Combo->new; $w->set_use_arrows_always(1); $w->entry->set_editable(!$e->{not_edit}); $w->disable_activate; } $w->set_popdown_strings(@formatted_list); $w->set_text(ref($e->{val}) ? may_apply($e->{format}, ${$e->{val}}) : $formatted_list[0]) if $w->isa('Gtk2::ComboBox'); ($real_w, $w) = ($w, $w->entry); #- FIXME workaround gtk suckiness (set_text generates two 'change' signals, one when removing the whole, one for inserting the replacement..) my $idle; $w->signal_connect(changed => sub { $idle ||= Glib::Idle->add(sub { undef $idle; $changed->(); 0 }); }); $set = sub { my $s = may_apply($e->{format}, $_[0]); $w->set_text($s) if $s ne $w->get_text && $_[0] ne $w->get_text; }; $get = sub { my $s = $w->get_text; my $i = eval { find_index { $s eq $_ } @formatted_list }; defined $i ? $e->{list}[$i] : $s; }; } else { $w = Gtk2::Entry->new; $w->signal_connect(changed => $changed); $w->signal_connect(focus_in_event => sub { $w->select_region(0, -1) }); $w->signal_connect(focus_out_event => sub { $w->select_region(0, 0) }); $set = sub { $w->set_text($_[0]) if $_[0] ne $w->get_text }; $get = sub { $w->get_text }; } $w->signal_connect(key_press_event => $may_go_to_next); $w->set_visibility(0) if $e->{hidden}; } $w->signal_connect(focus_out_event => sub { $update->(sub { $common->{callbacks}{focus_out}($ind) }); }); $tooltips->set_tip($w, $e->{help}) if $e->{help} && !ref($e->{help}); $real_w ||= $w; $real_w = gtkpack_(Gtk2::HBox->new, if_($e->{icon}, 0, eval { gtkcreate_img($e->{icon}) }), 0, gtkadd_widget($label_sizegrp, $e->{label}), 1, gtkadd_widget($realw_sizegrp, $real_w), ) if !$real_w->isa("Gtk2::CheckButton") || $e->{icon} || $e->{label}; { e => $e, w => $w, real_w => $real_w, focus_w => $focus_w || $w, get => $get || sub { ${$e->{val}} }, set => $set || sub {}, grow => $grow }; }; @widgets_always = map_index { $create_widget->($_, $::i) } @$l; @widgets_advanced = map_index { $create_widget->($_, $::i + @$l) } @$l2; my $pack = create_box_with_title($mainw, @{$common->{messages}}); ugtk2::set_main_window_size($mainw) if $mainw->{pop_it} && @$l; my @before_widgets_advanced = ( (map { { grow => 0, real_w => Gtk2::WrappedLabel->new($_) } } @{$common->{advanced_messages}}), { grow => 0, real_w => Gtk2::HSeparator->new }, ); my $first_time = 1; my $set_advanced = sub { ($advanced) = @_; $update->($common->{callbacks}{advanced}) if $advanced && !$first_time; foreach (@before_widgets_advanced, @widgets_advanced) { my $w = $_->{embed_scroll} || $_->{real_w}; $advanced ? $w->show : $w->hide; } @widgets = (@widgets_always, if_($advanced, @widgets_advanced)); $mainw->sync; #- for $set_all below (mainly for the set of clist) $first_time = 0; $set_all->(); #- must be done when showing advanced lists (to center selected value) }; my $advanced_button = [ $common->{advanced_label}, sub { my ($w) = @_; $set_advanced->(!$advanced); $w->child->set_label($advanced ? $common->{advanced_label_close} : $common->{advanced_label}); } ]; my @help = if_($common->{interactive_help}, [ N("Help"), sub { my $message = $common->{interactive_help}->() or return; $o->ask_warn(N("Help"), $message); }, 1 ]); if ($::expert && @$l2) { $common->{advanced_state} = 1; $advanced_button->[0] = $common->{advanced_label_close}; } my $buttons_pack = ($common->{ok} || !exists $common->{ok}) && $mainw->create_okcancel($common->{ok}, $common->{cancel}, '', @help, if_(@$l2, $advanced_button));