2013-07-08 2 views
0

Je:JTextPane - Bullet avec la liste des HTMLEditorKit ne rendant pas correctement à moins que je ne setText (getText()) et repeindre

JTextPane jtextPane = new JTextPane(); 
jtextPane.setEditorKit(new HTMLEditorKit()); 
... 

Puis plus tard, je tente d'ajouter un bouton liste non numérotée à la barre d'outils tels que l'action est:

Action insertBulletAction = 
     HTMLEditorKit.InsertHTMLTextAction ("Bullets", "<ul><li> </li></ul>", 
              HTML.Tag.P, HTML.Tag.UL); 
JButton insertBulletJButton = new JButton(insertBulletAction); 

Et cela inclut le code correct si je prends une sauvegarde du code HTML généré. Cependant, il sera rendu très mal, comme même pas proche de raisonnable comme illustré ci-dessous:

snapshot of generated bullet

Cependant, si je fais:

jtextPane.setText(jtextPane.getText()); 
jtextPane.repaint(); 

tout va bien. Mais si je ne fais pas les deux lignes, alors aucune ne fonctionnera seule. Je peux aussi le faire fonctionner en définissant le texte avant de rendre le jtextPane visible.

C'est vraiment étrange et je ne comprends pas pourquoi je devrais faire un setText(getText()) suivi d'un repaint(). PS: Ceci est très similaire à cette question: How to implement bullet points in a JTextPane? Et cela fonctionne, sauf que ce n'est pas le rendu correctement. Je ne sais pas si cela a à voir avec HTMLEditorKit vs RTFEditorKit, mais quelque chose qui fait échouer le rendu. Le code source html sous est parfait ...

PS2: This link is also very handy mais il n'a pas non plus montré de solution.

Mise à jour: Voici le code complet tel que demandé, mais il n'y a pas grand-chose d'autre ...

public static void main(String[] args) 
{ 
    JFrame jframe = new JFrame(); 
    jframe.setSize(800, 600); 
    jframe.setVisible(true); 

    JTextPane jtextPane = new JTextPane(); 
    jtextPane.setEditorKit(new HTMLEditorKit()); 

    Action insertBulletAction = new HTMLEditorKit.InsertHTMLTextAction ("Bullets", 
            "<ul><li> </li></ul>", HTML.Tag.P, HTML.Tag.UL); 
    JButton insertBulletJButton = new JButton(insertBulletAction); 
    insertBulletJButton.setRequestFocusEnabled(false); 

    jframe.setLayout(new BorderLayout()); 
    jframe.add(new JScrollPane(jtextPane)); 
    jframe.add(insertBulletJButton, BorderLayout.SOUTH); 
} 
+0

exemple de code lié par @StanislavL se fait dans le document (modèle pour JTextComponents) – mKorbel

+0

Mais je ne fais pas des insertions dans le document ou setText, je suis juste essayer d'utiliser la fonctionnalité Java intégrée. Et cela fonctionne, la source dans le document est correcte. C'est juste que le JTextPane n'est pas rendu correctement à moins que je ne réinitialise le texte ... –

+0

