From 7e9f595c1839af791ff370ba6b4ffb8efd70c329 Mon Sep 17 00:00:00 2001 From: Ivica Ico Bukvic <ico@monsoon-hp.(none)> Date: Thu, 1 Mar 2012 10:42:51 -0500 Subject: [PATCH] added support for tooltips (based on the http://sourceforge.net/tracker/?func=detail&aid=2838176&group_id=55736&atid=478072) --- src/g_editor.c | 133 ++++++++++++++++++++++++++++++-- src/g_text.c | 8 +- src/pd.tk | 205 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 335 insertions(+), 11 deletions(-) diff --git a/src/g_editor.c b/src/g_editor.c index 2b192e7e3..31c6e04fc 100644 --- a/src/g_editor.c +++ b/src/g_editor.c @@ -2591,6 +2591,7 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which, { /* look for an outlet */ int noutlet; + int ninlet; if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4) { int width = x2 - x1; @@ -2634,10 +2635,12 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which, sys_vgui(".x%x.c itemconfigure %s -outline $select_nlet_color -width $highlight_width\n", x, canvas_cnct_outlet_tag); - //sys_vgui(".x%x.c raise %s\n", - // x, - // canvas_cnct_outlet_tag); + + sys_vgui(".x%x.c raise %s\n", + x, + canvas_cnct_outlet_tag); outlet_issignal = obj_issignaloutlet(ob,closest); + //sys_vgui("pdtk_canvas_enteritem .x%x.c %d %d %s 0\n;", x, xpos, ypos, canvas_cnct_outlet_tag); } // jsarlo if(x->gl_magic_glass) { @@ -2652,6 +2655,46 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which, else if (doit) goto nooutletafterall; } + /* look for an inlet (these are colored differently since they are not connectable) */ + else if (ob && (ninlet = obj_ninlets(ob)) && ypos <= y1+4) + { + int width = x2 - x1; + int nin1 = (ninlet > 1 ? ninlet - 1 : 1); + int closest = ((xpos-x1) * (nin1) + width/2)/width; + int hotspot = x1 + + (width - IOWIDTH) * closest / (nin1); + if (closest < ninlet && + xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1)) + { + t_rtext *y = glist_findrtext(x, (t_text *)&ob->ob_g); + + if (canvas_cnct_inlet_tag[0] != 0) + { + sys_vgui(".x%x.c itemconfigure %s -outline %s -fill %s -width 1\n", + x, canvas_cnct_inlet_tag, + (last_inlet_filter ? "black" : (inlet_issignal ? "$signal_cord" : "$msg_cord")), + (inlet_issignal ? "$signal_nlet" : "$msg_nlet")); + } + + if (y) + { + last_inlet_filter = gobj_filter_highlight_behavior(y); + sprintf(canvas_cnct_inlet_tag, + "%si%d", + rtext_gettag(y), + closest); + sys_vgui(".x%x.c itemconfigure %s -width $highlight_width\n", + x, + canvas_cnct_inlet_tag); + + sys_vgui(".x%x.c raise %s\n", + x, + canvas_cnct_inlet_tag); + inlet_issignal = obj_issignalinlet(ob,closest); + //sys_vgui("pdtk_canvas_enteritem .x%x.c %d %d %s 0\n;", x, xpos, ypos, canvas_cnct_outlet_tag); + } + } + } /* not in an outlet; select and move */ else if (doit) { @@ -2690,6 +2733,14 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which, else // jsarlo { + if (canvas_cnct_inlet_tag[0] != 0) + { + sys_vgui(".x%x.c itemconfigure %s -outline %s -fill %s -width 1\n", + x, canvas_cnct_inlet_tag, + (last_inlet_filter ? "black" : (inlet_issignal ? "$signal_cord" : "$msg_cord")), + (inlet_issignal ? "$signal_nlet" : "$msg_nlet")); + } + if (canvas_cnct_outlet_tag[0] != 0) { sys_vgui(".x%x.c itemconfigure %s -outline %s -fill %s -width 1\n", @@ -2698,6 +2749,7 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which, (outlet_issignal ? "$signal_nlet" : "$msg_nlet")); canvas_cnct_outlet_tag[0] = 0; } + if(x->gl_magic_glass) { magicGlass_unbind(x->gl_magic_glass); magicGlass_hide(x->gl_magic_glass); @@ -2980,10 +3032,11 @@ void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit) sys_vgui(".x%x.c itemconfigure %s -outline $select_nlet_color -width $highlight_width\n", x, canvas_cnct_inlet_tag); - //sys_vgui(".x%x.c raise %s\n", - // x, - // canvas_cnct_inlet_tag); + sys_vgui(".x%x.c raise %s\n", + x, + canvas_cnct_inlet_tag); inlet_issignal = obj_issignalinlet(ob2, closest2); + //sys_vgui("pdtk_canvas_enteritem .x%x.c %d %d %s 0\n;", x, xpos, ypos, canvas_cnct_outlet_tag); } canvas_setcursor(x, CURSOR_EDITMODE_CONNECT); } @@ -4723,6 +4776,68 @@ static void glist_setlastxy(t_glist *gl, int xval, int yval) canvas_last_glist_y = yval; } +static void canvas_enterobj(t_canvas *x, t_symbol *item, t_floatarg xpos, + t_floatarg ypos, t_floatarg xletno) +{ + t_symbol *name = 0, *helpname, *dir; + int yoffset = 0, xoffset = 0; + if (item == gensym("inlet")) + { + yoffset = 1; + xoffset = xletno==0 ? 1 : -1; + } + else if (item == gensym("outlet")) + { + yoffset = -1; + xoffset = xletno== 0 ? 1 : -1; + } + int x1, y1, x2, y2; + t_gobj *g; + if (g = canvas_findhitbox(x, xpos+xoffset, ypos+yoffset, + &x1, &y1, &x2, &y2)) + { + if (pd_class((t_pd *)g)==canvas_class ? + canvas_isabstraction((t_canvas *)g) : 0) + { + t_canvas *z = (t_canvas *)g; + name = z->gl_name; + helpname = z->gl_name; + dir = canvas_getdir(z); + } + else + { + name = g->g_pd->c_name; + helpname = g->g_pd->c_helpname; + dir = g->g_pd->c_externdir; + } + sys_vgui("pdtk_gettip .x%lx.c %s %d \ + [list %s] [list %s] [list %s]\n", + x, item->s_name, (int)xletno, + name->s_name, helpname->s_name, dir->s_name); + } +} + +static void canvas_tip(t_canvas *x, t_symbol *s, int argc, t_atom *argv) +{ + if (s == gensym("echo")) + return; + if (argv->a_type != A_FLOAT) + error("canvas_tip: bad argument"); + else + { + sys_vgui("pdtk_tip .x%lx.c 1", x); + t_atom *at = argv; + int i; + for (i=0; i<argc; i++) + { + if (at[i].a_type == A_FLOAT) + sys_vgui(" %g", at[i].a_w.w_float); + else if (at[i].a_type == A_SYMBOL) + sys_vgui(" %s", at[i].a_w.w_symbol->s_name); + } + sys_gui("\n"); + } +} void g_editor_setup(void) { @@ -4737,6 +4852,12 @@ void g_editor_setup(void) A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)glist_noselect, gensym("noselect"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_enterobj, gensym("enter"), + A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_tip, gensym("tip"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_tip, gensym("echo"), + A_GIMME, A_NULL); /* ------------------------ menu actions ---------------------------- */ class_addmethod(canvas_class, (t_method)canvas_menuclose, gensym("menuclose"), A_DEFFLOAT, 0); diff --git a/src/g_text.c b/src/g_text.c index fc96fa683..e3d8181fe 100644 --- a/src/g_text.c +++ b/src/g_text.c @@ -1475,7 +1475,7 @@ void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime, //fprintf(stderr,"glist_drawiofor o firsttime\n"); issignal = obj_issignaloutlet(ob,i); sys_vgui(".x%lx.c create rectangle %d %d %d %d \ - -fill %s -outline %s -tags %so%d\n", + -fill %s -outline %s -tags {%so%d outlet}\n", glist_getcanvas(glist), onset, y2 - 2, onset + IOWIDTH, y2, (issignal ? "$signal_nlet" : "$msg_nlet"), (issignal ? "$signal_cord" : "$msg_cord"), @@ -1505,7 +1505,7 @@ void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime, //fprintf(stderr,"glist_drawiofor i firsttime\n"); issignal = obj_issignalinlet(ob,i); sys_vgui(".x%lx.c create rectangle %d %d %d %d \ - -fill %s -outline %s -tags %si%d\n", + -fill %s -outline %s -tags {%si%d inlet}\n", glist_getcanvas(glist), onset, y1, onset + IOWIDTH, y1 + EXTRAPIX, (issignal ? "$signal_nlet" : "$msg_nlet"), (issignal ? "$signal_cord" : "$msg_cord"), @@ -1545,7 +1545,7 @@ void glist_drawiofor_withtag(t_glist *glist, t_object *ob, int firsttime, //fprintf(stderr,"drawiofor_withtag o firsttime\n"); issignal = obj_issignaloutlet(ob,i); sys_vgui(".x%lx.c create rectangle %d %d %d %d \ - -fill %s -outline %s -tags %so%d\n", + -fill %s -outline %s -tags {%so%d outlet}\n", glist_getcanvas(glist), onset, y2 - 2, onset + IOWIDTH, y2, (issignal ? "$signal_nlet" : "$msg_nlet"), (issignal ? "$signal_cord" : "$msg_cord"), @@ -1570,7 +1570,7 @@ void glist_drawiofor_withtag(t_glist *glist, t_object *ob, int firsttime, //fprintf(stderr,"drawiofor_withtag i firsttime\n"); issignal = obj_issignalinlet(ob,i); sys_vgui(".x%lx.c create rectangle %d %d %d %d \ - -fill %s -outline %s -tags %si%d\n", + -fill %s -outline %s -tags {%si%d inlet}\n", glist_getcanvas(glist), onset, y1, onset + IOWIDTH, y1 + EXTRAPIX, (issignal ? "$signal_nlet" : "$msg_nlet"), (issignal ? "$signal_cord" : "$msg_cord"), diff --git a/src/pd.tk b/src/pd.tk index 1cd202b64..f6a8daeac 100644 --- a/src/pd.tk +++ b/src/pd.tk @@ -334,6 +334,14 @@ set global_clipboard 0 set last_clipboard 0 set global_selection 0 +#TOOLTIPS PATCH +#TODO: make a separate tcl file for tooltips and put +#afterid in its scope +variable afterid 0 +variable duplicate_tags -1 +variable current_window 0 +variable nlet_color 0 + # x:y location tooltip during dragging set tooltip [toplevel .tooltip -bd 1 -bg black] wm attributes $tooltip -topmost 1 @@ -1405,6 +1413,7 @@ if {$pd_nt == 2} { # no key command for Mac OS X -state disabled .mbar.edit add command -label {Find last error} .mbar.edit add separator +.mbar.edit add checkbutton -label [_ "Autotips"] -variable ::autotips_button .mbar.edit add command -label {Edit mode} -accelerator [accel_munge "Ctrl+e"] \ -state disabled @@ -2252,10 +2261,12 @@ proc pdtk_canvas_new {name width height geometry editable} { # instead of "red = #BC3C60" we take "grey85", so there is no difference, # if widget is selected or not. + $name.m.edit add checkbutton -label [_ "Autotips"] -variable ::autotips_button + $name.m.edit add checkbutton -label "Edit mode" \ -indicatoron false -selectcolor black \ -command [concat menu_editmode $name] \ - -accelerator [accel_munge "Ctrl+e"] + -accelerator [accel_munge "Ctrl+e"] -variable ::editmode($name) if { $editable == 1 } { $name.m.edit entryconfigure "Edit mode" -background "#7dd37d" @@ -2498,6 +2509,15 @@ proc pdtk_canvas_new {name width height geometry editable} { bind $name.c <KeyRelease> {pdtk_canvas_sendkey %W 0 %K %A 0 1} bind $name.c <Motion> {pdtk_canvas_motion %W %x %y 0} bind $name.c <Control-Motion> {pdtk_canvas_motion %W %x %y 2} + + # canvas bindings --------------------------------------------------------- + # just for tooltips right now + $name.c bind inlet <Enter> "pdtk_canvas_enteritem %W %x %y inlet %#" + $name.c bind outlet <Enter> "pdtk_canvas_enteritem %W %x %y outlet %#" + $name.c bind text <Enter> "pdtk_canvas_enteritem %W %x %y text %#" + $name.c bind inlet <Leave> "pdtk_canvas_leaveitem %W inlet" + $name.c bind outlet <Leave> "pdtk_canvas_leaveitem %W outlet" + $name.c bind text <Leave> "pdtk_canvas_leaveitem %W text" if {$pd_nt == 2} { bind $name.c <Option-Motion> {pdtk_canvas_motion %W %x %y 4} @@ -7063,3 +7083,186 @@ if { [info tclversion] >= 8.5 && $pd_nt == 0 } { } } + +proc pdtk_canvas_enteritem_gettags {tkcanvas x y item} { + variable nlet_color + if {[winfo exists $tkcanvas]} { + set mytoplevel [winfo toplevel $tkcanvas] + set id [$tkcanvas find withtag current] + set tags [$tkcanvas gettags $id] + set xletno -1 + set object text + if { [string match *o* $item] } { set object outlet } + if { [string match *i* $item] } { set object inlet } + foreach tag $tags { + if { $" $tag -- xletno] } { + # iemgui tag + regexp ".*OUT(\[0-9\]+)$" $tag -- xletno + regexp ".*IN(\[0-9\]+)$" $tag -- xletno + } + } + + set nlet_color [$tkcanvas itemcget [lindex $tags 0] -outline] + + pd [concat $mytoplevel enter $object \ + [$tkcanvas canvasx $x] [$tkcanvas canvasy $y] $xletno \;] + } +} + +proc pdtk_canvas_enteritem {tkcanvas x y item enterid} { + variable afterid + variable duplicate_tags + variable current_window + #if {$::autotips_button == 0} {return} + if {$item != $duplicate_tags} { + #if {$item eq "inlet" || + # $item eq "outlet"} { + # $tkcanvas itemconfigure $item -activewidth 5 + #} + + set duplicate_tags $item + if {$current_window eq $tkcanvas} { + after cancel $afterid + } + set current_window $tkcanvas + set afterid [after 250 pdtk_canvas_enteritem_gettags \ + $tkcanvas $x $y $item] + } +} + +# move activewidth to toggle on editmode? +proc pdtk_canvas_leaveitem {w item} { + variable afterid + variable current_window + variable duplicate_tags + #if {$::autotips_button == 0} {return} + after cancel $afterid + set afterid 0 + if {[lsearch -exact [$w gettags $w.tipwindow] "sticky"] == -1} { + #if {$item eq "inlet" || + #$item eq "outlet"} { + # $w itemconfigure $item -activewidth 0 + #} + if {[winfo exists $w.tiplabel]} { + set afterid [after 50 "pdtk_tip $w 0 0"] + set current_window $w + } + } + set duplicate_tags -1; +} + +proc pdtk_tip {w fromc show args} { + variable select_color + variable nlet_color + set exists [winfo exists $w.tiplabel] + if {$show == 0} { + catch {destroy $w.tiplabel} + catch {$w delete $w.tipwindow} + } else { + if {$exists} { + $w.tiplabel configure -text [join $args] + $w dtag $w.tipwindow "sticky" + if {$fromc == 1} { + $w addtag "sticky" withtag $w.tipwindow + } + } else { + if { $nlet_color ne $select_color } { + set fg "#ffffff" + } else { + set fg "#000000" + } + label $w.tiplabel -text [join $args] -bd 1 \ + -wraplength [winfo width $w] -bg $nlet_color -fg $fg -bd 1 \ + -padx 2 -pady 2 -relief flat + } + set yreal [expr [$w canvasy 0] * -1 + \ + [winfo pointery $w]-[winfo rooty $w]] + set yoffset 0 + if {$yreal < [expr [winfo height $w] - \ + [winfo reqheight $w.tiplabel]] - 5} { + set yoffset [winfo height $w] + set anchor "sw" + } else { + set anchor "nw" + } + set x [$w canvasx 0] + set y [expr [$w canvasy 0] + $yoffset] + set tags $w.tipwindow + if {$fromc == 1} { + lappend tags "sticky" + } + if {$exists} { + $w coords $w.tipwindow $x $y + $w itemconfigure $w.tipwindow -anchor $anchor + } else { + $w create window $x $y -window $w.tiplabel -anchor $anchor \ + -tags $tags + $w bind $w.tipwindow <Enter> "pdtk_tip_mouseover $w" + } + } +} + +# move the tip if the user happens to mouse over it +proc pdtk_tip_mouseover {w} { + set msg [$w.tiplabel cget -text] + set sticky [expr [lsearch -exact [$w gettags $w.tipwindow] \ + "sticky"] != -1] + pdtk_tip $w $sticky 1 $msg +} + +proc pdtk_gettip { w item xletno name helpname dir } { + if {$dir eq {}} { + set dir $::sys_libdir/doc/5.reference + } + # trim off trailing ".pd" for abstractions + regexp {^(.*)(?:\.pd)} $name -- name + # use $varxlet to see if an object has a + # variable xlet (marked in the docs as + # "INLET_N" or "OUTLET_N") + set varxlet {} + set metatag description + if {$item eq "inlet" || + $item eq "outlet"} { + set varxlet [join [list $item "n"] "_"] + set metatag [join [list $item $xletno] "_"] + set msg "[string toupper [string map {_ " "} $metatag] 0 0] of $name" + } elseif {$item eq "text"} { + set metatag "description" + set msg $name + } + set filefound 0 + if {![catch {set fp [open [file join $dir \ + "$name-help.pd"]]}] || + ![catch {set fp [open [file join $dir \ + "$helpname-help.pd"]]}] } { + set filefound 1 + } + if {$filefound} { + set filecontents [read $fp] + close $fp + # leave in pd's linebreaks-- serendipitously it + # makes the tipwindow more compact/easier to read + regsub -all {[{}]} $filecontents "" filecontents + # turn escaped semicolons into linebreaks + regsub -all {[\n\s]\\;[\n\s]} $filecontents "\n" filecontents + set match {} + # if $varxlet ne {} then the $item is an inlet or outlet + if { $varxlet ne {} && + [regexp -nocase \ + "#X text \[0-9\]+ \[0-9\]+ $varxlet (\[^;\]+)" \ + $filecontents] } { + set match "(variable inlet)" + } + regexp -nocase \ + "#X text \[0-9\]+ \[0-9\]+ $metatag (\[^;\]+)" \ + $filecontents -- match + if { $match ne {} } { + set msg [string trim "$msg: $match"] + } + } + # make Pd's comma atoms look pretty + regsub -all { \\,} $msg {,} msg + regsub -all {\n\\,} $msg ",\n" msg + pdtk_tip $w 0 1 $msg +} + -- GitLab