{"id":141,"date":"2009-09-08T01:26:32","date_gmt":"2009-09-07T23:26:32","guid":{"rendered":"http:\/\/grummfy.be\/blog\/?p=141"},"modified":"2009-09-08T01:39:07","modified_gmt":"2009-09-07T23:39:07","slug":"java-drag-drop-sur-un-jtable","status":"publish","type":"post","link":"https:\/\/grummfy.be\/blog\/141","title":{"rendered":"java : Drag &#038; Drop sur un JTable"},"content":{"rendered":"<h1>Drag &amp; Drop sur un JTable<\/h1>\n<p style=\"text-align: justify;\">Dans nos application moderne il semble logique de pouvoir faire du <acronym title=\"Drag&amp;Drop\">DND<\/acronym> \u00e0 gogo, pourtant ce n&rsquo;est pas des plus simple, avec certain langage de le faire,\u00a0 d&rsquo;o\u00f9 cette exemple qui m&rsquo;a fait un peu travaillez et arrachez un ou deux cheveux de ma longue chevelure de geek&#8230;<\/p>\n<p style=\"text-align: justify;\">Bref, voici l&rsquo;essentiel :<\/p>\n<ol>\n<li>Il faut que le composant puisse \u00eatre pris (drag) et\/ou d\u00e9pos\u00e9 (drop)<\/li>\n<li>Il faut convertir le composant en donn\u00e9e exportable et transportable (Transferable)<\/li>\n<li>Il faut pouvoir restaurer les donn\u00e9e correctement et suivant le type les restaurer de tel ou tel mani\u00e8re &#8230;<\/li>\n<\/ol>\n<p><!--more-->Par d\u00e9faut, java impl\u00e9mente tout une s\u00e9rie de composant d\u00e9j\u00e0 \u00ab\u00a0drag&amp;drop ready\u00a0\u00bb :<\/p>\n<p><img decoding=\"async\" src=\"file:\/\/\/tmp\/moz-screenshot.jpg\" alt=\"\" \/><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-159\" title=\"Tableau DND java\" src=\"http:\/\/grummfy.be\/blog\/wp\/wp-content\/uploads\/2009\/09\/Sans-titre.png\" alt=\"Tableau DND java\" width=\"340\" height=\"338\" srcset=\"https:\/\/grummfy.be\/blog\/wp\/wp-content\/uploads\/2009\/09\/Sans-titre.png 709w, https:\/\/grummfy.be\/blog\/wp\/wp-content\/uploads\/2009\/09\/Sans-titre-300x297.png 300w\" sizes=\"auto, (max-width: 340px) 100vw, 340px\" \/><\/p>\n<p>Dans notre cas le JTable peut-\u00eatre pris par copie mais on ne peux droper dedans&#8230;<\/p>\n<p>Donc nous devons g\u00e9r\u00e9 :<\/p>\n<ul>\n<li>Transport des donn\u00e9es : ici nos lignes \u2192 Class JTableRowTransferable<\/li>\n<li>Faire en sorte que le gestionnaire de DND accepte le drop et surtout l&rsquo;accepte de notre transport de donn\u00e9e \u2192 Class MyTransferHandler (class interne)<\/li>\n<li>Et bien entendu faire comprendre \u00e0 notre Jtable \u2192 il suffit de faire myJtable.setDragEnable(true); \ud83d\ude09<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">Une fois tout cela fait je me suis vite rendu compte que pour l&rsquo;utilisateur ce n&rsquo;\u00e9tait pas simple!!! En fait, l&rsquo;utilisateur devait s\u00e9lectionner la ligne (et pas plusieurs) et ensuite l\u00e9g\u00e8rement d\u00e9cal\u00e9 sur le cot\u00e9 (pour ne pas partir vers la s\u00e9lection d&rsquo;autre ligne) et puis seulement bouger le tout &#8230; ING\u00c9RABLE ! Bref, je me suis dit \u00ab\u00a0let&rsquo;s go pour un bouton de <span style=\"text-decoration: line-through;\">drague<\/span> drag!\u00a0\u00bb J&rsquo;ai donc chercher avec un JButton.\u00a0 Mais<sup class='footnote'><a href='#fn-141-1' id='fnref-141-1' onclick='return fdfootnote_show(141)'>1<\/a><\/sup> malheureusement pour utiliser un JButton il faudrait impl\u00e9menter un render (une class qui dit comment doit s&rsquo;afficher un objet dans une JTable) pour la JTable. Donc on oublie!<\/p>\n<p style=\"text-align: justify;\">Pour finir, je me suis rabattu vers une ImageIcon <img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-143\" title=\"Appuyez sur le &quot;bouton&quot; vert et glissez la ligne vers le haut ou le bas ...\" src=\"http:\/\/grummfy.be\/blog\/wp\/wp-content\/uploads\/2009\/09\/drag_me_green.png\" alt=\"Appuyez sur le &quot;bouton&quot; vert et glissez la ligne vers le haut ou le bas ...\" width=\"47\" height=\"14\" \/> (ou rien du tout cela aurait \u00e9t\u00e9 pareil&#8230;) et un MouseListener sur mon JTable.<\/p>\n<p style=\"text-align: justify;\">Bref, je ne vais pas vous faire un cours, pour cela je vous renvoi plus bas! Donc, je vous propose de t\u00e9l\u00e9charger l&rsquo;exemple : <a title=\"T\u00e9l\u00e9charger l'exemple\" rel=\"attachment wp-att-148\" href=\"http:\/\/grummfy.be\/blog\/?attachment_id=148\">DragTableExample.tar<\/a><\/p>\n<h2>Plus d&rsquo;info sur le DND en java ?<\/h2>\n<ul>\n<li>Excellent article sur le fonctionnement du DND : <a href=\"http:\/\/mbaron.developpez.com\/javase\/dnddt\/\" target=\"_blank\">Introduction au Drag and Drop : transfert de donn\u00e9es<\/a>[fr]<\/li>\n<li><a href=\"http:\/\/gfx.developpez.com\/tutoriel\/java\/swing\/drag\/\" target=\"_blank\">Drag &amp; Drop avec Style et l&rsquo;API Swing<\/a>[fr]<\/li>\n<li>Larticle de sun : <a href=\"http:\/\/java.sun.com\/docs\/books\/tutorial\/uiswing\/dnd\/index.html\">Lesson: Drag and Drop and Data Transfer<\/a>[en]<\/li>\n<li>Encore un autre tr\u00e8s bon article : <a href=\"http:\/\/www.javaworld.com\/javaworld\/jw-03-1999\/jw-03-dragndrop.html\">How to drag and drop with Java 2<\/a>[en]<\/li>\n<\/ul>\n<p><!--nextpage--><\/p>\n<h2>Le code<\/h2>\n<pre lang=\"java\">import java.awt.Point;\r\nimport java.awt.datatransfer.DataFlavor;\r\nimport java.awt.datatransfer.Transferable;\r\nimport java.awt.datatransfer.UnsupportedFlavorException;\r\nimport java.awt.event.MouseEvent;\r\nimport java.awt.event.MouseListener;\r\nimport java.io.IOException;\r\nimport java.util.Vector;\r\n\r\nimport javax.swing.DropMode;\r\nimport javax.swing.ImageIcon;\r\nimport javax.swing.JComponent;\r\nimport javax.swing.JFrame;\r\nimport javax.swing.JScrollPane;\r\nimport javax.swing.JTable;\r\nimport javax.swing.JTextField;\r\nimport javax.swing.TransferHandler;\r\nimport javax.swing.table.DefaultTableModel;\r\n\r\n\/**\r\n * @author Grummfy 2009\r\n * Ce code est sous licence LGPL v2\r\n * Ce code est pourri et DOIT \u00eatre am\u00e9liorer mais le but est l'exemple ...\r\n *\/\r\npublic class DragTableExample\r\n{\r\n\t\/\/\r\n\t\/\/ static\r\n\t\/\/\r\n\r\n\tpublic static void main(String... args)\r\n\t{\r\n\t\tDragTableExample dte = new DragTableExample();\r\n\r\n\t\tJFrame jf = new JFrame();\r\n\t\tjf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r\n\t\tjf.add(new JScrollPane(dte.getJTable()));\r\n\t\tjf.pack();\r\n\t\tjf.setVisible(true);\r\n\t}\r\n\r\n\t\/\/\r\n\t\/\/ la classe\r\n\t\/\/\r\n\tprivate JTable jTable;\r\n\tprivate int colCount = 11;\r\n\tprivate int rowCount = 10;\r\n\r\n\tpublic DragTableExample()\r\n\t{\r\n\t\tJTextField jtf = new JTextField(\"Drag This!\");\r\n\t\tjtf.setDragEnabled(true);\r\n\r\n\t\t\/\/ rempli le jtable\r\n\t\tthis.jTable = new JTable(new MyDefTableModel(this.mkNames(), this.mkData()));\r\n\r\n\t\t\/\/ permet de faire du drag (\u00e9j\u00e0 impl\u00e9ment\u00e9 par d\u00e9faut dans jtable\r\n\t\tthis.jTable.setDragEnabled(true);\r\n\r\n\t\t\/\/ on ajoute notre gestionnaire de clique sur la premi\u00e8ere collone (#0)\r\n\t\tthis.jTable.addMouseListener(new MouseListener()\r\n\t\t{\r\n\t\t\tpublic void mouseClicked(MouseEvent e){}\r\n\t\t\tpublic void mouseEntered(MouseEvent e){}\r\n\t\t\tpublic void mouseExited(MouseEvent e){}\r\n\t\t\tpublic void mouseReleased(MouseEvent e){}\r\n\r\n\t\t\tpublic void mousePressed(MouseEvent e)\r\n\t\t\t{\r\n\t\t\t\tPoint p = e.getPoint ();\r\n\t\t\t\tint colonne = jTable.columnAtPoint(p);\r\n\t\t\t\tint ligne = jTable.rowAtPoint(p);\r\n\t\t\t\tif (colonne == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\t\/\/ s\u00e9lection de la ligne\r\n\t\t\t\t\tjTable.getSelectionModel().setSelectionInterval(ligne, ligne);\r\n\r\n\t\t\t\t\t\/\/ lancement du drag\r\n\t\t\t\t\tTransferHandler handler = jTable.getTransferHandler();\r\n\t\t\t\t\thandler.exportAsDrag(jTable, e, TransferHandler.MOVE);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\t\/\/Le mode de drop est en mode d'isnertion de ligne =&gt; si ond \u00e9pose cela ajoute une ligne!\r\n\t\tthis.jTable.setDropMode(DropMode.INSERT_ROWS);\r\n\r\n\t\tthis.jTable.setTransferHandler(new MyTransferHandler());\r\n\t}\r\n\r\n\tpublic JTable getJTable()\r\n\t{\r\n\t\treturn this.jTable;\r\n\t}\r\n\r\n\tprivate Vector mkNames()\r\n\t{\r\n\t\tVector vNames = new Vector(this.colCount + 1);\r\n\r\n\t\tvNames.add(\"Col of drag\");\r\n\r\n\t\tfor (int colonne = 1; colonne &lt; this.colCount; colonne++)\r\n\t\t\tvNames.add(\"Col #\" + String.valueOf(colonne));\r\n\t\treturn vNames;\r\n\t}\r\n\r\n private Vector&lt;Vector&lt;Object&gt;&gt; mkData()\r\n {\r\n Vector&lt;Vector&lt;Object&gt;&gt; vData = new Vector&lt;Vector&lt;Object&gt;&gt;(this.rowCount);\r\n\r\n for (int ligne = 0; ligne &lt; this.rowCount; ligne++)\r\n {\r\n Vector&lt;Object&gt; vCol = new Vector&lt;Object&gt;(this.colCount);\r\n\r\n for(int colonne = 0; colonne &lt; this.colCount; colonne++)\r\n {\r\n if (colonne == 0)\r\n vCol.add(new ImageIcon(\"images\/drag_me_green.png\"));\r\n else\r\n \/\/ colonnes \"normal\"\r\n vCol.add(\"(\" + String.valueOf(ligne) + \",\" + String.valueOf(colonne) + \")\");\r\n }\r\n vData.add(vCol);\r\n }\r\n return vData;\r\n }\r\n\r\n \/\/\r\n \/\/ Inner class - classe interne\r\n \/\/\r\n\r\n \/**\r\n * La classe qui permet de dire si on peux faire l'import et l'export et surtout comment ...\r\n *\/\r\n private class MyTransferHandler extends TransferHandler\r\n {\r\n public boolean canImport(TransferHandler.TransferSupport support)\r\n {\r\n return true;\r\n }\r\n\r\n public boolean importData(TransferHandler.TransferSupport info)\r\n {\r\n JTable.DropLocation dropLocation = (JTable.DropLocation) info.getDropLocation();\r\n if (dropLocation.isInsertRow())\r\n {\r\n MyDefTableModel m = (MyDefTableModel)jTable.getModel();\r\n\r\n if (info.getTransferable().isDataFlavorSupported(JTableRowTransferable.DATA_ROW))\r\n {\r\n try\r\n {\r\n Object[] myObject = (Object[]) info.getTransferable().getTransferData(JTableRowTransferable.DATA_ROW);\r\n int rowIdSrc = (Integer) myObject[0];\r\n Object[] data = (Object[]) ((Vector&lt;Object&gt;)myObject[1]).toArray();\r\n m.removeRow(rowIdSrc);\r\n\r\n int locRow = dropLocation.getRow();\r\n \/\/ TODO recheck lalgo en attendant ...\r\n locRow = (locRow &gt;= jTable.getRowCount())?(locRow - 1):locRow;\r\n m.insertRow(locRow, data);\r\n return true;\r\n }\r\n catch (Exception e)\r\n {\r\n e.printStackTrace();\r\n }\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n protected Transferable createTransferable(JComponent cp)\r\n {\r\n JTable tab = (JTable) cp;\r\n int row = tab.getSelectedRow();\r\n MyDefTableModel m = (MyDefTableModel) tab.getModel();\r\n int colCount = m.getColumnCount();\r\n Vector&lt;Object&gt; v = new Vector&lt;Object&gt;(colCount);\r\n for (int i = 0; i &lt; colCount; i++)\r\n v.add(m.getValueAt(row, i));\r\n\r\n return new JTableRowTransferable(v, row);\r\n }\r\n\r\n public int getSourceActions(JComponent cp)\r\n {\r\n return MOVE;\r\n }\r\n }\r\n}\r\n\r\n\/**\r\n * Mod\u00e8le du JTable utilis\u00e9, bas\u00e9 sur DefaultTable mais permettant en plsu d'utiliser les render\r\n *\/\r\nclass MyDefTableModel extends DefaultTableModel\r\n{\r\n public MyDefTableModel(Vector names, Vector data)\r\n {\r\n super(data, names);\r\n }\r\n\r\n \/**\r\n * Permet d'utilsier les \"render\" d\u00e9j\u00e0 impl\u00e9ment\u00e9 (type primitif, Boolean (=&gt; checkbox), ImageIcon)\r\n *\/\r\n public Class&lt;?&gt; getColumnClass(int columnIndex)\r\n {\r\n if (this.getRowCount() &gt; 0)\r\n {\r\n return super.getValueAt(0, columnIndex).getClass();\r\n }\r\n return super.getColumnClass(columnIndex);\r\n }\r\n}\r\n\r\n\/**\r\n * Classe qui \"transporte\" l'info durant le drag &amp; drop\r\n *\/\r\nclass JTableRowTransferable implements Transferable\r\n{\r\n public static final DataFlavor DATA_ROW = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + \";class=\" + Object.class.getName(), null);\r\n\r\n private Vector&lt;Object&gt; value;\r\n\r\n private int row;\r\n\r\n public JTableRowTransferable(Vector&lt;Object&gt; v, int rowId)\r\n {\r\n this.value = v;\r\n this.row = rowId;\r\n }\r\n\r\n public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException\r\n {\r\n if (flavor == null)\r\n {\r\n throw new IOException();\r\n }\r\n else if (flavor.equals(DATA_ROW))\r\n {\r\n Object[] o = {this.row, this.value};\r\n return o;\r\n }\r\n else if (flavor.equals(DataFlavor.stringFlavor))\r\n {\r\n return this.value.toString();\r\n }\r\n else\r\n {\r\n throw new UnsupportedFlavorException(flavor);\r\n }\r\n }\r\n\r\n public DataFlavor[] getTransferDataFlavors()\r\n {\r\n return new DataFlavor[] { DATA_ROW, DataFlavor.stringFlavor };\r\n }\r\n\r\n public boolean isDataFlavorSupported(DataFlavor flavor)\r\n {\r\n return (flavor.equals(DATA_ROW) || flavor.equals(DataFlavor.stringFlavor));\r\n }\r\n}<\/pre>\n<div class='footnotes' id='footnotes-141'>\n<div class='footnotedivider'><\/div>\n<ol>\n<li id='fn-141-1'> puisque mais il y a sinon cela ne serait pas marrant <span class='footnotereverse'><a href='#fnref-141-1'>&#8617;<\/a><\/span><\/li>\n<\/ol>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Drag &amp; Drop sur un JTable Dans nos application moderne il semble logique de pouvoir faire du DND \u00e0 gogo, pourtant ce n&rsquo;est pas des plus simple, avec certain langage de le faire,\u00a0 d&rsquo;o\u00f9 cette exemple qui m&rsquo;a fait un peu travaillez et arrachez un ou deux cheveux de ma longue chevelure de geek&#8230; Bref, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"webmentions_disabled_pings":false,"webmentions_disabled":false,"footnotes":""},"categories":[9],"tags":[38,72,67,24,42,28],"class_list":["post-141","post","type-post","status-publish","format-standard","hentry","category-dev","tag-decouverte","tag-java","tag-libre","tag-programmation","tag-script","tag-trucs-et-astuces"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/posts\/141","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/comments?post=141"}],"version-history":[{"count":34,"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/posts\/141\/revisions"}],"predecessor-version":[{"id":172,"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/posts\/141\/revisions\/172"}],"wp:attachment":[{"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/media?parent=141"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/categories?post=141"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/grummfy.be\/blog\/wp-json\/wp\/v2\/tags?post=141"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}