From d3027badf6764286d958e587d3d8d468d6fd241c Mon Sep 17 00:00:00 2001
From: Jonathan Wilkes <jon.w.wilkes@gmail.com>
Date: Fri, 9 Oct 2015 11:59:24 -0400
Subject: [PATCH] First draft of search bar for canvas. This still has a bug--
 if you're editing a box before you click the shortcut, the events get screwed
 up temporarily

---
 pd/nw/css/default.css |  15 ++++-
 pd/nw/pd_canvas.html  |  19 ++++++
 pd/nw/pd_canvas.js    | 152 ++++++++++++++++++++++++++++++++++--------
 pd/nw/pdgui.js        |   5 +-
 4 files changed, 159 insertions(+), 32 deletions(-)

diff --git a/pd/nw/css/default.css b/pd/nw/css/default.css
index 7c704b8b5..c4305a227 100644
--- a/pd/nw/css/default.css
+++ b/pd/nw/css/default.css
@@ -1,9 +1,8 @@
 /* Global CSS */
 
 @font-face {
-    font-family: "Droid Sans Mono Dotted";
-/*    src: url("../DejaVuSansMono.ttf"); */
-    src: url("../DroidSansMonoDotted.ttf");
+    font-family: "DejaVu Sans Mono";
+    src: url("../DejaVuSansMono.ttf");
 }
 
 .noselect {
@@ -69,6 +68,7 @@ mark.console_find_highlighted {
     background: red;
     position: fixed;
     bottom: 0;
+    left: 0;
 }
 
 /* Pure Data Patch Window (aka canvas) */
@@ -266,6 +266,15 @@ text {
     -webkit-animation: fizzle 0.5s linear 1;
 }
 
+#canvas_find {
+    width: 100%;
+    height: 1em;
+    background: red;
+    position: fixed;
+    bottom: 0;
+    left: 0;
+}
+
 /* Common to all dialogs */
 
 .submit_buttons {
diff --git a/pd/nw/pd_canvas.html b/pd/nw/pd_canvas.html
index 252014c03..d7caa3ed7 100644
--- a/pd/nw/pd_canvas.html
+++ b/pd/nw/pd_canvas.html
@@ -17,6 +17,25 @@
          overflow="visible"
          class="noselect">
     </svg>
+    <div id="canvas_find" style="display:none;">
+      <label><input type="text"
+                    id="canvas_find_text"
+                    name="canvas_find_text"
+                    defaultValue="Search in Canvas"
+                    style="width:10em"/>
+      </label>
+      <label>Match Whole Word
+        <input type="checkbox"
+               id="canvas_find_whole_word"
+               name="canvas_find_whole_word"
+               onchange="canvas_find_whole_word(this);"/> 
+      </label>
+      <input type="button"
+             id="canvas_find_button"
+             name="canvas_find_button"
+             value="Search"/>
+      </input>
+    </div>
     <script type="text/javascript" src="./pd_canvas.js"></script>
   </body>
 </html>
diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index 1982bb5b2..5d08b63a3 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -36,9 +36,37 @@ function text_to_fudi(text) {
     return text;
 }
 
+// Should probably be in pdgui.js
+function encode_for_dialog(s) {
+    s = s.replace(/\s/g, '+_');
+    s = s.replace(/\$/g, '+d');
+    s = s.replace(/;/g, '+s');
+    s = s.replace(/,/g, '+c');
+    s = s.replace(/\+/g, '++');
+    s = '+' + s;
+    return s;
+}
+
+// These three functions need to be inside canvas_events closure
+function canvas_find_whole_word(elem) {
+    canvas_events.match_words(elem.checked);
+}
+
+function canvas_find_blur() {
+    canvas_events.normal();
+}
+
+function canvas_find_focus() {
+pdgui.gui_post("flub!");
+    var state = canvas_events.get_state();
+    canvas_events.search();
+}
+
 var canvas_events = (function() {
     var name,
         state,
+        match_words_state = false,
+        last_search_term = '',
         svg_view = document.getElementById('patchsvg').viewBox.baseVal,
         textbox = function () {
             return document.getElementById('new_object_textentry');
@@ -57,7 +85,8 @@ var canvas_events = (function() {
                 return false;
             },
             mousedown: function(evt) {
-                // tk events (and, therefore, Pd evnets) are one greater than html5...
+                // tk events (and, therefore, Pd evnets) are one greater
+                // than html5...
                 var b = evt.button + 1;
                 var mod;
                 // For some reason right-click sends a modifier value of "8",
@@ -74,7 +103,7 @@ var canvas_events = (function() {
                     b, mod
                 );
                 //evt.stopPropagation();
-                evt.preventDefault();
+                //evt.preventDefault();
             },
             mouseup: function(evt) {
                 //pdgui.gui_post("mouseup: x: " +
@@ -159,7 +188,7 @@ var canvas_events = (function() {
                     pdgui.gui_canvas_sendkey(name, 1, evt, hack);
                     pdgui.set_keymap(key_code, hack);
                 }
-//                pdgui.gui_post("keydown time: keycode is " + evt.keyCode);
+                //pdgui.gui_post("keydown time: keycode is " + evt.keyCode);
                 last_keydown = evt.keyCode;
                 //evt.stopPropagation();
                 //evt.preventDefault();
@@ -179,7 +208,7 @@ var canvas_events = (function() {
 
                 pdgui.gui_canvas_sendkey(name, 1, evt, evt.charCode);
                 pdgui.set_keymap(last_keydown, evt.charCode);
-//                pdgui.gui_post("keypress time: charcode is " + evt.charCode);
+                //pdgui.gui_post("keypress time: charcode is " + evt.charCode);
                 // Don't do things like scrolling on space, arrow keys, etc.
                 //evt.stopPropagation();
                 evt.preventDefault();
@@ -187,7 +216,7 @@ var canvas_events = (function() {
             keyup: function(evt) {
                 var my_char_code = pdgui.get_char_code(evt.keyCode);
                 pdgui.gui_canvas_sendkey(name, 0, evt, my_char_code);
-//                pdgui.gui_post("keyup time: charcode is: " + my_char_code);
+                //pdgui.gui_post("keyup time: charcode is: " + my_char_code);
                 if (evt.keyCode === 13 && evt.ctrlKey) {
                     pdgui.pdsend(name, "reselect");
                 }
@@ -206,9 +235,9 @@ var canvas_events = (function() {
                     // standard in Pd-Vanilla, Pd-l2ork uses and preserves
                     // them inside comments
                     utils.create_obj();
-//                    var fudi_msg = text_to_fudi(textbox().innerText);
-//                    pdgui.pdsend(name, "createobj", fudi_msg);
-//                    pdgui.gui_post("formatted content is " + fudi_msg);
+                    //var fudi_msg = text_to_fudi(textbox().innerText);
+                    //pdgui.pdsend(name, "createobj", fudi_msg);
+                    //pdgui.gui_post("formatted content is " + fudi_msg);
                     events.mousedown(evt);
                     canvas_events.normal();
                 }
@@ -256,6 +285,24 @@ var canvas_events = (function() {
                 //evt.stopPropagation();
                 //evt.preventDefault();
                 //return false;
+            },
+            find_click: function(evt) {
+                var t = document.getElementById('canvas_find_text').value;
+                if (t !== '') {
+                    if (t === last_search_term) {
+                        pdgui.pdsend(name, 'findagain');
+                    } else {
+                        pdgui.pdsend(name, 'find',
+                        encode_for_dialog(t),
+                        match_words_state ? '1' : '0');
+                    }
+                }
+                last_search_term = t;
+            },
+            find_keydown: function(evt) {
+                if (evt.keyCode === 13) {
+                    events.find_click(evt);
+                }
             }
         },
         utils = {
@@ -308,7 +355,15 @@ var canvas_events = (function() {
             console.log("tried to savepanel something");
         }, false
     );
+    document.querySelector("#canvas_find_text").addEventListener("focusin",
+        canvas_find_focus, false
+    );
 
+    document.querySelector("#canvas_find_text").addEventListener("blur",
+        canvas_find_blur, false
+    );
+    document.querySelector("#canvas_find_button").addEventListener("click",
+        events.find_click);
     // closing the Window
     // this isn't actually closing the window yet
     nw.Window.get().on("close", function() {
@@ -365,6 +420,10 @@ var canvas_events = (function() {
             state = 'floating_text';
             set_edit_menu_modals(false);
         },
+        search: function() {
+            this.none();
+            document.addEventListener("keydown", events.find_keydown, false);
+        },
         register: function(n) {
             name = n;
         },
@@ -373,6 +432,12 @@ var canvas_events = (function() {
         },
         set_obj: function() {
             utils.set_obj();
+        },
+        create_obj: function() {
+            utils.create_obj();
+        },
+        match_words: function(state) {
+            match_words_state = state;
         }
     }
 }());
@@ -446,15 +511,31 @@ function nw_undo_menu(undo_text, redo_text) {
     }
 }
 
-function check_box_for_text() {
+function have_live_box() {
     var state = canvas_events.get_state();
     if (state === 'text' || state === 'floating_text') {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// If there's a box being edited, send the box's text to Pd
+function update_live_box() {
+    if (have_live_box()) {
         canvas_events.set_obj();
     }
 }
 
+// If there's a box being edited, try to instantiate it in Pd
+function instantiate_live_box() {
+    if (have_live_box()) {
+        canvas_events.create_obj();
+    }
+}
+
 // Menus for the Patch window
-function nw_create_patch_window_menus (name) {
+function nw_create_patch_window_menus(name) {
 
     // Window menu
     var windowMenu = new nw.Menu({
@@ -744,7 +825,22 @@ function nw_create_patch_window_menus (name) {
 
     editMenu.append(new nw.MenuItem({
         label: l('menu.find'),
-        click: menu_generic,
+        click: function () {
+            var find_bar = document.getElementById('canvas_find'),
+                find_bar_text = document.getElementById('canvas_find_text'),
+                state = find_bar.style.getPropertyValue('display');
+            // if there's a box being edited, try to instantiate it in Pd
+            instantiate_live_box();
+            if (state === 'none') {
+                find_bar.style.setProperty('display', 'inline');
+                find_bar_text.focus();
+                find_bar_text.select();
+                canvas_events.search();
+            } else {
+                find_bar.style.setProperty('display', 'none');
+                canvas_events.none();
+            }
+        },
         key: 'f',
         modifiers: "ctrl",
         tooltip: l('menu.find_tt'),
@@ -779,7 +875,7 @@ function nw_create_patch_window_menus (name) {
     editMenu.append(new nw.MenuItem({
         label: l('menu.editmode'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "editmode 0");
         },
         key: 'e',
@@ -812,7 +908,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.object'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "obj 0");
         },
@@ -824,7 +920,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.msgbox'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "msg 0");
         },
@@ -836,7 +932,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.number'),
         click: function() { 
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "floatatom 0");
         },
@@ -848,7 +944,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.symbol'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "symbolatom 0");
         },
@@ -860,7 +956,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.comment'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "text 0");
         },
@@ -876,7 +972,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.bang'),
         click: function(e) {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "bng 0");
         },
@@ -888,7 +984,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.toggle'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "toggle 0");
         },
@@ -900,7 +996,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.number2'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "numbox 0");
         },
@@ -912,7 +1008,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.vslider'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "vslider 0");
         },
@@ -924,7 +1020,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.hslider'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "hslider 0");
         },
@@ -936,7 +1032,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.vradio'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "vradio 0");
         },
@@ -948,7 +1044,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.hradio'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "hradio 0");
         },
@@ -960,7 +1056,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.vu'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "vumeter 0");
         },
@@ -972,7 +1068,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.cnv'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             pdgui.pdsend(name, "mycnv 0");
         },
@@ -988,7 +1084,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.graph'),
         click: function() {
-            check_box_for_text();
+            update_live_box();
             pdgui.pdsend(name, "dirty 1");
             // leaving out some placement logic... see pd.tk menu_graph
             pdgui.pdsend(name, "graph NULL 0 0 0 0 30 30 0 30");
@@ -999,7 +1095,7 @@ function nw_create_patch_window_menus (name) {
     putMenu.append(new nw.MenuItem({
         label: l('menu.array'),
         click: function() {
-                check_box_for_text();
+                update_live_box();
                 pdgui.pdsend(name, "dirty 1");
                 pdgui.pdsend(name, "menuarray");
             },
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index d27f6a269..53c9bed96 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -1274,7 +1274,10 @@ function gui_fontsize_to_pd_fontsize(fontsize) {
     return gobj_fontsize_kludge(fontsize, 'pd');
 }
 
-// Another hack, similar to above
+// Another hack, similar to above. We use this to
+// make sure that there is enough vertical space
+// between lines to fill the box when there is
+// multi-line text.
 function gobj_font_y_kludge(fontsize) {
     switch (fontsize) {
         case 8: return -0.5;
-- 
GitLab