44import java .awt .Dimension ;
55import java .awt .Insets ;
66import java .awt .Point ;
7+ import java .awt .event .KeyEvent ;
78import java .beans .PropertyChangeEvent ;
89
910import javax .swing .text .AbstractDocument .BranchElement ;
1011import javax .swing .text .AttributeSet ;
12+ import javax .swing .text .BadLocationException ;
1113import javax .swing .text .Document ;
1214import javax .swing .text .Element ;
1315import javax .swing .text .JTextComponent ;
1416import javax .swing .text .StyleConstants ;
1517
18+ import javajs .util .PT ;
1619import javajs .util .SB ;
1720import swingjs .JSToolkit ;
1821import swingjs .api .js .DOMNode ;
@@ -65,6 +68,7 @@ public DOMNode updateDOMNode() {
6568 DOMNode .setStyles (domNode ); // default for pre is font-height
6669 $ (domNode ).addClass ("swingjs-doc" );
6770 setupViewNode ();
71+ System .out .println ("JSEditorPaneUI todo -- tab; adding after a tab; backspace through tab" );
6872 }
6973 textListener .checkDocument ();
7074 setCssFont (domNode , c .getFont ());
@@ -87,6 +91,8 @@ public void propertyChange(PropertyChangeEvent e) {
8791 @ SuppressWarnings ("unused" )
8892 private int epTimer ;
8993 private String currentHTML ;
94+ private static String JSTAB = "<span class='j2stab'> </span>" ;
95+ private int tabCount = 4 ;
9096
9197 @ Override
9298 public boolean handleJSEvent (Object target , int eventType , Object jQueryEvent ) {
@@ -139,6 +145,10 @@ private int getJSCharCount(DOMNode sib) {
139145 case "DIV" :
140146 n = 1 ;
141147 break ;
148+ case "SPAN" :
149+ if (isJSTAB (sib ))
150+ return 1 ;
151+ break ;
142152 }
143153
144154 return n + (/** @j2sNative sib.textContent && sib.textContent.length || */ 0 );
@@ -228,17 +238,11 @@ else if (isSup)
228238 if (haveStyle )
229239 sb .append ("<span" + style + ">" );
230240 String t = text .substring (start , isDiv ? end - 1 : end );
231- //if (start == 0) {
232241 if (t .indexOf (' ' ) >= 0 )
233242 t = t .replace (' ' , '\u00A0' );
234- // for (int i = 0; i < t.length(); i++) {
235- // if (t.charAt(i) != ' ')
236- // break;
237- // t = t.substring(0, i) + " " + t.substring(i + 1);
238- // i += 5;
239- // }
240- //System.out.println("text:'" + t + "'");
241- //}
243+ if (t .indexOf ('\t' ) >= 0 ) {
244+ t = PT .rep (t , "\t " , JSTAB );
245+ }
242246 sb .append (t );
243247 if (haveStyle )
244248 sb .append ("</span>" );
@@ -366,13 +370,16 @@ protected String getPropertyPrefix() {
366370 * @param pt target caret position
367371 * @return range information or length: [textNode,charOffset] or [nontextNode,charNodesOffset] or [null, nlen]
368372 */
373+ @ SuppressWarnings ("unused" )
369374 @ Override
370375 protected Object [] getJSNodePt (DOMNode node , int off , int pt ) {
376+ // JavaScript
371377 boolean isRoot = (off < 0 );
372378 if (isRoot ) {
373379 lastTextNode = null ;
374380 off = 0 ;
375- }
381+ }
382+ boolean isTAB = isJSTAB (node );
376383 // Must consider several cases for BR and DIV:
377384 // <br>
378385 // <div><br><div> where br counts as 1 character --> [div, 0] or [null, 1]
@@ -383,7 +390,9 @@ protected Object[] getJSNodePt(DOMNode node, int off, int pt) {
383390 // also note that range can point to a character position only if the node is #text
384391 // otherwise, it must point to a childNodes index in the parent node. So <br> must
385392 // be indicated this second way.
386-
393+ //
394+ // TAB will be indicated as a JSTAB string (see above).
395+
387396 /**
388397 * @j2sNative
389398 var nodes = node.childNodes;
@@ -395,24 +404,32 @@ protected Object[] getJSNodePt(DOMNode node, int off, int pt) {
395404 var ipt = off;
396405 var nlen = 0;
397406 var i1 = (tag == "DIV" || tag == "P" ? 1 : 0);
407+ var ptIncr = 0;
398408 for (var i = 0; i < n; i++) {
399409 node = nodes[i];
400410 if (node.innerText) {
401- ret = this.getJSNodePt$swingjs_api_js_DOMNode$I$I(node, ipt, pt, false);
411+ ret = this.getJSNodePt$swingjs_api_js_DOMNode$I$I(node, ipt, pt);
402412 if (ret[0] != null) {
403413 return ret;
404- }
414+ }
405415 nlen = ret[1];
416+ pt += ret[4];
406417 } else if (node.tagName == "BR") {
407418 if (ipt == pt)
408419 return [node.parentNode, i];
409420 nlen = (isRoot ? 1 : 0);
410- } else if (ipt + (nlen = (this.lastTextNode = node).length) >= pt) {
411- return [node, Math.max(0, pt - ipt)];
421+ } else {
422+ this.lastTextNode = node;
423+ nlen = node.length;
424+ var p = ipt + (isTAB ? 1 : nlen);
425+ if (p >= pt)
426+ return [node, Math.max(0, !isTAB ? pt - ipt : p == pt ? nlen : 0)];
427+ if (isTAB)
428+ ptIncr = nlen - 1;
412429 }
413430 ipt += nlen;
414431 }
415- return (isRoot ? [this.lastTextNode, Math.max(0, ret[3] - 1)] : [null, ipt + i1 - off, node, nlen]);
432+ return (isRoot ? [this.lastTextNode, Math.max(0, ret[3] - 1)] : [null, ipt + i1 - off, node, nlen, ptIncr ]);
416433 */
417434 {
418435 return null ;
@@ -438,6 +455,8 @@ private static Object getInnerTextSafely(DOMNode node, boolean isLast, SB sb) {
438455 DOMNode [] nodes = (DOMNode []) DOMNode .getAttr (node , "childNodes" );
439456 if (tagName == "BR" || nodes .length == 1 && DOMNode .getAttr (nodes [0 ], "tagName" ) == "BR" ) {
440457 sb .append ("\n " );
458+ } else if (tagName == "SPAN" && isJSTAB (node )) {
459+ sb .append ("\t " );
441460 } else {
442461 for (int i = 0 , n = nodes .length ; i < n ; i ++)
443462 ret = (Boolean ) getInnerTextSafely (nodes [i ], i == n - 1 , sb );
@@ -455,6 +474,12 @@ private static Object getInnerTextSafely(DOMNode node, boolean isLast, SB sb) {
455474 return (isRoot ? sb .toString () : ret );
456475 }
457476
477+ private static boolean isJSTAB (Object node ) {
478+ return node != null
479+ && (/** @j2sNative node.nodeType != 3 &&*/ true )
480+ && (((DOMNode ) node ).getAttribute ("class" ).indexOf ("j2stab" ) >= 0 );
481+ }
482+
458483 int timeoutID ;
459484
460485 @ SuppressWarnings ("unused" )
@@ -476,6 +501,10 @@ void setJSTextDelayed() {
476501 @ SuppressWarnings ("unused" )
477502 @ Override
478503 protected void jsSelect (Object [] r1 , Object [] r2 , boolean andScroll ) {
504+ fixTabRange (r1 );
505+ if (r1 != r2 )
506+ fixTabRange (r2 );
507+
479508 //System.out.println("jsSelect " + r1 + r2);
480509 // range index may be NaN
481510 /**
@@ -506,54 +535,76 @@ protected void jsSelect(Object[] r1, Object[] r2, boolean andScroll) {
506535 }
507536 }
508537
538+ /**
539+ * @param r
540+ */
541+ private void fixTabRange (Object [] r ) {
542+ DOMNode node = (DOMNode )r [0 ];
543+ boolean isStart = (/** @j2sNative r[1] || */ 0 ) == 0 ;
544+ if (isJSTAB (node )) {
545+ if (isStart ) {
546+
547+ } else {
548+
549+ }
550+
551+ }
552+ System .out .println ("jsep fixTabRange " + r + " " + isJSTAB (node ) + " " + node );
553+ }
554+
509555 @ Override
510556 public void updateJSCursorFromCaret () {
511557 updateJSCursor ("editordefault" );
512558 }
513559
514- @ SuppressWarnings ("unused" )
560+ @ SuppressWarnings ("unused" )
515561 @ Override
516- boolean getJSMarkAndDot (Point pt ) {
562+ boolean getJSMarkAndDot (Point pt , int keycode ) {
517563 int dot = 0 , mark = 0 , apt = 0 , fpt = 0 ;
518- DOMNode anode = null , fnode = null ;
564+ DOMNode anode = null , fnode = null , apar = null , fpar = null ;
519565 String atag = null , ftag = null ;
520-
566+ int alen = 0 , flen = 0 ;
567+
568+ boolean toEnd = (keycode == KeyEvent .VK_RIGHT || keycode == KeyEvent .VK_KP_RIGHT );
569+ boolean toStart = (keycode == KeyEvent .VK_LEFT || keycode == KeyEvent .VK_KP_LEFT );
521570 /**
522- * @j2sNative
571+ * @j2sNative
523572 *
524573 *
525- * var s = window.getSelection();
526- * anode = s.anchorNode;
527- * apt = s.anchorOffset;
528- * if (anode.tagName) {
529- * anode = anode.childNodes[apt];
530- * apt = 0;
531- * }
532- * fnode = s.focusNode;
533- * fpt = s.focusOffset;
534- * if (fnode.tagName) {
535- * fnode = fnode.childNodes[fpt];
536- * fpt = 0;
537- * }
574+ * var s = window.getSelection(); anode = s.anchorNode; apt =
575+ * s.anchorOffset; if (anode.tagName) { anode =
576+ * anode.childNodes[apt]; apt = 0; } else { alen = anode.length; apar
577+ * = anode.parentElement; } fnode = s.focusNode; fpt = s.focusOffset;
578+ * if (fnode.tagName) { fnode = fnode.childNodes[fpt]; fpt = 0; }
579+ * else { flen = fnode.length; fpar = fnode.parentElement; }
538580 */
539581
540582 if (anode == null || fnode == null ) {
541583 System .out .println ("JSEditorPaneUI anode or fnode is null " );
542584 return false ;
543585 }
586+ boolean isAInTab = (alen == tabCount && apt != 0 && isJSTAB (apar ));
587+ boolean isFInTab = (flen == tabCount && fpt != 0 && isJSTAB (fpar ));
588+ boolean updateJS = false ;
589+ if (isAInTab )
590+ apt = (apt == tabCount || (updateJS = toEnd ) ? 1 : 0 );
591+ if (isFInTab )
592+ fpt = (fpt == tabCount || (updateJS = toEnd ) ? 1 : 0 );
593+ if (toStart && (isAInTab && apt == 0 || isFInTab && fpt == 0 ))
594+ updateJS = true ;
544595 mark = getJSDocOffset (anode );
545596 dot = (anode == fnode ? mark : getJSDocOffset (fnode )) + fpt ;
546597 mark += apt ;
547-
548- //System.out.println("==windows at " + mark + "-" + dot + "/" + apt + " " + fpt);
549598
599+ System .out .println ("==windows at " + mark + "-" + dot + "/" + apt + " " + fpt + " " + isAInTab + " " + isFInTab );
550600 pt .x = mark ;
551601 pt .y = dot ;
552602
603+ if (updateJS )
604+ setJSSelection (mark , dot , false );
553605 return true ;
554606 }
555607
556-
557608 @ Override
558609 void setJSMarkAndDot (int mark , int dot , boolean andScroll ) {
559610 // key up with text change -- need to refresh data-ui attributes
@@ -562,13 +613,29 @@ void setJSMarkAndDot(int mark, int dot, boolean andScroll) {
562613 editor .getCaret ().setDot (dot );
563614 updateDataUI ();
564615 }
616+
617+ @ Override
618+ public boolean isFocusable () {
619+ return false ;
620+ }
565621
622+
566623 @ Override
567624 protected boolean handleTab (Object jqEvent ) {
568- // TODO check. OK? Problem is that we can't CTRL-tab out of a JEditorPane
569- return NOT_CONSUMED ;
625+ int x0 = editor .getCaret ().getMark ();
626+ int y = editor .getCaret ().getDot ();
627+ int x = Math .min (x0 , y );
628+ /** @j2sNative xxt = this.focusNode */
629+ y = Math .max (x0 , y );
630+ try {
631+ if (x < y )
632+ editor .getDocument ().remove (x , y - x );
633+ editor .getDocument ().insertString (x , "\t " , null );
634+ setJavaMarkAndDot (new Point (x + 1 , x + 1 ));
635+ } catch (BadLocationException e ) {
636+ }
637+ System .out .println ("jsep handleTab " + x + " " + y + " " + editor .getText ().replace ('\t' ,'_' ));
638+ return CONSUMED ;
570639 }
571640
572-
573-
574641}
0 commit comments