Pour une meilleure aide plus tôt, postez un [SSCCE] (http://sscce.org/). –

Répondre

2

La réponse est en fait assez complexe. Fondamentalement, le InsertHtmlAction n'est tout simplement pas assez bon en soi. Vous avez besoin de beaucoup de travail et de logique pour accéder à une action de liste de travail. Cela demande beaucoup de logique! Vous devez donc certainement remplacer la classe Action. Fondamentalement les paramètres du InsertHtmlAction changeront selon où vous êtes dans le code html. Cela dit, j'ai étudié plusieurs solutions open source pour mieux comprendre ce qui était impliqué. Beaucoup de longues heures plus tard (et beaucoup d'heures passées à l'avance aussi), et j'ai finalement été capable de le comprendre assez bien pour ce dont j'avais besoin. Mais c'est assez complexe. Trop complexe pour écrire ici, il faudrait un chapitre d'un livre pour expliquer les concepts. Et même alors, je suis toujours flou sur certains détails (je travaille encore à travers).

Je peux maintenant comprendre pourquoi les gens vendent des composants pour cela! J'ai trouvé que la plupart des solutions Open Source ne traitent pas vraiment bien les listes. Ils fonctionnent généralement un peu mais la plupart ont des bugs flagrants. Cela ou ils ne s'occupent vraiment que des cas les plus élémentaires de listes.

Voici une liste de systèmes que j'ai consultés pour comprendre comment ils fonctionnent pour avoir une meilleure compréhension de tout. J'ai malheureusement trouvé la documentation manquante ou difficile à comprendre, alors regarder ces projets m'a aidé plus que toute autre chose.

Le plus utile

  • Shef - le plus utile de tous.
  • ekit - décent, mais de nombreux bugs et pas la meilleure organisation du code
  • MetaphaseEditor - Tout comme EKIT

moyennement utile (plus complexe, buggy, moins pertinentes, etc.)

  • OOoBean - J'ai essayé mais trop (et donc trop de complexité) pour ce dont j'avais besoin. Ça a l'air vraiment bien, vous avez juste besoin d'investir du temps.
  • JXHTMLEdit - intérêts Semblait

Liens supplémentaires

  • JWebEngine - La plupart du temps pour rendre
  • Joeffice - Intéressant mais il est toutes les vidéos et ne suffisait pas encore assez prêt.
  • Richtext - Pas de commentaires. Je n'ai regardé que brièvement.
  • JRichTextEditor - Pas de commentaires non plus, même chose.

Paid

  • JWord - Regardez très intéressant, mais il était au-delà du budget pour ce que je faisais.
1

Pour ceux qui ont besoin d'une explication plus spécifique de la façon particulière dont HTMLEditorKit gère les listes, tout se résume au balisage généré. Je vais essayer de le garder aussi simple que possible. Revenons un peu et parlons de documents HTML dans Swing.

Il s'avère que Swing s'appuie sur les paragraphes pour faire le positionnement et la navigation du curseur. Par exemple, chaque fois que vous écrivez dans une nouvelle ligne, un nouveau pagraphe est généré. Même la vue correspondante du document dépend de la présence de paragraphes aux bons endroits. Il doit toujours y avoir un paragraphe dans le document. Sinon, des choses étranges commencent à se produire.

Alors, que se passe-t-il si le document est complètement vide? Sûrement, il n'y a pas besoin d'un paragraphe là. Eh bien, incroyablement, même dans ce cas, il y a un paragraphe. C'est l'un des effets de ce que la documentation appelle p-implicite ou le paragraphe implicite. Le code HTML généré pour un document vierge est:

<html> 
    <head></head> 
    <body> 
    <p style="margin-top: 0"> 

    </p> 
    </body> 
</html> 

Expectedly, lorsque vous insérez une liste, il est placé à l'intérieur paragraphe:

<html> 
    <head></head> 
    <body> 
    <p style="margin-top: 0"> 
     <ul> 
     <li> 

     </li> 
     </ul> 
    </p> 
    </body> 
</html> 

... ce qui est bien sûr une balise invalide (pas seulement parce qu'il n'y a pas de titre dans la tête). Mais attends! Ça devient plus intéressant.Une fois la liste insérée, le "pointeur interne" du document, pour ainsi dire, reste après la balise de fermeture </ul>. Par conséquent, si vous tapez « Bonjour », il sera placé en dehors de la liste:

<html> 
    <head></head> 
    <body> 
    <p style="margin-top: 0"> 
     <ul> 
     <li> 

     </li> 
     </ul> 
     Hello 
    </p> 
    </body> 
</html> 

C'est pourquoi que « Bonjour » apparaît vers la droite par rapport à la balle insérée. Maintenant, comme Stephane mentionné dans la question, setText(getText()) résout par magie le problème. En effet, la définition manuelle du contenu de l'instance JTextPane déclenche l'analyseur, qui à son tour place le "pointeur interne" là où il devrait être; dans la liste. Maintenant, lorsque vous tapez "Bonjour", il apparaîtra beaucoup plus près de la balle. Je dis beaucoup plus parce qu'il ya encore quelque chose ne va pas sur le HTML:

<html> 
    <head></head> 
    <body> 
    <p style="margin-top: 0"> 
     <ul> 
     <li> 
      Hello 
     </li> 
     </ul>  
    </p> 
    </body> 
</html> 

avis il n'y a pas de paragraphe enfermant le nouveau texte dans la liste. C'est pourquoi le texte n'apparaîtra pas juste à côté de la balle.

Comment allez-vous tout cela? Eh bien, c'est le truc délicat dont parlait Stéphane. Vous seriez confronté à une combinaison de bugs (tels que this one), des problèmes non documentés (comme this one) et le comportement par défaut comme nous l'avons vu. La solution la plus simple consiste à utiliser l'une des solutions de la liste de Stephane. Je suis d'accord Shef est le meilleur de tous, mais n'a pas eu beaucoup d'activité depuis 2009 (!). Personnellement, j'ai trouvé Stanislav's website incroyablement utile pour toutes les choses EditorKit.

Vous pouvez également jeter un oeil à ADAPRO: un éditeur d'assistance open-source assez stable dans lequel j'étais fortement impliqué. Les fonctions d'assistance sont boguées mais la fonctionnalité d'édition principale a été testée minutieusement. Le code suivant provient de ce projet. Il nécessite la classe ElementWriter de SHEF'snet.atlanticbb.tantlinger.ui.text package.

//HTML representation of an empty paragraph 
    private static final String sEmptyParagraph = "<p style=\"margin-top: 0\"></p>"; 

    /** 
    * Translates into HTML a given element of the document model. 
    * @param element Element to serialise to a HTML string 
    * @param out Serialiser to HTML string 
    * @return HTML string "equivalent" to given element 
    */ 
    static String extractHTML (Element element, StringWriter out) { 

     ElementWriter writer = new ElementWriter (out, element); 
     try { 
      writer.write(); 
     } catch (IOException e) { 
       System.out.println ("Error encountered when serialising element: " +e); 
       e.printStackTrace(); 
     } catch (BadLocationException e) { 
       System.out.println ("Error encountered when extracting HTML at the element's position: " +e); 
       e.printStackTrace(); 
     } 
     return out.toString(); 
    } 

    /** 
    * Determines if the parent element of the current paragraph element is one of a number provided as a list 
    * of tag names. If so, it returns the parent element. 
    * @param document Document model of the text 
    * @param iCaretPos Caret's current position 
    * @param sTags Possible parent tags 
    * @return Parent element 
    */ 
    static Element getNearestParent (HTMLDocument document, int iCaretPos, String sTags) { 
     Element root; 

     root = document.getParagraphElement (iCaretPos); 
     do { 
      root = root.getParentElement(); 
     } while (sTags.indexOf (root.getName()) == -1); 
     return root; 
    } 

    /** 
    * Inserts all HTML tags required to build an ordered/unordered list at the caret's current position. 
    * If the aim is instead to turn the numbered/bulleted paragraphs into plain ones, it takes care of 
    * deleting the necessary tags. 
    * @param sTypeList Type of list to build: "ul" or "ol". 
    * @param textArea Editable area containing text. 
    */ 
    static void insertList (String sTypeList, JTextPane textArea) { 
     boolean bOnlyListSelected;   //selection includes a list exclusively     
     int iStartIndex, iEndIndex,   //element indexes included in selection 
      iStartSel, iEndSel,    //starting and ending offset of selected text 
      iItemNo,      //total number of list items 
      i; 
     String sHTML,      //HTML code of text represented by a given element 
       sHTMLBlock,     //HTML code block to be inserted into document model 
       sRest;      //part of the text remaining unselected after the selected block     
     HTML.Tag tag;      //current list tag 
     HTMLDocument document;    //data model underlying the typed text 
     Element root,      //root element of the document model tree 
       section;     //element representing a block of text    
     SimpleAttributeSet attribIns;  //backup of current input attributes    

     //Fetches the current document 
     document = (HTMLDocument) textArea.getDocument(); 

     //Finds the topmost parent element of the current paragraph (effectively, is the list inside a table?) 
     root = getNearestParent (document, textArea.getCaretPosition(), "td body"); 

     //Range of elements included in the selection 
     iStartSel = textArea.getSelectionStart(); 
     iEndSel = textArea.getSelectionEnd(); 
     iStartIndex = root.getElementIndex (iStartSel); 
     iEndIndex = root.getElementIndex (iEndSel); 

     //HTML-related initialisations 
     sHTML = ""; 
     sHTMLBlock = ""; 
     tag = null; 

     //Checks if selection is comprised of just list items 
     i = iStartIndex; 
     bOnlyListSelected = true; 
     do { 
      tag = HTML.getTag (root.getElement(i).getName()); 

      //Is it a list tag? 
      if ((tag == null) || ((!tag.equals (HTML.Tag.OL)) && (!tag.equals (HTML.Tag.UL)))) 
       bOnlyListSelected = false; 
      i++; 
     } while (bOnlyListSelected && (i <= iEndIndex)); 

     //Back up current input attributes 
     attribIns = new SimpleAttributeSet (textArea.getInputAttributes()); 

     try { 
      //At some point in the selection there is no previous list... 
      if (!bOnlyListSelected) { 

       //Inserts <LI> tags for every text block 
       for (i = iStartIndex; i <= iEndIndex; i++) { 
        section = root.getElement(i); 
        tag = HTML.getTag (section.getName()); 

        //Retrieves current HTML 
        sHTML = extractHTML (section, new StringWriter()); 

        //If it is non-listed text, reconstitute the paragraph 
        if (tag == null) 
         sHTML = "<p style=\"margin-top: 0;\">" +sHTML+ "</p>"; 

        //Text in a list already => no nesting (delete <UL>/<OL> tags) 
        if (sHTML.indexOf("<li>") != -1) { 
         sHTML = sHTML.substring (sHTML.indexOf("<li>"), sHTML.length()); 
         sHTML = sHTML.substring (0, sHTML.lastIndexOf("</li>") + 5); 

        //Non-listed text => add <LI> tags  
        } else sHTML = "<li>" +sHTML+ "</li>"; 

        sHTMLBlock = sHTMLBlock + sHTML;     
       } 
       sHTMLBlock = "<"+sTypeList+">" +sHTMLBlock.trim()+ "</"+sTypeList+">"; 

       //Gets the text coming after caret or end of selection 
       sRest = textArea.getText (iEndSel, document.getLength() - iEndSel); 

       //Adds an empty paragraph at the end of the list if the latter coincides with the end of the document 
       //or if the rest of the document is empty. This is to avoid a glitch in the editor kit's write() method. 
       //http://java-sl.com/tip_html_kit_last_empty_par.html    
       if ((root.getElement(iEndIndex).getEndOffset() == root.getEndOffset()) || 
        sRest.replaceAll ("[\\p{Z}\\s]", "").trim().isEmpty()) 
        sHTMLBlock = sHTMLBlock + sEmptyParagraph; 

       //Removes the remaining old non-listed text block and saves resulting HTML string to document model 
       document.setOuterHTML (root.getElement(iEndIndex), sHTMLBlock); 
       if (iEndIndex > iStartIndex) 
        document.remove (root.getElement(iStartIndex).getStartOffset(), 
            root.getElement(iEndIndex - 1).getEndOffset() - 
            root.getElement(iStartIndex).getStartOffset()); 

      //Selection just includes list items 
      } else { 

        //Works out the list's length in terms of element indexes 
        root = root.getElement (root.getElementIndex (iStartSel)); 
        iItemNo = root.getElementCount(); 
        iStartIndex = root.getElementIndex (textArea.getSelectionStart()); 
        iEndIndex = root.getElementIndex (textArea.getSelectionEnd()); 

        //For everery <LI> block, remove the <LI> tag 
        for (i = iStartIndex; i <= iEndIndex; i++) { 
         sHTML = extractHTML (root.getElement(i), new StringWriter());   
         sHTML = sHTML.substring(sHTML.indexOf("<li>") + 4, sHTML.length()); 
         sHTML = sHTML.substring(0, sHTML.lastIndexOf("</li>")); 
         sHTMLBlock = sHTMLBlock + sHTML;      
        } 

        //List selected partially? => divide list 
        if (iItemNo > (iEndIndex - iStartIndex + 1)) { 

         //Saves HTML string to document model 
         ((HTMLEditorKit) textArea.getEditorKit()).insertHTML (document, root.getElement(iEndIndex).getEndOffset(), 
              sHTMLBlock, 3, 0, HTML.Tag.P); 

         //Removes the old block 
         document.remove (root.getElement(iStartIndex).getStartOffset(), 
             root.getElement(iEndIndex).getEndOffset() - 
             root.getElement(iStartIndex).getStartOffset()); 

        //Removes the list tag associated with the block  
        } else document.setOuterHTML (root, sHTMLBlock.trim());      
      } 

     } catch (Exception eTexto) { 
       System.out.println ("Problemas al eliminar/insertar texto: " +eTexto); 
       eTexto.printStackTrace(); 
     } 

     //Recover selection. Previous operations displace the cursor and thus selection highlight is lost 
     textArea.setSelectionStart (iStartSel); 
     textArea.setSelectionEnd (iEndSel); 

     //If only one list item has been created and is the first one, copy all previous style information to the list 
     if ((!bOnlyListSelected) && (iStartSel == iEndSel)) { 
      textArea.setCharacterAttributes (attribIns, false); 
     }   
} 
Questions connexes