/* SPDX-License-Identifier: GPL-3.0-or-later */
/ *
This js file is meant to be used in Cycling ' 74 Max for the [ jsui ] object .
It is designed to mimic [ preset ] , but better .
Copyright ( C ) 2024 Théophile Clet < contact @ tflcl . xyz > - https : //tflcl.xyz.
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < https : //www.gnu.org/licenses/gpl-3.0.txt>.
* /
autowatch = 0 ;
// When developping, autowatch = 1 isn't enough. You also need to manually call the loadbang function, and then re-binding the pattrstorage.
// A "loadbang, pattrstorage test" message does the trick.
inlets = 1
setinletassist ( 0 , "Connect to the linked pattrstorage" ) ;
outlets = 4 ;
setoutletassist ( 0 , "Outputs last triggered action" ) ;
setoutletassist ( 1 , "Connect to umenu to list stored presets" ) ;
setoutletassist ( 2 , "Connect to textedit to edit preset name" ) ;
setoutletassist ( 3 , "Connect to toggle to show active presets lock state" )
mgraphics . init ( ) ;
mgraphics . relative _coords = 0 ;
mgraphics . autofill = 0 ;
// LOOK
var slot _size = 14 ;
var slot _round = 0 ;
var slot _round _ratio = 0 ;
var margin = 4 ;
var spacing = 4 ;
var font _size = 14 ;
var font _name = "Arial" ;
var background _color = [ 0.2 , 0.2 , 0.2 , 1 ] ;
var empty _slot _color = [ 0.349 , 0.349 , 0.349 , 1 ] ;
var active _slot _color = [ 0.808 , 0.898 , 0.910 , 1 ] ;
var stored _slot _color = [ 0.502 , 0.502 , 0.502 , 1 ] ;
var interp _slot _color = [ 1.0 , 1.0 , 1.0 , 0.8 ] ;
var text _bg _color = [ 1 , 1 , 1 , 0.5 ] ;
var text _color = [ 0.129 , 0.129 , 0.129 , 1 ] ;
var color _1 = [ 0.743 , 0.41 , 0.501 , 1 ] ; // Color set for the filled slots. I don't like how this is declared. More info in color_wheel() declaration
var color _2 = [ 0.679 , 0.405 , 0.669 , 1 ] ;
var color _3 = [ 0.527 , 0.459 , 0.756 , 1 ] ;
var color _4 = [ 0.367 , 0.542 , 0.712 , 1 ] ;
var color _5 = [ 0.283 , 0.606 , 0.559 , 1 ] ;
var color _6 = [ 0.316 , 0.616 , 0.377 , 1 ] ;
var color _wheel _default = [ color _1 , color _2 , color _3 , color _4 , color _5 , color _6 ] ;
var color _wheel _custom = color _wheel _default . slice ( ) ;
var color _wheel _size = 6 ;
// FEEL
var layout = 0 ; // 0: grid mode (same as [preset]). 1: list mode
var display _interp = 1 ; // Enable/disable the UI feedback when interpolating between presets
var ignore _slot _zero = 1 ; // Makes previous_active_slot and interpolation display to ignore slot 0. Can be usefull when using slot 0 as a temporary step for interpolation.
var auto _writeagain = 0 ; // When enabled, will send a "writeagain" to pattrstorage any time a preset is stored/deleted/moved/renamed/(un)locked
var menu _number _only = 0 ; // Populates the umenu connected to 2nd outlet with stored preset number only, instead of number and name
var scrollable = 0 ; // Defines weither the object can be scrolled or not
var min _rows = 10 ; // Minimum number of rows to display if scrollable is enabled
var color _mode = 0 ; // Change the way the filled slots (stored presets) color is handeld. 0: stored_slot_color. 1: looping through color_1 to color_6. 2: Freely assign colors 1 to 6. 3: Set any color to any preset
var select _mode = 0 ; // 0: single click to select and recall the slot. 1: single click to select the slot, double click to recall it.
// (WORK)
var pattrstorage _name , pattrstorage _obj = null ;
var columns , rows = 0 ;
var slots = [ ] ; // Stores on screen box, name, lock and interpolation state for all slots
var slots _highest = 0 ; // Highest filled preset slot number
var slots _count _display = 0 ; // Number of slots to be displayed
var filled _slots = [ ] ; // List of stored slots
var active _slot = 0 ; //Last recalled slot
var previous _active _slot = 0 ; //Previously recalled slot
var previous _target = 0 ; //Here to deal with ongoing interpolations
var selected _slot = 0 ; //Last selected slot. Relevant especially when select_mode = 1. Otherwise it is the same as active_slot
var ui _width = box . rect [ 2 ] - box . rect [ 0 ] ;
var ui _height = box . rect [ 3 ] - box . rect [ 1 ] ;
var bg _width , bg _height = 0 ;
var mg = new MGraphics ( ui _width , ui _height ) ;
var base _drawing ;
var is _painting _base = 0 ;
var half _slot _size , half _margin , half _spacing ;
var last _x , last _y , last _hovered = - 1 ;
var y _offset = 0 ; // handle scrolling
var drag _scroll = 0 ; // handle scrolling when dragging outside of boundaries
var shift _hold , option _hold = 0 ;
var is _interpolating = 0 ;
var is _dragging = 0 ; // Drag flag
var drag _slot = - 1 ; // Stores the slot that's being dragged
var is _writing = 0 ;
// Keeping track of various variables for dealing with color modes
var requested _slot = - 1 ; // Which slot we're waiting a value for (used in get_all_preset_colors)
var color _mode _candidate = 0 ; // Which color mode we're aiming
var is _listening _to _subscriptionlist = 0 ; //Filters out received subscriptionlist messages when not updating slot color values
var is _listening _to _clientlist = 0 ; //Filters out received clientlist messages when not updating slot color values
var color _pattr ;
var has _loaded = false ;
if ( jsarguments . length > 1 ) { // Depreciated, use "pattrstorage" attribute instead of jsarguments.
pattrstorage _name = jsarguments [ 1 ] ;
}
// FUNCTIONS
function slot ( left , top , right , bottom , name , lock , interp , color _index , color _custom ) {
this . left = left ;
this . top = top ;
this . right = right ;
this . bottom = bottom ;
this . name = name ;
this . lock = lock ;
this . interp = interp ;
this . color _index = color _index ;
this . color _custom = color _custom ;
this . init = function ( ) {
this . left = 0 ;
this . top = 0 ;
this . right = 0 ;
this . bottom = 0 ;
this . name = null ;
this . lock = 0 ;
this . interp = - 1 ;
this . init _color ( ) ;
}
this . init _color = function ( ) {
this . color _index = 0 ;
this . color _custom = stored _slot _color ;
}
this . clear = function ( ) {
this . name = null ;
this . lock = 0 ;
this . interp = - 1 ;
this . color _index = 0 ;
this . color _custom = stored _slot _color ;
}
}
function loadbang ( ) {
// post("loadbang\n");
has _loaded = true ;
find _pattrstorage ( pattrstorage _name ) ;
calc _rows _columns ( ) ;
}
function calc _rows _columns ( ) {
half _margin = margin / 2 ;
half _spacing = spacing / 2 ;
half _slot _size = slot _size / 2 ;
slots [ 0 ] = new slot ( 0 , 0 , 0 , 0 , "(tmp)" , 0 , - 1 , 0 , stored _slot _color ) ; // Slot 0 is valid, but not represented in the GUI (and never saved by pattrstorage)
if ( layout == 0 ) {
columns = Math . floor ( ( ui _width - margin + spacing ) / ( slot _size + spacing ) ) ;
rows = Math . floor ( ( ui _height - margin + spacing ) / ( slot _size + spacing ) ) ;
if ( scrollable ) {
rows = Math . max ( rows , Math . max ( min _rows , Math . ceil ( slots _highest / columns ) ) ) ;
}
} else {
columns = 1 ;
rows = Math . floor ( ( ui _height - margin + spacing ) / ( slot _size + spacing ) ) ;
if ( scrollable ) {
rows = Math . max ( rows , Math . max ( min _rows , slots _highest ) ) ;
}
}
slots _count _display = columns * rows ;
for ( var i = 0 ; i < rows ; i ++ ) {
var top = margin + i * ( spacing + slot _size ) ;
var bottom = top + slot _size ;
for ( var j = 0 ; j < columns ; j ++ ) {
var left = margin + j * ( spacing + slot _size ) ;
var right = left + slot _size ;
var cur = 1 + i * columns + j ;
var prev _state = new slot ( ) ;
prev _state . init ( ) ;
if ( typeof slots [ cur ] !== 'undefined' ) {
prev _state = slots [ cur ] ;
}
slots [ cur ] = new slot ( left , top , right , bottom , prev _state . name , prev _state . lock , prev _state . interp , prev _state . color _index , prev _state . color _custom ) ;
}
}
if ( slots _count _display < slots _highest ) {
for ( var i = slots _count _display + 1 ; i <= slots _highest ; i ++ ) {
slots [ i ] = new slot ( ) ;
slots [ i ] . init ( ) ;
}
}
paint _base ( ) ;
}
calc _rows _columns . local = 1 ;
function draw _slot ( id , scale , cont ) {
scale = typeof cont !== 'undefined' ? scale : 1 ; // Sets scale to 1 by default if not passed as argument
cont = typeof cont !== 'undefined' ? cont : mgraphics ; // Sets drawing context to mgraphics by default if not passed as argument
var offset = slot _size * ( 1 - scale ) ;
if ( is _painting _base ) {
draw _slot _bubble ( slots [ id ] . left * scale , slots [ id ] . top * scale , slot _size * scale , slot _size * scale , cont ) ;
} else {
draw _slot _bubble ( slots [ id ] . left + offset , slots [ id ] . top + offset , slot _size * scale , slot _size * scale , cont ) ;
}
cont . fill ( ) ;
if ( layout == 1 ) {
// slot text background
var bg _txt _pos _x = margin + slot _size + spacing ;
var bg _txt _pos _y = slots [ id ] . top ;
var bg _txt _dim _w = ui _width - ( 2 * margin + slot _size + spacing ) ;
var bg _txt _dim _h = slot _size ;
if ( slots [ id ] . name != null ) {
cont . set _source _rgba ( stored _slot _color ) ;
} else {
cont . set _source _rgba ( empty _slot _color ) ;
}
// slot name
cont . set _font _size ( font _size * scale ) ;
var text = format _slot _name ( id ) ;
if ( is _painting _base ) {
draw _text _bubble ( bg _txt _pos _x * scale , bg _txt _pos _y * scale , bg _txt _dim _w * scale , bg _txt _dim _h * scale , text , cont ) ;
} else {
draw _text _bubble ( bg _txt _pos _x + offset , bg _txt _pos _y + offset , bg _txt _dim _w * scale , bg _txt _dim _h * scale , text , cont ) ;
}
}
}
draw _slot . local = 1 ;
function draw _slot _bubble ( x , y , w , h , cont ) {
cont = typeof cont !== 'undefined' ? cont : mgraphics ;
// I assume rectange is faster to draw than rectangle_rounded. Btw rectangle_rounded is wacky when showing interpolation. Maybe *interp on the first slot_round could solve this?
if ( slot _round ) {
cont . rectangle _rounded ( x , y , w , h , slot _round _ratio * w , slot _round _ratio * h ) ;
} else {
cont . rectangle ( x , y , w , h ) ;
}
}
draw _slot _bubble . local = 1 ;
function draw _text _bubble ( x , y , w , h , text , cont ) {
cont = typeof cont !== 'undefined' ? cont : mgraphics ;
// slot text background
cont . rectangle _rounded ( x , y , w , h , 4 , 4 ) ;
cont . fill ( ) ;
var text _dim = cont . text _measure ( text ) ;
var txt _pos _x = x + spacing ;
var txt _pos _y = y + ( text _dim [ 1 ] + h ) / 2 - text _dim [ 1 ] * 0.18 ;
cont . set _source _rgba ( text _color ) ;
cont . move _to ( txt _pos _x , txt _pos _y ) ;
cont . show _text ( text . toString ( ) ) ;
}
draw _text _bubble . local = 1 ;
function format _slot _name ( id ) {
var text = id ;
// If slot is locked, add brackets around its number
if ( slots [ id ] . lock == 1 ) {
text = '[' + text + ']' ;
}
// If slot has a name, append it to the preset name
if ( slots [ id ] . name != null ) {
text += ': ' + slots [ id ] . name ;
}
text = text . toString ( ) ;
return text ;
}
format _slot _name . local = 1 ;
function paint _base ( ) {
// We draw all slots (empty and stored ones) so we don't have to for every redraw
// post("paint_base\n");
is _painting _base = 1 ;
// Background
bg _width = layout == 0 ? columns * ( slot _size + spacing ) - spacing + 2 * margin : ui _width ;
bg _height = rows * ( slot _size + spacing ) - spacing + 2 * margin ;
mg = new MGraphics ( ui _width * 2 , bg _height * 2 ) ;
with ( mg ) {
set _source _rgba ( background _color ) ;
rectangle ( 0 , 0 , bg _width * 2 , bg _height * 2 ) ;
fill ( ) ;
select _font _face ( font _name ) ;
set _font _size ( font _size ) ;
// All slots
for ( var i = 1 ; i <= slots _count _display ; i ++ ) {
if ( i != drag _slot ) { //We mask the slot that is currently dragged as it is drawn at the mouse position already
if ( slots [ i ] . name != null ) {
if ( color _mode == 1 ) {
set _source _rgba ( color _wheel _custom [ i % color _wheel _size ] ) ;
} else if ( color _mode == 2 ) {
set _source _rgba ( color _wheel _custom [ Math . abs ( slots [ i ] . color _index ) % color _wheel _size ] ) ;
} else if ( color _mode == 3 ) {
set _source _rgba ( slots [ i ] . color _custom ) ;
} else {
set _source _rgba ( stored _slot _color ) ;
}
} else {
set _source _rgba ( empty _slot _color ) ;
}
draw _slot ( i , 2 , mg ) ;
}
}
}
is _painting _base = 0 ;
update _umenu ( ) ;
base _drawing = new Image ( mg ) ;
mgraphics . redraw ( ) ;
}
paint _base . local = 1 ;
function paint ( )
{
// Handling Presentation mode enable/disable
var cur _size = mgraphics . size ;
if ( cur _size [ 0 ] != ui _width || cur _size [ 1 ] != ui _height ) {
onresize ( cur _size [ 0 ] , cur _size [ 1 ] ) ;
} else {
// post("redraw\n");
with ( mgraphics ) {
select _font _face ( font _name ) ;
set _font _size ( font _size ) ;
translate ( 0 , y _offset ) ;
// Draw the base, which includes empty and filled slots
// It is first rendered at twice the size in order to make texts look nice and cripsy on hidpi discplays
// So we need to scale it down here
scale ( 0.5 , 0.5 ) ;
image _surface _draw ( base _drawing ) ;
scale ( 2 , 2 ) ;
set _line _width ( 1 ) ;
// Active slot
if ( is _dragging == 0 && active _slot > 0 && active _slot <= slots _count _display ) {
set _source _rgba ( active _slot _color ) ;
if ( color _mode ) {
draw _slot _bubble ( slots [ active _slot ] . left + 1.5 , slots [ active _slot ] . top + 1.5 , slot _size - 3 , slot _size - 3 ) ;
set _line _width ( 3 ) ;
stroke ( ) ;
} else {
draw _slot _bubble ( slots [ active _slot ] . left , slots [ active _slot ] . top , slot _size , slot _size ) ;
fill ( ) ;
}
}
// Previous active slot
if ( is _dragging == 0 && previous _active _slot > 0 && previous _active _slot <= slots _count _display ) {
// set_source_rgba(active_slot_color);
// draw_slot_bubble(slots[previous_active_slot][0]+0.75, slots[previous_active_slot][1]+0.75, slot_size-1.5, slot_size-1.5);
// set_line_width(1.5);
// stroke();
set _source _rgba ( active _slot _color [ 0 ] , active _slot _color [ 1 ] , active _slot _color [ 2 ] , active _slot _color [ 3 ] * 0.5 ) ;
if ( color _mode ) {
draw _slot _bubble ( slots [ previous _active _slot ] . left + 1.5 , slots [ previous _active _slot ] . top + 1.5 , slot _size - 3 , slot _size - 3 ) ;
set _line _width ( 3 ) ;
stroke ( ) ;
} else {
draw _slot _bubble ( slots [ previous _active _slot ] . left , slots [ previous _active _slot ] . top , slot _size , slot _size ) ;
fill ( ) ;
}
}
// Selected slot
if ( selected _slot > 0 && selected _slot <= slots _count _display ) {
set _source _rgba ( active _slot _color ) ;
set _line _width ( 1 ) ;
draw _slot _bubble ( slots [ selected _slot ] . left - 0.5 , slots [ selected _slot ] . top - 0.5 , slot _size + 1 , slot _size + 1 ) ;
stroke ( ) ;
}
// Interpolated slots
if ( is _dragging == 0 && display _interp && is _interpolating ) {
for ( var i = 1 ; i <= slots _count _display ; i ++ ) {
var interp = slots [ i ] . interp ;
if ( interp >= 0 ) {
set _source _rgba ( interp _slot _color ) ;
draw _slot _bubble ( slots [ i ] . left , slots [ i ] . top , slot _size , slot _size ) ;
stroke ( ) ;
draw _slot _bubble ( slots [ i ] . left , slots [ i ] . top + slot _size * ( 1 - interp ) , slot _size , slot _size * interp ) ;
fill ( ) ;
}
}
}
// Hovered slot
if ( last _hovered > - 1 ) {
if ( shift _hold ) {
if ( option _hold ) {
// About to delete
set _source _rgba ( empty _slot _color [ 0 ] , empty _slot _color [ 1 ] , empty _slot _color [ 2 ] , 0.8 ) ;
draw _slot _bubble ( slots [ last _hovered ] . left + 1 , slots [ last _hovered ] . top + 1 , slot _size - 2 , slot _size - 2 ) ;
fill ( ) ;
} else {
// About to store
set _source _rgba ( active _slot _color [ 0 ] , active _slot _color [ 1 ] , active _slot _color [ 2 ] , 0.7 ) ;
draw _slot _bubble ( slots [ last _hovered ] . left + 1 , slots [ last _hovered ] . top + 1 , slot _size - 2 , slot _size - 2 ) ;
fill ( ) ;
}
}
// Slot border
set _source _rgba ( 1 , 1 , 1 , 0.8 ) ;
draw _slot _bubble ( slots [ last _hovered ] . left , slots [ last _hovered ] . top , slot _size , slot _size ) ;
stroke ( ) ;
if ( layout == 0 ) {
//Text (slot number and name)
var text = format _slot _name ( last _hovered ) ;
var text _dim = text _measure ( text ) ;
// If the text is too big or a slot is being dragged, display the text on top of the next slot.
// Otherwise, it gets displayed on the hovered slot.
var bg _txt _dim _w = text _dim [ 0 ] > slot _size ? text _dim [ 0 ] + 4 : slot _size + 4 ;
var bg _txt _dim _h = text _dim [ 1 ] > slot _size ? text _dim [ 1 ] + 4 : slot _size + 4 ;
var bg _txt _pos _x = text _dim [ 0 ] > slot _size || is _dragging ? slots [ last _hovered ] . left + slot _size + 2 : slots [ last _hovered ] . left - 2 ;
var bg _txt _pos _y = text _dim [ 1 ] > slot _size || is _dragging ? slots [ last _hovered ] . top - 2 : slots [ last _hovered ] . top - 2 ;
// If there is not enough place, text is displayed on the left
if ( bg _txt _pos _x + bg _txt _dim _w > ui _width ) {
bg _txt _pos _x = slots [ last _hovered ] . left - half _spacing - bg _txt _dim _w ;
}
var txt _pos _x = text _dim [ 0 ] > slot _size ? bg _txt _pos _x + half _spacing : bg _txt _pos _x + ( bg _txt _dim _w / 2 ) - ( text _dim [ 0 ] / 2 ) ;
var txt _pos _y = bg _txt _pos _y + ( bg _txt _dim _h + text _dim [ 1 ] ) / 2 - text _dim [ 1 ] * 0.18 ;
// Bubble background
set _source _rgba ( text _bg _color ) ;
rectangle _rounded ( bg _txt _pos _x , bg _txt _pos _y , bg _txt _dim _w , bg _txt _dim _h , 4 , 4 ) ;
fill ( ) ;
// Buble text
set _source _rgba ( text _color ) ;
move _to ( txt _pos _x , txt _pos _y ) ;
show _text ( text . toString ( ) ) ;
}
}
// Drag slot
if ( is _dragging ) {
if ( layout == 0 ) {
translate ( last _x , last _y ) ;
rotate ( 0.15 ) ;
scale ( 1.1 , 1.1 ) ;
// Slot shadow
set _source _rgba ( 0 , 0 , 0 , 0.15 ) ;
for ( var i = 0 ; i < 4 ; i ++ ) {
draw _slot _bubble ( i * 0.4 + 1 - slot _size / 2 , i * 0.4 + 1 - slot _size / 2 , slot _size + i * 0.8 , slot _size + i * 0.8 ) ;
fill ( ) ;
}
draw _slot _bubble ( 2 - slot _size / 2 , 2 - slot _size / 2 , slot _size , slot _size ) ;
fill ( ) ;
//Flying slot
set _source _rgba ( active _slot _color ) ;
draw _slot _bubble ( - slot _size / 2 , - slot _size / 2 , slot _size , slot _size ) ;
fill ( ) ;
} else {
translate ( last _x , last _y ) ;
// rotate(0.15);
set _source _rgba ( active _slot _color ) ;
draw _slot _bubble ( - slot _size / 2 , - slot _size / 2 , slot _size , slot _size ) ;
fill ( ) ;
// slot name
var text = format _slot _name ( drag _slot ) ;
var bg _txt _pos _x = slot _size / 2 + spacing ;
var bg _txt _pos _y = - slot _size / 2 ;
var bg _txt _dim _w = ui _width - ( 2 * margin + slot _size + spacing ) ;
var bg _txt _dim _h = slot _size ;
set _source _rgba ( stored _slot _color ) ;
draw _text _bubble ( bg _txt _pos _x , bg _txt _pos _y , bg _txt _dim _w , bg _txt _dim _h , text ) ;
}
}
}
}
}
paint . local = 0 ;
function color _wheel ( ) {
// Rather than using an array of colors, each color has its own variable, so they can be declared as attributes and saved with the patch
// But that makes the code sooo ugly...
var args = arrayfromargs ( arguments ) ;
if ( args . length == 0 ) {
// Reset to default
color _wheel _custom = [ ] ;
color _wheel _custom = color _wheel _default . slice ( ) ;
color _1 = color _wheel _default [ 0 ] ;
color _2 = color _wheel _default [ 1 ] ;
color _3 = color _wheel _default [ 2 ] ;
color _4 = color _wheel _default [ 3 ] ;
color _5 = color _wheel _default [ 4 ] ;
color _6 = color _wheel _default [ 5 ] ;
} else if ( args . length == 5 ) {
// Set color
var n = args [ 0 ] ;
var col = [ args [ 1 ] , args [ 2 ] , args [ 3 ] , args [ 4 ] ]
if ( n > 0 && n < 7 ) {
switch ( n ) {
case 1 :
color _1 = col ;
break ;
case 2 :
color _2 = col ;
break ;
case 3 :
color _3 = col ;
break ;
case 4 :
color _4 = col ;
break ;
case 5 :
color _5 = col ;
break ;
case 6 :
color _6 = col ;
break ;
}
color _wheel _custom [ n - 1 ] = col ;
} else {
error ( 'color_wheel: index out of range\n' ) ;
}
}
paint _base ( ) ;
}
function setcolor ( ) {
if ( preset _color _pattr _exist ( ) ) {
var args = arrayfromargs ( arguments ) ;
var nb _args = args . length ;
var slot _nb = selected _slot ;
if ( nb _args < 1 && nb _args > 5 ) {
error ( "color: wrong number of arguments." ) ;
} else {
if ( nb _args == 0 ) {
// Reset colors of selected slot to default values
slots [ selected _slot ] . init _color ( ) ;
} else if ( nb _args == 1 ) {
// Set the color index of the currently selected slot (for when color_mode is 2)
slots [ selected _slot ] . color _index = Math . floor ( args ) ;
} else if ( nb _args == 2 ) {
// Set the color index to the 2nd argument for the slot number defined by the 1st argument
slot _nb = Math . floor ( args [ 0 ] ) ;
slots [ slot _nb ] . color _index = Math . floor ( args [ 1 ] ) ;
} else if ( nb _args == 4 ) {
// Set the custom color of the currently selected slot (for when color_mode is 3)
slots [ selected _slot ] . color _custom = [ args [ 0 ] , args [ 1 ] , args [ 2 ] , args [ 3 ] ] ;
} else if ( nb _args == 5 ) {
// Set the custom color for the slot number defined by the 1st argument to the color defined by following arguments in rgba format.
slot _nb = Math . floor ( args [ 0 ] ) ;
slots [ slot _nb ] . color _custom = [ args [ 1 ] , args [ 2 ] , args [ 3 ] , args [ 4 ] ] ;
}
update _preset _color _pattr ( slot _nb ) ;
paint _base ( ) ;
trigger _writeagain ( ) ;
}
}
}
function preset _color _pattr _exist ( ) {
var obj = this . patcher . getnamed ( "preset_color" ) ;
if ( ! obj ) {
error ( "preset_color pattr not found.\n" ) ;
color _pattr = 0 ;
return false ;
} else if ( obj . maxclass != "pattr" ) {
error ( "preset_color named object is not a pattr object.\n" ) ;
color _pattr = 0 ;
return false ;
} else if ( obj . getattr ( 'invisible' ) == 1 ) {
error ( "preset_color has been found but has invisible attribute set to 1\n" ) ;
color _pattr = 0 ;
return false ;
} else {
color _pattr = obj ;
return true ;
}
}
preset _color _pattr _exist . local = 1 ;
function update _preset _color _pattr ( s ) {
var cstm = slots [ s ] . color _custom ;
to _pattrstorage ( "setstoredvalue" , "preset_color" , s , slots [ s ] . color _index , cstm [ 0 ] , cstm [ 1 ] , cstm [ 2 ] , cstm [ 3 ] ) ;
}
update _preset _color _pattr . local = 1
function get _all _preset _colors ( ) {
if ( filled _slots . length ) {
for ( var i = 0 ; i < filled _slots . length ; i ++ ) {
get _preset _color ( filled _slots [ i ] ) ;
}
requested _slot = - 1 ;
}
}
get _all _preset _colors . local = 1 ;
function get _preset _color ( s ) {
requested _slot = s ;
to _pattrstorage ( "getstoredvalue" , "preset_color" , s ) ;
}
get _preset _color . local = 1 ;
function preset _color ( ) {
var args = arrayfromargs ( arguments ) ;
// post(pattrstorage_name, "preset_color", args, '----- args.length: ', args.length, '----- requested_slot: ', requested_slot,'\n');
if ( args . length == 5 ) {
var col = Math . max ( 0 , Math . floor ( args [ 0 ] ) ) % color _wheel _size ;
slots [ requested _slot ] . color _index = col ;
slots [ requested _slot ] . color _custom = [ args [ 1 ] , args [ 2 ] , args [ 3 ] , args [ 4 ] ] ;
} else if ( args . length == 4 ) {
slots [ requested _slot ] . color _index = 0 ;
slots [ requested _slot ] . color _custom = args ;
} else if ( args . length == 1 ) {
var col = Math . max ( 0 , Math . floor ( args ) ) % color _wheel _size ;
slots [ requested _slot ] . color _index = col ;
slots [ requested _slot ] . color _custom = stored _slot _color ;
}
}
function anything ( ) {
// Here just to avoid error messages in case pattrstorage sends unhandled message, like when using getstoredvalue, getsubscriptionlist, getalias, etc.
// Handle the "delete" messages here because we can't declare a "function delete" (it is a reserved word in js and cannot be used as a function name.
if ( messagename == "delete" ) {
var v = arrayfromargs ( arguments ) [ 0 ] ;
v = Math . floor ( v ) ;
if ( v >= 0 ) {
if ( slots [ v ] . lock > 0 ) {
error ( 'cannot delete locked slot ' + v + '\n' ) ;
} else {
slots [ v ] . name = null ;
slots [ v ] . interp = - 1 ;
if ( active _slot == v ) {
active _slot = 0 ;
} else if ( previous _active _slot == v ) {
previous _active _slot = 0 ;
}
// to_pattrstorage("getslotname", v);
to _pattrstorage ( "delete" , v ) ;
to _pattrstorage ( "getslotlist" ) ;
paint _base ( ) ;
set _active _slot ( active _slot ) ;
if ( ! is _dragging ) {
outlet ( 0 , "delete" , v ) ;
if ( selected _slot == v ) {
selected _slot == 0
outlet ( 2 , 'set' ) ;
outlet ( 3 , 'set' , 0 ) ;
}
}
trigger _writeagain ( ) ;
}
}
} else {
// Passthrough to pattrstorage
var args = arrayfromargs ( arguments ) ;
args . unshift ( messagename ) ;
to _pattrstorage . apply ( null , args ) ;
// If the called function messes with presets, we resync the jsui
var mess _with _presets = [ 'insert' , 'lockall' , 'read' , 'readagain' , 'remove' , 'renumber' ] ;
if ( mess _with _presets . indexOf ( messagename ) > - 1 ) {
resync ( ) ;
}
}
}
function bang ( ) {
to _pattrstorage ( "recall" , active _slot ) ;
}
function msg _int ( v ) {
to _pattrstorage ( "recall" , v ) ;
}
function msg _float ( v ) {
var s = Math . floor ( v ) ;
var i = v % 1 ;
to _pattrstorage ( "recall" , s , s + 1 , i ) ;
}
function pattrstorage ( v ) {
find _pattrstorage ( v ) ;
paint _base ( ) ;
}
function slotlist ( ) {
filled _slots = arrayfromargs ( arguments ) ;
if ( filled _slots . length ) {
// If the highest numbered preset is above the maximum number of displayed presets, we need to extend slots[]
slots _highest = filled _slots [ filled _slots . length - 1 ] ;
if ( slots _count _display < slots _highest ) {
for ( var i = slots _count _display + 1 ; i <= slots _highest ; i ++ ) {
slots [ i ] = new slot ( ) ;
slots [ i ] . init ( ) ;
}
}
for ( var i = 0 ; i < filled _slots . length ; i ++ ) {
to _pattrstorage ( "getslotname" , filled _slots [ i ] ) ;
}
get _all _preset _colors ( ) ;
}
}
function slotname ( ) {
var args = arrayfromargs ( arguments ) ;
if ( args [ 0 ] > 0 && args [ 1 ] != "(undefined)" ) {
slots [ args [ 0 ] ] . name = args [ 1 ] ;
}
}
function setslotname ( ) {
// Because [pattrstorage] doesn't output anything when renaming presets with "slotname", we use a custom "setslotname" instead, that will rename the active preset
if ( active _slot > 0 ) {
var sname = arrayfromargs ( arguments ) . join ( ' ' ) ;
slotname ( selected _slot , sname ) ;
to _pattrstorage ( "slotname" , selected _slot , sname ) ;
update _umenu ( ) ;
select ( selected _slot ) ;
trigger _writeagain ( ) ;
if ( layout == 1 ) {
paint _base ( ) ;
}
}
}
function text ( ) {
setslotname ( arrayfromargs ( arguments ) . join ( ' ' ) ) ;
}
function recall ( ) {
var args = arrayfromargs ( arguments ) ;
if ( args . length == 1 ) {
previous _active _slot = active _slot ;
is _interpolating = 0 ;
set _active _slot ( args [ 0 ] ) ;
outlet ( 0 , 'recall' , args [ 0 ] ) ;
} else if ( args . length == 3 ) {
var src _slot = args [ 0 ] ;
var trg _slot = args [ 1 ] ;
for ( var i = 0 ; i < filled _slots . length ; i ++ ) {
slots [ filled _slots [ i ] ] . interp = - 1 ;
}
if ( slots [ src _slot ] . name != null && slots [ trg _slot ] . name != null ) {
if ( ignore _slot _zero == 1 && src _slot == 0 ) {
// Set src_slot as if we were interpolating from the last recalled preset different than 0
// This way we can monitor which preset we come from even if we used preset 0 as intermediary preset
if ( previous _target != active _slot ) {
// If the last target preset was through interpollation or direct recall
src _slot = previous _active _slot ;
} else {
src _slot = active _slot ;
}
}
var interp = Math . min ( 1 , Math . max ( 0 , args [ 2 ] ) ) ;
if ( interp == 0.0 ) {
slots [ src _slot ] . interp = - 1 ;
slots [ trg _slot ] . interp = - 1 ;
is _interpolating = 0 ;
if ( previous _target != active _slot ) {
previous _active _slot = active _slot ;
} else if ( args [ 0 ] != 0 ) {
previous _active _slot = args [ 0 ] ;
} else {
previous _active _slot = previous _target ;
}
set _active _slot ( src _slot ) ;
} else if ( interp == 1.0 ) {
slots [ src _slot ] . interp = - 1 ;
slots [ trg _slot ] . interp = - 1 ;
is _interpolating = 0 ;
previous _target = trg _slot ;
set _active _slot ( trg _slot ) ;
} else {
slots [ src _slot ] . interp = 1 - interp ;
slots [ trg _slot ] . interp = interp ;
is _interpolating = 1 ;
active _slot = 0 ;
}
outlet ( 0 , "recall" , src _slot , trg _slot , interp ) ;
}
}
mgraphics . redraw ( ) ;
}
function recallmulti ( ) {
var args = arrayfromargs ( arguments ) ;
var interp _slots = [ ] ;
var summed _weight = 0 ;
for ( var i = 0 ; i < args . length ; i ++ ) {
var weight = args [ i ] % 1. ;
if ( weight == 0 ) weight = 1 ;
summed _weight += weight ;
interp _slots . push ( [ Math . floor ( args [ i ] ) , weight ] ) ;
}
for ( var i = 0 ; i < interp _slots . length ; i ++ ) {
var nb = interp _slots [ i ] [ 0 ] ;
if ( slots [ nb ] . name != null ) {
interp _slots [ i ] [ 1 ] /= summed _weight ;
} else {
interp _slots [ i ] [ 1 ] = - 1 ;
}
slots [ nb ] . interp = interp _slots [ i ] [ 1 ]
}
is _interpolating = 1 ;
mgraphics . redraw ( ) ;
outlet ( 0 , "recallmulti" , args ) ;
}
function store ( v ) {
v = Math . floor ( v ) ;
if ( v >= 0 ) {
if ( slots [ v ] && slots [ v ] . lock > 0 ) {
error ( 'cannot overwrite locked slot ' + v + '\n' ) ;
} else {
var recalc _rows _flag = scrollable && v > slots _highest ;
if ( color _pattr ) {
//Initialize preset color pattr to default for new preset (otherwise, previously set color is used)
color _pattr . message ( 0 ) ;
}
to _pattrstorage ( "store" , v ) ;
to _pattrstorage ( "getslotlist" ) ;
if ( recalc _rows _flag ) {
calc _rows _columns ( ) ;
} else {
paint _base ( ) ;
}
if ( ! ( ignore _slot _zero && v == 0 ) ) {
set _active _slot ( v ) ;
}
outlet ( 0 , "store" , v ) ;
if ( v ) {
// We writagain only if stored preset is > 0
trigger _writeagain ( ) ;
}
}
}
}
function setlock ( v ) {
lock ( selected _slot , v ) ;
}
function lock ( ) {
var args = arrayfromargs ( arguments ) ;
if ( args . length == 2 ) {
to _pattrstorage ( "lock" , args [ 0 ] , args [ 1 ] ) ;
to _pattrstorage ( "getlockedslots" ) ;
outlet ( 0 , "lock" , args [ 0 ] , args [ 1 ] ) ;
trigger _writeagain ( ) ;
if ( layout == 1 ) {
paint _base ( ) ;
}
}
}
function lockedslots ( ) {
var locked _slots = arrayfromargs ( arguments ) ;
for ( var i = 1 ; i < slots . length ; i ++ ) {
slots [ i ] . lock = 0 ;
}
if ( locked _slots . length ) {
for ( var i = 0 ; i < locked _slots . length ; i ++ ) {
slots [ locked _slots [ i ] ] . lock = 1 ;
if ( locked _slots [ i ] == selected _slot ) {
select ( selected _slot ) ;
}
}
}
}
function write ( ) {
var args = arrayfromargs ( arguments ) ;
if ( is _writing ) {
is _writing = 0 ;
var filename = args [ 0 ] ;
var state = args [ 1 ] ;
if ( state ) {
post ( pattrstorage _name + ' pattrstorage: ' + filename + ' updated\n' ) ;
} else {
error ( pattrstorage _name + ': error while writing ' + filename + '\n' ) ;
}
} else {
if ( args . length < 2 ) {
error ( "Send your write messages directly to the pattrstorage instead.\n" ) ;
}
}
}
function read ( ) {
var args = arrayfromargs ( arguments ) ;
var state = args [ 1 ] ;
if ( state ) {
pattrstorage ( pattrstorage _name ) ;
}
}
function subscriptionlist ( ) {
var client = arrayfromargs ( arguments ) [ 0 ] ;
if ( is _listening _to _subscriptionlist ) {
if ( client == "preset_color" ) {
// [pattr preset_color] subscribed
// post("preset_color pattr object found and subscribed to bound pattrstorage. Switching to color mode", color_mode_candidate, '\n');
is _listening _to _subscriptionlist = 0 ;
color _mode = color _mode _candidate ;
paint _base ( ) ;
} else if ( client == "done" ) {
error ( "A [pattr preset_color] object has been found but it isn't subscribed to your pattrstorage. Please add it to your subscribelist and try changing color mode again.\n" )
is _listening _to _subscriptionlist = 0 ;
}
}
}
function clientlist ( ) {
var client = arrayfromargs ( arguments ) [ 0 ] ;
if ( is _listening _to _clientlist ) {
if ( client == "preset_color" ) {
// post("preset_color pattr object found and client to bound pattrstorage. Switching to color mode", color_mode_candidate, '\n');
is _listening _to _clientlist = 0 ;
color _mode = color _mode _candidate ;
paint _base ( ) ;
} else if ( client == "done" ) {
error ( "A [pattr preset_color] object has been found but seems to be invisible to the pattrstorage.\n" )
is _listening _to _clientlist = 0 ;
}
}
}
function resync ( ) {
set _active _slot ( 0 ) ;
slots _clear ( ) ;
to _pattrstorage ( "getslotlist" ) ;
to _pattrstorage ( "getlockedslots" ) ;
calc _rows _columns ( ) ;
}
function find _pattrstorage ( name ) {
active _slot = 0 ;
pattrstorage _obj = this . patcher . getnamed ( name ) ;
if ( pattrstorage _obj !== null ) {
pattrstorage _name = name ;
slots _clear ( ) ;
// this.patcher.hiddenconnect(pattrstorage_obj, 0, this.box, 0);
to _pattrstorage ( "getslotlist" ) ;
to _pattrstorage ( "getlockedslots" ) ;
} else {
pattrstorage _name = null ;
active _slot = 0 ;
previous _active _slot = 0 ;
selected _slot = 0 ;
slots _clear ( ) ;
if ( name != undefined ) {
error ( "Pattrstorage" , name , "doesn't exist.\n" ) ;
}
}
}
find _pattrstorage . local = 1 ;
function to _pattrstorage ( ) {
if ( pattrstorage _obj !== null ) {
// post('To pattrstorage:', arrayfromargs(arguments), '\n');
pattrstorage _obj . message ( arrayfromargs ( arguments ) ) ;
}
}
function select ( v ) {
if ( filled _slots . indexOf ( v ) > - 1 ) {
selected _slot = v ;
set _umenu ( selected _slot ) ;
if ( selected _slot != 0 ) {
outlet ( 2 , "set" , slots [ selected _slot ] . name ) ;
} else {
outlet ( 2 , "set" ) ;
}
outlet ( 3 , "set" , slots [ selected _slot ] . lock ) ;
}
}
function slots _clear ( ) {
slots [ 0 ] . init ( ) ;
slots [ 0 ] . name = "(tmp)" ;
for ( var i = 1 ; i < slots . length ; i ++ ) {
slots [ i ] . clear ( ) ;
}
}
slots _clear . local = 1 ;
function get _slot _index ( x , y ) {
// Returns which slot is hovered by the mouse
for ( var i = 1 ; i <= slots _count _display ; i ++ ) {
if ( y > ( slots [ i ] . top - half _spacing ) && y < ( slots [ i ] . bottom + half _spacing ) && x > ( slots [ i ] . left - half _spacing ) && x < ( slots [ i ] . right + half _spacing ) ) {
return i ;
}
}
return - 1 ;
}
get _slot _index . local = 1 ;
function set _active _slot ( int ) {
if ( int < 0 ) {
active _slot = 0 ;
} else {
active _slot = int ;
}
// outlet(0, "previous", previous_active_slot);
select ( active _slot ) ;
}
set _active _slot . local = 1 ;
function update _umenu ( ) {
if ( pattrstorage _obj !== null ) {
outlet ( 1 , "clear" ) ;
outlet ( 1 , "setcheck" , 8226 ) ;
for ( var i = 0 ; i < filled _slots . length ; i ++ ) {
var nb = filled _slots [ i ] ;
var txt = null ;
if ( ! menu _number _only ) {
txt = slots [ filled _slots [ i ] ] . name ;
}
outlet ( 1 , "append" , nb , txt ) ;
}
if ( layout == 1 ) {
set _umenu ( selected _slot ) ;
}
}
}
update _umenu . local = 1 ;
function set _umenu ( v ) {
outlet ( 1 , "clearchecks" ) ;
var item = filled _slots . indexOf ( v ) ;
outlet ( 1 , "checkitem" , item ) ;
if ( menu _number _only ) {
outlet ( 1 , "setsymbol" , v ) ;
} else {
outlet ( 1 , "setsymbol" , v + ' ' + slots [ v ] . name ) ;
}
}
function trigger _writeagain ( ) {
if ( auto _writeagain && ! is _dragging ) {
is _writing = 1 ;
to _pattrstorage ( "writeagain" ) ;
}
}
trigger _writeagain . local = 1 ;
// MOUSE EVENTS
function onidle ( x , y , but , cmd , shift , capslock , option , ctrl )
{
var redraw _flag = false ;
if ( last _x != x || last _y != y - y _offset ) {
last _x = x ;
last _y = y - y _offset ;
var cur = get _slot _index ( x , y - y _offset ) ;
if ( cur != last _hovered ) {
last _hovered = cur ;
redraw _flag = true ;
}
}
if ( shift _hold != shift || option _hold != option ) {
shift _hold = shift ;
option _hold = option ;
redraw _flag = true ;
}
if ( redraw _flag ) {
mgraphics . redraw ( ) ;
}
}
onidle . local = 1 ;
function onidleout ( )
{
last _hovered = - 1 ;
mgraphics . redraw ( ) ;
}
onidleout . local = 1 ;
function onclick ( x , y , but , cmd , shift , capslock , option , ctrl )
{
if ( last _hovered > - 1 && pattrstorage _name != null ) {
var output = "recall" ;
if ( select _mode ) {
output = "select" ;
}
if ( shift ) {
output = "store" ;
if ( option ) {
output = "delete" ;
}
} else if ( slots [ last _hovered ] . name == null ) {
return ;
}
if ( output == "store" ) {
store ( last _hovered ) ;
} else {
if ( output == "select" ) {
select ( last _hovered ) ;
// mgraphics.redraw();
} else {
to _pattrstorage ( output , last _hovered ) ;
}
}
}
last _x = x ;
last _y = y - y _offset ;
}
onclick . local = 1 ;
function ondblclick ( x , y , but , cmd , shift , capslock , option , ctrl )
{
if ( last _hovered > - 1 && pattrstorage _name != null && filled _slots . indexOf ( last _hovered ) > - 1 ) {
to _pattrstorage ( "recall" , last _hovered ) ;
}
last _x = x ;
last _y = y - y _offset ;
}
ondblclick . local = 1 ;
function ondrag ( x , y , but , cmd , shift , capslock , option , ctrl )
{
if ( pattrstorage _name != null ) {
y -= y _offset ;
if ( is _dragging == 0 && last _hovered > 0 && slots [ last _hovered ] . name !== null ) {
// To prevent mistakes, is_dragging is set to 1 only when dragging for more than 10 pixels
var dist _from _start = Math . sqrt ( ( x - last _x ) * ( x - last _x ) + ( y - last _y ) * ( y - last _y ) ) ;
if ( dist _from _start > 10 ) {
is _dragging = 1 ;
drag _slot = last _hovered ;
paint _base ( ) ;
}
} else if ( is _dragging == 1 ) {
last _hovered = get _slot _index ( x , y ) ;
if ( ! but ) {
// When the button is released, the dragging ceases
if ( last _hovered > 0 && last _hovered != drag _slot ) {
var cur _active _slot = active _slot ;
var cur _prev _active _slot = previous _active _slot ;
var offset = ( ( last _hovered <= drag _slot ) && slots [ last _hovered ] . name != null ) ? 1 : 0 ;
var offset _others = slots [ last _hovered ] . name != null ? 1 : 0 ;
var drag _slot _lock = slots [ drag _slot ] . lock ;
var recalc _rows _flag = 0
// If the slot we wan to drag is locked, we need to temporarily unlock it.
if ( drag _slot _lock ) {
lock ( drag _slot , 0 ) ;
}
// If new slot is empty we just move the drag preset here. If it's not, we move al next slots to the right
if ( slots [ last _hovered ] . name !== null ) {
if ( slots _highest == slots _count _display ) {
// If there's a stored preset in the last displayed slot, it will be pushed to a new row at insert, so we need to add a new row at redraw
recalc _rows _flag = 1 ;
}
to _pattrstorage ( "insert" , last _hovered ) ;
}
to _pattrstorage ( "copy" , drag _slot + offset , last _hovered ) ;
to _pattrstorage ( "delete" , drag _slot + offset ) ;
slots _clear ( ) ;
to _pattrstorage ( "getslotlist" ) ;
to _pattrstorage ( "getlockedslots" ) ;
// All this just to keep trace of the active and previous active slots
if ( cur _active _slot == drag _slot ) {
active _slot = last _hovered ;
} else if ( last _hovered == cur _active _slot ) {
active _slot = cur _active _slot + 1 ;
} else if ( cur _active _slot > last _hovered ) {
active _slot += offset _others ;
}
if ( cur _prev _active _slot == drag _slot ) {
previous _active _slot = last _hovered ;
} else if ( cur _prev _active _slot == last _hovered ) {
previous _active _slot = cur _prev _active _slot + 1 ;
} else if ( cur _prev _active _slot > last _hovered ) {
previous _active _slot += offset _others ;
}
// If the dragged slot was locked, relock it.
if ( drag _slot _lock ) {
lock ( last _hovered , 1 ) ;
}
outlet ( 0 , "drag" , drag _slot , last _hovered , offset ) ;
is _dragging = 0 ;
drag _slot = - 1 ;
select ( last _hovered ) ;
if ( recalc _rows _flag ) {
calc _rows _columns ( ) ;
} else {
paint _base ( ) ;
}
trigger _writeagain ( ) ;
} else {
// Drag released but not somewhere we can throw a slot in
is _dragging = 0 ;
drag _slot = - 1 ;
paint _base ( ) ;
}
} else {
// Click still hold, we keep dragging
if ( scrollable ) {
// Auto-scroll if mouse out of bounds
if ( y + y _offset < 0 && y - ( last _y - drag _scroll ) < 0 ) {
drag _scroll = 2 ;
} else if ( y + y _offset > ui _height && y - ( last _y - drag _scroll ) > 0 ) {
drag _scroll = - 2 ;
} else {
drag _scroll = 0 ;
}
y _offset += drag _scroll ;
y _offset = Math . min ( y _offset , 0 ) ;
y _offset = Math . max ( y _offset , - 1 * ( bg _height - ui _height ) ) ;
}
mgraphics . redraw ( ) ;
}
last _x = x ;
last _y = y ;
}
}
}
ondrag . local = 1 ;
function onwheel ( x , y , wheel _inc _x , wheel _inc _y , cmd , shift , caps , opt , ctrl )
{
if ( scrollable ) {
y _offset += wheel _inc _y * 100.0 ;
y _offset = Math . min ( y _offset , 0 ) ;
y _offset = Math . max ( y _offset , - 1 * ( bg _height - ui _height ) ) ;
mgraphics . redraw ( ) ;
}
}
onwheel . local = 1 ;
// RESIZING
function onresize ( w , h )
{
ui _width = w ;
ui _height = h ;
calc _rows _columns ( ) ;
to _pattrstorage ( "getslotlist" ) ;
paint _base ( ) ;
}
onresize . local = 1 ;
// 64x64 is the default jsui size. We use that to know if the object has just been created,
// in which case we resize it to a more convenient size to start with.
if ( ui _width == 64 && ui _height == 64 ) {
box . setboxattr ( "patching_rect" , box . rect [ 0 ] , box . rect [ 1 ] , 130 , 58 ) ;
}
// Allows for dynamic resizing even in presentation mode (addressing the limitation of onresize())
var pres _rect = new MaxobjListener ( this . box , "presentation_rect" , get _prect ) ;
function get _prect ( prect ) {
// post(this.patcher.wind.assoc.getattr("globalpatchername") == max.frontpatcher.wind.assoc.getattr("globalpatchername") ? 1 : 0, "\n")
onresize ( prect . value [ 2 ] , prect . value [ 3 ] )
}
get _prect . local = 1 ;
// ATTRIBUTES DECLARATION
declareattribute ( "pattrstorage" , "getpattrstorage" , "setpattrstorage" , 1 ) ;
function getpattrstorage ( ) {
if ( pattrstorage _name == null ) {
return
} else {
return pattrstorage _name ;
}
}
function setpattrstorage ( v ) {
// This method is called for the first time when the patch is loading, before the loadbang (not all objects are instanciated yet)
// With v being the value stored whithin the patcher
if ( v == null ) {
pattrstorage _name = null ;
pattrstorage _obj = null ;
} else {
pattrstorage _name = arrayfromargs ( arguments ) [ 0 ] ;
}
// post('set_pattrstorage', pattrstorage_name, '\n');
// If the loadbang already occured once, we need to retrigger here
if ( has _loaded ) {
loadbang ( ) ;
} else {
// Otherwise, we have no way to know how we're here in the code
// (was it just an attribute change? or maybe the object got copy-pasted with already set attribute, or it is being instantiated at patch load with saved attributes)
// So we have to delay the loadbang to make sure it will work in any case
// and won't be triggered before this or other objects are being instantiated completely.
var init _tsk = new Task ( delayed _init ) ;
init _tsk . schedule ( 200 ) ;
}
}
function delayed _init ( ) {
loadbang ( ) ;
arguments . callee . task . freepeer ( ) ;
}
delayed _init . local = 1 ;
declareattribute ( "bubblesize" , "getslotsize" , "setslotsize" , 1 ) ;
function getslotsize ( ) {
return slot _size ;
}
function setslotsize ( v ) {
if ( arguments . length ) {
slot _size = Math . max ( 2 , v ) ;
} else {
slot _size = 20 ;
}
calc _rows _columns ( ) ;
}
declareattribute ( "slot_round" , "getslotround" , "setslotround" , 1 ) ;
function getslotround ( ) {
return slot _round ;
}
function setslotround ( v ) {
if ( arguments . length ) {
slot _round = Math . max ( 0 , Math . min ( slot _size , v ) ) ;
} else {
slot _round = 0 ;
}
slot _round _ratio = slot _round / slot _size ;
calc _rows _columns ( ) ;
}
declareattribute ( "margin" , "getmargin" , "setmargin" , 1 ) ;
function getmargin ( ) {
return margin ;
}
function setmargin ( v ) {
if ( arguments . length ) {
margin = Math . max ( 0 , v ) ;
} else {
margin = 4 ;
}
calc _rows _columns ( ) ;
}
declareattribute ( "spacing" , "getspacing" , "setspacing" , 1 ) ;
function getspacing ( ) {
return spacing ;
}
function setspacing ( v ) {
if ( arguments . length ) {
spacing = Math . max ( 1 , v ) ;
} else {
spacing = 4 ;
}
calc _rows _columns ( ) ;
}
declareattribute ( "bgcolor" , "getbgcolor" , "setbgcolor" , 1 ) ;
function getbgcolor ( ) {
return background _color ;
}
function setbgcolor ( ) {
if ( arguments . length == 4 ) {
background _color = [ arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ] ;
} else if ( arguments . length == 0 ) {
background _color = [ 0.2 , 0.2 , 0.2 , 1 ] ;
} else {
error ( 'bgcolor: wrong number of arguments\n' ) ;
}
paint _base ( ) ;
}
declareattribute ( "empty_slot_color" , "getemptycolor" , "setemptycolor" , 1 ) ;
function getemptycolor ( ) {
return empty _slot _color ;
}
function setemptycolor ( ) {
if ( arguments . length == 4 ) {
empty _slot _color = [ arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ] ;
} else if ( arguments . length == 0 ) {
empty _slot _color = [ 0.349 , 0.349 , 0.349 , 1 ] ;
} else {
error ( 'empty_slot_color: wrong number of arguments\n' ) ;
}
paint _base ( ) ;
}
declareattribute ( "active_slot_color" , "getactiveslotcolor" , "setactiveslotcolor" , 1 ) ;
function getactiveslotcolor ( ) {
return active _slot _color ;
}
function setactiveslotcolor ( ) {
if ( arguments . length == 4 ) {
active _slot _color = [ arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ] ;
} else if ( arguments . length == 0 ) {
active _slot _color = [ 0.808 , 0.898 , 0.910 , 1 ] ;
} else {
error ( 'active_slot_color: wrong number of arguments\n' ) ;
}
mgraphics . redraw ( ) ;
}
declareattribute ( "stored_slot_color" , "getstoredslotcolor" , "setstoredslotcolor" , 1 ) ;
function getstoredslotcolor ( ) {
return stored _slot _color ;
}
function setstoredslotcolor ( ) {
if ( arguments . length == 4 ) {
stored _slot _color = [ arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ] ;
} else if ( arguments . length == 0 ) {
stored _slot _color = [ 0.502 , 0.502 , 0.502 , 1 ] ;
} else {
error ( 'stored_slot_color: wrong number of arguments\n' ) ;
}
paint _base ( ) ;
}
declareattribute ( "interp_slot_color" , "getinterpslotcolor" , "setinterpslotcolor" , 1 ) ;
function getinterpslotcolor ( ) {
return interp _slot _color ;
}
function setinterpslotcolor ( ) {
if ( arguments . length == 4 ) {
interp _slot _color = [ arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ] ;
} else if ( arguments . length == 0 ) {
interp _slot _color = [ 1.0 , 1.0 , 1.0 , 0.8 ] ;
} else {
error ( 'interp_slot_color: wrong number of arguments\n' ) ;
}
mgraphics . redraw ( ) ;
}
declareattribute ( "text_bg_color" , "gettextbgcolor" , "settextbgcolor" , 1 ) ;
function gettextbgcolor ( ) {
return text _bg _color ;
}
function settextbgcolor ( ) {
if ( arguments . length == 4 ) {
text _bg _color = [ arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ] ;
} else if ( arguments . length == 0 ) {
text _bg _color = [ 1 , 1 , 1 , 0.5 ] ;
} else {
error ( 'text_bg_color: wrong number of arguments\n' ) ;
}
mgraphics . redraw ( ) ;
}
declareattribute ( "text_color" , "gettextcolor" , "settextcolor" , 1 ) ;
function gettextcolor ( ) {
return text _color ;
}
function settextcolor ( ) {
if ( arguments . length == 4 ) {
text _color = [ arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ] ;
} else if ( arguments . length == 0 ) {
text _color = [ 0.129 , 0.129 , 0.129 , 1 ] ;
} else {
error ( 'text_color: wrong number of arguments\n' ) ;
}
mgraphics . redraw ( ) ;
}
declareattribute ( "fontsize" , "getfontsize" , "setfontsize" , 1 ) ;
function getfontsize ( ) {
return font _size ;
}
function setfontsize ( v ) {
if ( arguments . length ) {
font _size = Math . max ( 2 , v ) ;
} else {
font _size = 14 ;
}
if ( layout == 1 ) {
paint _base ( ) ;
} else {
mgraphics . redraw ( ) ;
}
}
declareattribute ( "fontname" , "getfontname" , "setfontname" , 1 ) ;
function getfontname ( ) {
return font _name ;
}
function setfontname ( v ) {
if ( arguments . length ) {
var fontlist = mgraphics . getfontlist ( ) ;
if ( fontlist . indexOf ( v ) > - 1 ) {
font _name = v . toString ( ) ;
} else {
error ( "Font not found.\n" ) ;
}
} else {
font _name = 'Arial' ;
}
if ( layout == 1 ) {
paint _base ( ) ;
} else {
mgraphics . redraw ( ) ;
}
}
declareattribute ( "autowriteagain" , "getautowriteagain" , "setautowriteagain" , 1 ) ;
function getautowriteagain ( ) {
return auto _writeagain ;
}
function setautowriteagain ( v ) {
if ( v == 0 ) {
auto _writeagain = 0 ;
} else {
auto _writeagain = 1 ;
}
}
declareattribute ( "ignoreslotzero" , "getignoreslotzero" , "setignoreslotzero" , 1 ) ;
function getignoreslotzero ( ) {
return ignore _slot _zero ;
}
function setignoreslotzero ( v ) {
if ( v == 0 ) {
ignore _slot _zero = 0 ;
} else {
ignore _slot _zero = 1 ;
}
}
declareattribute ( "display_interp" , "getdisplayinterp" , "setdisplayinterp" , 1 ) ;
function getdisplayinterp ( ) {
return display _interp ;
}
function setdisplayinterp ( v ) {
if ( v == 0 ) {
display _interp = 0 ;
} else {
display _interp = 1 ;
}
}
declareattribute ( "layout" , "getlayout" , "setlayout" , 1 ) ;
function getlayout ( ) {
return layout ;
}
function setlayout ( v ) {
if ( v == 0 ) {
layout = 0 ;
} else {
layout = 1 ;
}
y _offset = 0 ;
calc _rows _columns ( ) ;
}
declareattribute ( "scrollable" , "getscrollable" , "setscrollable" , 1 ) ;
function getscrollable ( ) {
return scrollable ;
}
function setscrollable ( v ) {
if ( v == 0 ) {
scrollable = 0 ;
} else {
scrollable = 1 ;
}
y _offset = 0 ;
calc _rows _columns ( ) ;
}
declareattribute ( "min_rows" , "getmin_rows" , "setmin_rows" , 1 ) ;
function getmin _rows ( ) {
return min _rows ;
}
function setmin _rows ( v ) {
if ( v > 0 ) {
min _rows = v ;
}
if ( scrollable ) {
calc _rows _columns ( ) ;
}
}
declareattribute ( "select_mode" , "getselect_mode" , "setselect_mode" , 1 ) ;
function getselect _mode ( ) {
return select _mode ;
}
function setselect _mode ( v ) {
if ( v == 1 ) {
select _mode = 1 ;
} else {
select _mode = 0 ;
}
mgraphics . redraw ( ) ;
}
declareattribute ( "color_mode" , "getcolor_mode" , "setcolor_mode" , 1 ) ;
function getcolor _mode ( ) {
return color _mode ;
}
function setcolor _mode ( v ) {
v = Math . floor ( v ) ;
v = Math . max ( 0 , Math . min ( 3 , v ) ) ;
// For color modes 2 and 3 (select and custom),
// we need to ensure there's a [pattr preset_color] somewhere to store the preset color
if ( v >= 2 ) {
if ( ! preset _color _pattr _exist ( ) ) {
v = 0 ;
color _mode = v ;
paint _base ( ) ;
} else {
if ( pattrstorage _obj != null && pattrstorage _obj . getattr ( 'subscribemode' ) == 1 ) {
// If the pattrstorage is in subscribe mode, we need to query its subscription list,
// ...and wait for the result to continue (see function subscribelist)
post ( pattrstorage _name , "subscribe mode detected. Checking for subscribed 'preset_color' client.\n" ) ;
is _listening _to _subscriptionlist = 1 ;
color _mode _candidate = v ;
to _pattrstorage ( "getsubscriptionlist" ) ;
} else {
// If not in subscribe mode
is _listening _to _clientlist = 1 ;
color _mode _candidate = v ;
to _pattrstorage ( "getclientlist" ) ;
}
}
} else {
color _mode = v ;
paint _base ( ) ;
}
}
declareattribute ( "color_1" , "getcolor1" , "setcolor1" , 1 ) ;
function getcolor1 ( ) {
return color _1 ;
}
function setcolor1 ( ) {
if ( arguments . length == 4 ) {
color _wheel ( 1 , arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ) ;
} else if ( arguments . length == 0 ) {
color _wheel ( 1 , 0.743 , 0.41 , 0.501 , 1 ) ;
} else {
error ( 'color_1: wrong number of arguments\n' ) ;
}
}
declareattribute ( "color_2" , "getcolor2" , "setcolor2" , 1 ) ;
function getcolor2 ( ) {
return color _2 ;
}
function setcolor2 ( ) {
if ( arguments . length == 4 ) {
color _wheel ( 2 , arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ) ;
} else if ( arguments . length == 0 ) {
color _wheel ( 0.679 , 0.405 , 0.669 , 1 ) ;
} else {
error ( 'color_2: wrong number of arguments\n' ) ;
}
}
declareattribute ( "color_3" , "getcolor3" , "setcolor3" , 1 ) ;
function getcolor3 ( ) {
return color _3 ;
}
function setcolor3 ( ) {
if ( arguments . length == 4 ) {
color _wheel ( 3 , arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ) ;
} else if ( arguments . length == 0 ) {
color _wheel ( 3 , 0.527 , 0.459 , 0.756 , 1 ) ;
} else {
error ( 'color_3: wrong number of arguments\n' ) ;
}
}
declareattribute ( "color_4" , "getcolor4" , "setcolor4" , 1 ) ;
function getcolor4 ( ) {
return color _4 ;
}
function setcolor4 ( ) {
if ( arguments . length == 4 ) {
color _wheel ( 4 , arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ) ;
} else if ( arguments . length == 0 ) {
color _wheel ( 4 , 0.367 , 0.542 , 0.712 , 1 ) ;
} else {
error ( 'color_4: wrong number of arguments\n' ) ;
}
}
declareattribute ( "color_5" , "getcolor5" , "setcolor5" , 1 ) ;
function getcolor5 ( ) {
return color _5 ;
}
function setcolor5 ( ) {
if ( arguments . length == 4 ) {
color _wheel ( 5 , arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ) ;
} else if ( arguments . length == 0 ) {
color _wheel ( 5 , 0.283 , 0.606 , 0.559 , 1 ) ;
} else {
error ( 'color_5: wrong number of arguments\n' ) ;
}
}
declareattribute ( "color_6" , "getcolor6" , "setcolor6" , 1 ) ;
function getcolor6 ( ) {
return color _6 ;
}
function setcolor6 ( ) {
if ( arguments . length == 4 ) {
color _wheel ( 6 , arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] ) ;
} else if ( arguments . length == 0 ) {
color _wheel ( 6 , 0.316 , 0.616 , 0.377 , 1 ) ;
} else {
error ( 'color_6: wrong number of arguments\n' ) ;
}
}
// UTILITY
function post _keys ( obj ) {
post ( 'Keys of obj: ' , obj , '\n' ) ;
post ( Object . keys ( obj ) ) ;
post ( '\n' ) ;
}