From 2cf27a5aee85d9a00a63731eebb9c4a46b03e6c8 Mon Sep 17 00:00:00 2001 From: Stephane Dallongeville <stephane.dallongeville@pasteur.fr> Date: Thu, 21 Jul 2022 16:24:14 +0200 Subject: [PATCH] Initial commit --- LICENSE.md | 595 ++++++++++++++++++ pom.xml | 83 +++ .../ylemontag/matlabimporter/MainPanel.java | 270 ++++++++ .../matlabimporter/MatlabImporterPlugin.java | 332 ++++++++++ .../matlabimporter/VariableListComponent.java | 129 ++++ .../resources/icon-Matlab-function-caller.png | Bin 0 -> 4052 bytes .../tooltip-Matlab-function-caller.png | Bin 0 -> 21881 bytes 7 files changed, 1409 insertions(+) create mode 100644 LICENSE.md create mode 100644 pom.xml create mode 100644 src/main/java/plugins/ylemontag/matlabimporter/MainPanel.java create mode 100644 src/main/java/plugins/ylemontag/matlabimporter/MatlabImporterPlugin.java create mode 100644 src/main/java/plugins/ylemontag/matlabimporter/VariableListComponent.java create mode 100644 src/main/resources/icon-Matlab-function-caller.png create mode 100644 src/main/resources/tooltip-Matlab-function-caller.png diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..bf09192 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,595 @@ +GNU General Public License +========================== + +_Version 3, 29 June 2007_ +_Copyright (C) 2007 Free Software Foundation, Inc. <<http://fsf.org/>>_ + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +## Preamble + +The GNU General Public License is a free, copyleft license for software and other +kinds of works. + +The licenses for most software and other practical works are designed to take away +your freedom to share and change the works. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change all versions of a +program--to make sure it remains free software for all its users. We, the Free +Software Foundation, use the GNU General Public License for most of our software; it +applies also to any other work released this way by its authors. You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General +Public Licenses are designed to make sure that you have the freedom to distribute +copies of free software (and charge for them if you wish), that you receive source +code or can get it if you want it, that you can change the software or use pieces of +it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or +asking you to surrender the rights. Therefore, you have certain responsibilities if +you distribute copies of the software, or if you modify it: responsibilities to +respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, +you must pass on to the recipients the same freedoms that you received. You must make +sure that they, too, receive or can get the source code. And you must show them these +terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: **(1)** assert +copyright on the software, and **(2)** offer you this License giving you legal permission +to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is +no warranty for this free software. For both users' and authors' sake, the GPL +requires that modified versions be marked as changed, so that their problems will not +be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of +the software inside them, although the manufacturer can do so. This is fundamentally +incompatible with the aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we have designed +this version of the GPL to prohibit the practice for those products. If such problems +arise substantially in other domains, we stand ready to extend this provision to +those domains in future versions of the GPL, as needed to protect the freedom of +users. + +Finally, every program is threatened constantly by software patents. States should +not allow patents to restrict development and use of software on general-purpose +computers, but in those that do, we wish to avoid the special danger that patents +applied to a free program could make it effectively proprietary. To prevent this, the +GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work in +a fashion requiring copyright permission, other than the making of an exact copy. The +resulting work is called a "modified version" of the earlier work or a +work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based on +the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for infringement under +applicable copyright law, except executing it on a computer or modifying a private +copy. Propagation includes copying, distribution (with or without modification), +making available to the public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through a computer +network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" to the +extent that it includes a convenient and prominently visible feature that **(1)** +displays an appropriate copyright notice, and **(2)** tells the user that there is no +warranty for the work (except to the extent that warranties are provided), that +licensees may convey the work under this License, and how to view a copy of this +License. If the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of a +work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of interfaces +specified for a particular programming language, one that is widely used among +developers working in that language. + +The "System Libraries" of an executable work include anything, other than +the work as a whole, that **(a)** is included in the normal form of packaging a Major +Component, but which is not part of that Major Component, and **(b)** serves only to +enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code form. +A "Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on which +the executable work runs, or a compiler used to produce the work, or an object code +interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all the +source code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. However, +it does not include the work's System Libraries, or general-purpose tools or +generally available free programs which are used unmodified in performing those +activities but which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for the work, and +the source code for shared libraries and dynamically linked subprograms that the work +is specifically designed to require, such as by intimate data communication or +control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +### 2. Basic Permissions + +All rights granted under this License are granted for the term of copyright on the +Program, and are irrevocable provided the stated conditions are met. This License +explicitly affirms your unlimited permission to run the unmodified Program. The +output from running a covered work is covered by this License only if the output, +given its content, constitutes a covered work. This License acknowledges your rights +of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without +conditions so long as your license otherwise remains in force. You may convey covered +works to others for the sole purpose of having them make modifications exclusively +for you, or provide you with facilities for running those works, provided that you +comply with the terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for you must do so +exclusively on your behalf, under your direction and control, on terms that prohibit +them from making any copies of your copyrighted material outside their relationship +with you. + +Conveying under any other circumstances is permitted solely under the conditions +stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law + +No covered work shall be deemed part of an effective technological measure under any +applicable law fulfilling obligations under article 11 of the WIPO copyright treaty +adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention +of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of +technological measures to the extent such circumvention is effected by exercising +rights under this License with respect to the covered work, and you disclaim any +intention to limit operation or modification of the work as a means of enforcing, +against the work's users, your or third parties' legal rights to forbid circumvention +of technological measures. + +### 4. Conveying Verbatim Copies + +You may convey verbatim copies of the Program's source code as you receive it, in any +medium, provided that you conspicuously and appropriately publish on each copy an +appropriate copyright notice; keep intact all notices stating that this License and +any non-permissive terms added in accord with section 7 apply to the code; keep +intact all notices of the absence of any warranty; and give all recipients a copy of +this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer +support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions + +You may convey a work based on the Program, or the modifications to produce it from +the Program, in the form of source code under the terms of section 4, provided that +you also meet all of these conditions: + +* **a)** The work must carry prominent notices stating that you modified it, and giving a +relevant date. +* **b)** The work must carry prominent notices stating that it is released under this +License and any conditions added under section 7. This requirement modifies the +requirement in section 4 to "keep intact all notices". +* **c)** You must license the entire work, as a whole, under this License to anyone who +comes into possession of a copy. This License will therefore apply, along with any +applicable section 7 additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no permission to license the +work in any other way, but it does not invalidate such permission if you have +separately received it. +* **d)** If the work has interactive user interfaces, each must display Appropriate Legal +Notices; however, if the Program has interactive interfaces that do not display +Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are +not by their nature extensions of the covered work, and which are not combined with +it such as to form a larger program, in or on a volume of a storage or distribution +medium, is called an "aggregate" if the compilation and its resulting +copyright are not used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work in an aggregate +does not cause this License to apply to the other parts of the aggregate. + +### 6. Conveying Non-Source Forms + +You may convey a covered work in object code form under the terms of sections 4 and +5, provided that you also convey the machine-readable Corresponding Source under the +terms of this License, in one of these ways: + +* **a)** Convey the object code in, or embodied in, a physical product (including a +physical distribution medium), accompanied by the Corresponding Source fixed on a +durable physical medium customarily used for software interchange. +* **b)** Convey the object code in, or embodied in, a physical product (including a +physical distribution medium), accompanied by a written offer, valid for at least +three years and valid for as long as you offer spare parts or customer support for +that product model, to give anyone who possesses the object code either **(1)** a copy of +the Corresponding Source for all the software in the product that is covered by this +License, on a durable physical medium customarily used for software interchange, for +a price no more than your reasonable cost of physically performing this conveying of +source, or **(2)** access to copy the Corresponding Source from a network server at no +charge. +* **c)** Convey individual copies of the object code with a copy of the written offer to +provide the Corresponding Source. This alternative is allowed only occasionally and +noncommercially, and only if you received the object code with such an offer, in +accord with subsection 6b. +* **d)** Convey the object code by offering access from a designated place (gratis or for +a charge), and offer equivalent access to the Corresponding Source in the same way +through the same place at no further charge. You need not require recipients to copy +the Corresponding Source along with the object code. If the place to copy the object +code is a network server, the Corresponding Source may be on a different server +(operated by you or a third party) that supports equivalent copying facilities, +provided you maintain clear directions next to the object code saying where to find +the Corresponding Source. Regardless of what server hosts the Corresponding Source, +you remain obligated to ensure that it is available for as long as needed to satisfy +these requirements. +* **e)** Convey the object code using peer-to-peer transmission, provided you inform +other peers where the object code and Corresponding Source of the work are being +offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the +Corresponding Source as a System Library, need not be included in conveying the +object code work. + +A "User Product" is either **(1)** a "consumer product", which +means any tangible personal property which is normally used for personal, family, or +household purposes, or **(2)** anything designed or sold for incorporation into a +dwelling. In determining whether a product is a consumer product, doubtful cases +shall be resolved in favor of coverage. For a particular product received by a +particular user, "normally used" refers to a typical or common use of +that class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, the +product. A product is a consumer product regardless of whether the product has +substantial commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified version of +its Corresponding Source. The information must suffice to ensure that the continued +functioning of the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for +use in, a User Product, and the conveying occurs as part of a transaction in which +the right of possession and use of the User Product is transferred to the recipient +in perpetuity or for a fixed term (regardless of how the transaction is +characterized), the Corresponding Source conveyed under this section must be +accompanied by the Installation Information. But this requirement does not apply if +neither you nor any third party retains the ability to install modified object code +on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to +continue to provide support service, warranty, or updates for a work that has been +modified or installed by the recipient, or for the User Product in which it has been +modified or installed. Access to a network may be denied when the modification itself +materially and adversely affects the operation of the network or violates the rules +and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with +this section must be in a format that is publicly documented (and with an +implementation available to the public in source code form), and must require no +special password or key for unpacking, reading or copying. + +### 7. Additional Terms + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. Additional +permissions that are applicable to the entire Program shall be treated as though they +were included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may be +used separately under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any +additional permissions from that copy, or from any part of it. (Additional +permissions may be written to require their own removal in certain cases when you +modify the work.) You may place additional permissions on material, added by you to a +covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: + +* **a)** Disclaiming warranty or limiting liability differently from the terms of +sections 15 and 16 of this License; or +* **b)** Requiring preservation of specified reasonable legal notices or author +attributions in that material or in the Appropriate Legal Notices displayed by works +containing it; or +* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that +modified versions of such material be marked in reasonable ways as different from the +original version; or +* **d)** Limiting the use for publicity purposes of names of licensors or authors of the +material; or +* **e)** Declining to grant rights under trademark law for use of some trade names, +trademarks, or service marks; or +* **f)** Requiring indemnification of licensors and authors of that material by anyone +who conveys the material (or modified versions of it) with contractual assumptions of +liability to the recipient, for any liability that these contractual assumptions +directly impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you received +it, or any part of it, contains a notice stating that it is governed by this License +along with a term that is a further restriction, you may remove that term. If a +license document contains a further restriction but permits relicensing or conveying +under this License, you may add to a covered work material governed by the terms of +that license document, provided that the further restriction does not survive such +relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in +the relevant source files, a statement of the additional terms that apply to those +files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a +separately written license, or stated as exceptions; the above requirements apply +either way. + +### 8. Termination + +You may not propagate or modify a covered work except as expressly provided under +this License. Any attempt otherwise to propagate or modify it is void, and will +automatically terminate your rights under this License (including any patent licenses +granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated **(a)** provisionally, unless and until the +copyright holder explicitly and finally terminates your license, and **(b)** permanently, +if the copyright holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently +if the copyright holder notifies you of the violation by some reasonable means, this +is the first time you have received notice of violation of this License (for any +work) from that copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of +parties who have received copies or rights from you under this License. If your +rights have been terminated and not permanently reinstated, you do not qualify to +receive new licenses for the same material under section 10. + +### 9. Acceptance Not Required for Having Copies + +You are not required to accept this License in order to receive or run a copy of the +Program. Ancillary propagation of a covered work occurring solely as a consequence of +using peer-to-peer transmission to receive a copy likewise does not require +acceptance. However, nothing other than this License grants you permission to +propagate or modify any covered work. These actions infringe copyright if you do not +accept this License. Therefore, by modifying or propagating a covered work, you +indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients + +Each time you convey a covered work, the recipient automatically receives a license +from the original licensors, to run, modify and propagate that work, subject to this +License. You are not responsible for enforcing compliance by third parties with this +License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an organization, or +merging organizations. If propagation of a covered work results from an entity +transaction, each party to that transaction who receives a copy of the work also +receives whatever licenses to the work the party's predecessor in interest had or +could give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if the predecessor +has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or +affirmed under this License. For example, you may not impose a license fee, royalty, +or other charge for exercise of rights granted under this License, and you may not +initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging +that any patent claim is infringed by making, using, selling, offering for sale, or +importing the Program or any portion of it. + +### 11. Patents + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The work thus +licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned or +controlled by the contributor, whether already acquired or hereafter acquired, that +would be infringed by some manner, permitted by this License, of making, using, or +selling its contributor version, but do not include claims that would be infringed +only as a consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant patent +sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license +under the contributor's essential patent claims, to make, use, sell, offer for sale, +import and otherwise run, modify and propagate the contents of its contributor +version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent (such as an +express permission to practice a patent or covenant not to sue for patent +infringement). To "grant" such a patent license to a party means to make +such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the +Corresponding Source of the work is not available for anyone to copy, free of charge +and under the terms of this License, through a publicly available network server or +other readily accessible means, then you must either **(1)** cause the Corresponding +Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the +patent license for this particular work, or **(3)** arrange, in a manner consistent with +the requirements of this License, to extend the patent license to downstream +recipients. "Knowingly relying" means you have actual knowledge that, but +for the patent license, your conveying the covered work in a country, or your +recipient's use of the covered work in a country, would infringe one or more +identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you +convey, or propagate by procuring conveyance of, a covered work, and grant a patent +license to some of the parties receiving the covered work authorizing them to use, +propagate, modify or convey a specific copy of the covered work, then the patent +license you grant is automatically extended to all recipients of the covered work and +works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on the +non-exercise of one or more of the rights that are specifically granted under this +License. You may not convey a covered work if you are a party to an arrangement with +a third party that is in the business of distributing software, under which you make +payment to the third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties who would receive +the covered work from you, a discriminatory patent license **(a)** in connection with +copies of the covered work conveyed by you (or copies made from those copies), or **(b)** +primarily for and in connection with specific products or compilations that contain +the covered work, unless you entered into that arrangement, or that patent license +was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied +license or other defenses to infringement that may otherwise be available to you +under applicable patent law. + +### 12. No Surrender of Others' Freedom + +If conditions are imposed on you (whether by court order, agreement or otherwise) +that contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot convey a covered work so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not convey it at all. For example, if you +agree to terms that obligate you to collect a royalty for further conveying from +those to whom you convey the Program, the only way you could satisfy both those terms +and this License would be to refrain entirely from conveying the Program. + +### 13. Use with the GNU Affero General Public License + +Notwithstanding any other provision of this License, you have permission to link or +combine any covered work with a work licensed under version 3 of the GNU Affero +General Public License into a single combined work, and to convey the resulting work. +The terms of this License will continue to apply to the part which is the covered +work, but the special requirements of the GNU Affero General Public License, section +13, concerning interaction through a network will apply to the combination as such. + +### 14. Revised Versions of this License + +The Free Software Foundation may publish revised and/or new versions of the GNU +General Public License from time to time. Such new versions will be similar in spirit +to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that +a certain numbered version of the GNU General Public License "or any later +version" applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by the +Free Software Foundation. If the Program does not specify a version number of the GNU +General Public License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU +General Public License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no +additional obligations are imposed on any author or copyright holder as a result of +your choosing to follow a later version. + +### 15. Disclaimer of Warranty + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE +QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +### 16. Limitation of Liability + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE +OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE +WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16 + +If the disclaimer of warranty and limitation of liability provided above cannot be +given local legal effect according to their terms, reviewing courts shall apply local +law that most closely approximates an absolute waiver of all civil liability in +connection with the Program, unless a warranty or assumption of liability accompanies +a copy of the Program in return for a fee. + +_END OF TERMS AND CONDITIONS_ + +## How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to +the public, the best way to achieve this is to make it free software which everyone +can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them +to the start of each source file to most effectively state the exclusion of warranty; +and each file should have at least the "copyright" line and a pointer to +where the full notice is found. + + Apache Commons Codec - Icy plugin (http://icy.bioimageanalysis.org/). + Copyright (C) 2021 Biological Image Analysis unit, Institut Pasteur + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this +when it starts in an interactive mode: + + Apache Commons Codec Copyright (C) 2021 Biological Image Analysis unit, Institut Pasteur + This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type 'show c' for details. + +The hypothetical commands `show w` and `show c` should show the appropriate parts of +the General Public License. Of course, your program's commands might be different; +for a GUI interface, you would use an "about box". + +You should also get your employer (if you work as a programmer) or school, if any, to +sign a "copyright disclaimer" for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see +<<http://www.gnu.org/licenses/>>. + +The GNU General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider it +more useful to permit linking proprietary applications with the library. If this is +what you want to do, use the GNU Lesser General Public License instead of this +License. But first, please read +<<http://www.gnu.org/philosophy/why-not-lgpl.html>>. \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..779acdf --- /dev/null +++ b/pom.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <!-- Inherited Icy Parent POM --> + <parent> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>parent-pom-plugin</artifactId> + <version>1.0.6</version> + </parent> + + <!-- Project Information --> + <artifactId>matlab-importer</artifactId> + <version>2.5.1</version> + + <packaging>jar</packaging> + + <name>Matlab Importer</name> + <description>Import Matlab *.mat files as sequences in Icy.</description> + <inceptionYear>2022</inceptionYear> + + <organization> + <name>Institut Pasteur</name> + <url>https://pasteur.fr</url> + </organization> + + <licenses> + <license> + <name>GNU GPLv3</name> + <url>https://www.gnu.org/licenses/gpl-3.0.en.html</url> + <distribution>repo</distribution> + </license> + </licenses> + + <developers> + <developer> + <id>ylemontag</id> + <name>Yoann Le Montagner</name> + <url>https://icy.bioimageanalysis.org/author/ylemontag/</url> + <roles> + <role>developer</role> + <role>maintainer</role> + </roles> + </developer> + </developers> + + <!-- Project properties --> + <properties> + + </properties> + + <!-- Project build configuration --> + <build> + <plugins> + </plugins> + </build> + + <!-- List of project's dependencies --> + <dependencies> + <!-- The core of Icy --> + <dependency> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>icy-kernel</artifactId> + </dependency> + + <dependency> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>matlab-io</artifactId> + <version>2.5.2</version> + </dependency> + </dependencies> + + <!-- Icy Maven repository (to find parent POM) --> + <repositories> + <repository> + <id>icy</id> + <name>Icy's Nexus</name> + <url>https://icy-nexus.pasteur.fr/repository/Icy/</url> + </repository> + </repositories> +</project> diff --git a/src/main/java/plugins/ylemontag/matlabimporter/MainPanel.java b/src/main/java/plugins/ylemontag/matlabimporter/MainPanel.java new file mode 100644 index 0000000..19bfe08 --- /dev/null +++ b/src/main/java/plugins/ylemontag/matlabimporter/MainPanel.java @@ -0,0 +1,270 @@ +package plugins.ylemontag.matlabimporter; + +import java.awt.BorderLayout; +import java.awt.Component; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.filechooser.FileNameExtensionFilter; + +import plugins.ylemontag.matlabio.gui.ComplexModeComponent; +import plugins.ylemontag.matlabio.gui.DimensionMappingComponent; +import plugins.ylemontag.matlabio.gui.FileChooserComponent; + +public class MainPanel extends JPanel { + + private static final long serialVersionUID = 1L; + private FileChooserComponent _fileChooserComponent; + private DimensionMappingComponent _dimensionMappingComponent; + private VariableListComponent _variableListComponent; + private JButton _actionButton; + private JCheckBox _doPrefixComponent; + private ComplexModeComponent _complexModeComponent; + private JButton _refreshButton; + + /** + * Matlab file chooser component + */ + public FileChooserComponent getFileComponent() { + return _fileChooserComponent; + } + + /** + * Dimension mapping component + */ + public DimensionMappingComponent getDimensionMappingComponent() { + return _dimensionMappingComponent; + } + + /** + * Complex mode component + */ + public ComplexModeComponent getComplexModeComponent() { + return _complexModeComponent; + } + + /** + * Variable list component + */ + public VariableListComponent getVariableComponent() { + return _variableListComponent; + } + + /** + * Prefix check-box + */ + public JCheckBox getDoPrefixComponent() { + return _doPrefixComponent; + } + + /** + * Refresh button + */ + public JButton getRefreshButton() { + return _refreshButton; + } + + /** + * Action button + */ + public JButton getActionButton() { + return _actionButton; + } + + /** + * Create the panel + */ + public MainPanel() { + setBorder(new EmptyBorder(5, 5, 5, 5)); + setLayout(new BorderLayout(0, 0)); + + JPanel panel_8 = new JPanel(); + add(panel_8, BorderLayout.SOUTH); + panel_8.setLayout(new BorderLayout(0, 0)); + + Component verticalStrut_1 = Box.createVerticalStrut(5); + panel_8.add(verticalStrut_1, BorderLayout.NORTH); + + JPanel panel_9 = new JPanel(); + panel_8.add(panel_9, BorderLayout.EAST); + panel_9.setLayout(new BoxLayout(panel_9, BoxLayout.X_AXIS)); + + Component horizontalStrut_1 = Box.createHorizontalStrut(5); + panel_9.add(horizontalStrut_1); + + _refreshButton = new JButton("Refresh"); + _refreshButton.setToolTipText("Refresh the list of variables"); + panel_9.add(_refreshButton); + + Component horizontalStrut_9 = Box.createHorizontalStrut(5); + panel_9.add(horizontalStrut_9); + + _actionButton = new JButton("Import"); + _actionButton.setToolTipText("Import the selected objects"); + panel_9.add(_actionButton); + + Component horizontalGlue_2 = Box.createHorizontalGlue(); + panel_8.add(horizontalGlue_2, BorderLayout.CENTER); + + _doPrefixComponent = new JCheckBox("Prefix with filename"); + _doPrefixComponent.setToolTipText("The name of the imported sequences will be prefixed with the name of the Matlab .mat file"); + panel_8.add(_doPrefixComponent, BorderLayout.WEST); + + _variableListComponent = new VariableListComponent(); + + JScrollPane scrollPane = new JScrollPane(_variableListComponent); + add(scrollPane, BorderLayout.CENTER); + + JPanel panel = new JPanel(); + add(panel, BorderLayout.NORTH); + panel.setLayout(new BorderLayout(0, 0)); + + JPanel panel_1 = new JPanel(); + panel.add(panel_1, BorderLayout.NORTH); + panel_1.setLayout(new BorderLayout(0, 0)); + + Component verticalStrut = Box.createVerticalStrut(5); + panel_1.add(verticalStrut, BorderLayout.SOUTH); + + JPanel panel_4 = new JPanel(); + panel_1.add(panel_4, BorderLayout.CENTER); + panel_4.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null)); + panel_4.setLayout(new BorderLayout(0, 0)); + + Component verticalStrut_5 = Box.createVerticalStrut(5); + panel_4.add(verticalStrut_5, BorderLayout.NORTH); + + Component verticalStrut_6 = Box.createVerticalStrut(5); + panel_4.add(verticalStrut_6, BorderLayout.SOUTH); + + Component horizontalStrut_4 = Box.createHorizontalStrut(5); + panel_4.add(horizontalStrut_4, BorderLayout.EAST); + + JPanel panel_5 = new JPanel(); + panel_4.add(panel_5, BorderLayout.CENTER); + panel_5.setLayout(new BoxLayout(panel_5, BoxLayout.Y_AXIS)); + + JPanel panel_7 = new JPanel(); + panel_5.add(panel_7); + panel_7.setLayout(new BoxLayout(panel_7, BoxLayout.X_AXIS)); + + JLabel lblNewLabel = new JLabel("Source file"); + panel_7.add(lblNewLabel); + + Component horizontalGlue = Box.createHorizontalGlue(); + panel_7.add(horizontalGlue); + + Component verticalStrut_2 = Box.createVerticalStrut(5); + panel_5.add(verticalStrut_2); + + JPanel panel_6 = new JPanel(); + panel_5.add(panel_6); + panel_6.setLayout(new BorderLayout(0, 0)); + + FileNameExtensionFilter filter = new FileNameExtensionFilter("Matlab object files (*.mat)", "mat"); + _fileChooserComponent = new FileChooserComponent(FileChooserComponent.Mode.OPEN_DIALOG, filter); + panel_6.add(_fileChooserComponent); + + Component horizontalStrut_5 = Box.createHorizontalStrut(5); + panel_4.add(horizontalStrut_5, BorderLayout.WEST); + + JPanel panel_2 = new JPanel(); + panel_1.add(panel_2, BorderLayout.EAST); + panel_2.setLayout(new BoxLayout(panel_2, BoxLayout.X_AXIS)); + + Component horizontalStrut = Box.createHorizontalStrut(5); + panel_2.add(horizontalStrut); + + JPanel panel_3 = new JPanel(); + panel_2.add(panel_3); + panel_3.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null)); + panel_3.setLayout(new BorderLayout(0, 0)); + + Component verticalStrut_3 = Box.createVerticalStrut(5); + panel_3.add(verticalStrut_3, BorderLayout.NORTH); + + Component verticalStrut_4 = Box.createVerticalStrut(5); + panel_3.add(verticalStrut_4, BorderLayout.SOUTH); + + Component horizontalStrut_2 = Box.createHorizontalStrut(5); + panel_3.add(horizontalStrut_2, BorderLayout.WEST); + + Component horizontalStrut_3 = Box.createHorizontalStrut(5); + panel_3.add(horizontalStrut_3, BorderLayout.EAST); + + JPanel panel_10 = new JPanel(); + panel_3.add(panel_10, BorderLayout.CENTER); + panel_10.setLayout(new BoxLayout(panel_10, BoxLayout.Y_AXIS)); + + JPanel panel_12 = new JPanel(); + panel_10.add(panel_12); + panel_12.setLayout(new BoxLayout(panel_12, BoxLayout.X_AXIS)); + + JLabel lblNewLabel_1 = new JLabel("Dimension mapping"); + panel_12.add(lblNewLabel_1); + + Component horizontalGlue_1 = Box.createHorizontalGlue(); + panel_12.add(horizontalGlue_1); + + Component verticalStrut_7 = Box.createVerticalStrut(5); + panel_10.add(verticalStrut_7); + + JPanel panel_11 = new JPanel(); + panel_10.add(panel_11); + panel_11.setLayout(new BorderLayout(0, 0)); + + _dimensionMappingComponent = new DimensionMappingComponent(); + _dimensionMappingComponent.setToolTipText("Describe how a multi-dimension Matlab array should be interpreted as a sequence"); + panel_11.add(_dimensionMappingComponent, BorderLayout.CENTER); + + Component horizontalStrut_6 = Box.createHorizontalStrut(5); + panel_2.add(horizontalStrut_6); + + JPanel panel_13 = new JPanel(); + panel_13.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null)); + panel_2.add(panel_13); + panel_13.setLayout(new BorderLayout(0, 0)); + + Component horizontalStrut_7 = Box.createHorizontalStrut(5); + panel_13.add(horizontalStrut_7, BorderLayout.WEST); + + Component horizontalStrut_8 = Box.createHorizontalStrut(5); + panel_13.add(horizontalStrut_8, BorderLayout.EAST); + + Component verticalStrut_8 = Box.createVerticalStrut(5); + panel_13.add(verticalStrut_8, BorderLayout.NORTH); + + Component verticalStrut_9 = Box.createVerticalStrut(5); + panel_13.add(verticalStrut_9, BorderLayout.SOUTH); + + JPanel panel_14 = new JPanel(); + panel_13.add(panel_14, BorderLayout.CENTER); + panel_14.setLayout(new BoxLayout(panel_14, BoxLayout.Y_AXIS)); + + JPanel panel_15 = new JPanel(); + panel_14.add(panel_15); + panel_15.setLayout(new BoxLayout(panel_15, BoxLayout.X_AXIS)); + + JLabel lblNewLabel_2 = new JLabel("Complex variables"); + panel_15.add(lblNewLabel_2); + + Component horizontalGlue_3 = Box.createHorizontalGlue(); + panel_15.add(horizontalGlue_3); + + Component verticalStrut_10 = Box.createVerticalStrut(5); + panel_14.add(verticalStrut_10); + + _complexModeComponent = new ComplexModeComponent(); + _complexModeComponent.setToolTipText("Describe how complex-valued Matlab arrays should be imported: only the real part, only the imaginary part, or both. In the latter, the real data will be saved in even channels, and imaginary data in odd channels."); + panel_14.add(_complexModeComponent); + + } + +} diff --git a/src/main/java/plugins/ylemontag/matlabimporter/MatlabImporterPlugin.java b/src/main/java/plugins/ylemontag/matlabimporter/MatlabImporterPlugin.java new file mode 100644 index 0000000..0aa4500 --- /dev/null +++ b/src/main/java/plugins/ylemontag/matlabimporter/MatlabImporterPlugin.java @@ -0,0 +1,332 @@ +package plugins.ylemontag.matlabimporter; + +import icy.gui.dialog.MessageDialog; +import icy.gui.frame.IcyFrame; +import icy.gui.frame.progress.ProgressFrame; +import icy.plugin.abstract_.PluginActionable; +import icy.preferences.XMLPreferences; +import icy.sequence.Sequence; +import icy.system.thread.ThreadUtil; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import javax.swing.JButton; +import javax.swing.JCheckBox; + +import plugins.ylemontag.matlabio.ComplexMode; +import plugins.ylemontag.matlabio.DimensionMapping; +import plugins.ylemontag.matlabio.MatlabImporter; +import plugins.ylemontag.matlabio.gui.ComplexModeComponent; +import plugins.ylemontag.matlabio.gui.DimensionMappingComponent; +import plugins.ylemontag.matlabio.gui.DimensionMappingComponent.DimensionMappingChangedListener; +import plugins.ylemontag.matlabio.gui.FileChooserComponent; +import plugins.ylemontag.matlabio.gui.FileChooserComponent.FileChangedListener; +import plugins.ylemontag.matlabio.gui.MatlabProgressFrame; +import plugins.ylemontag.matlabio.lib.Controller; +import plugins.ylemontag.matlabio.lib.MLMeta; + +/** + * + * @author Yoann Le Montagner + * + * Plugin to import .mat file as sequences + */ +public class MatlabImporterPlugin extends PluginActionable +{ + private FileChooserComponent _fileChooserComponent; + private DimensionMappingComponent _dimensionMappingComponent; + private ComplexModeComponent _complexModeComponent; + private JCheckBox _doPrefixComponent; + private VariableListComponent _variableListComponent; + private JButton _refreshButton; + private JButton _actionButton; + private File _file; + private MatlabImporter _importer; + + @Override + public void run() + { + // GUI + IcyFrame frame = new IcyFrame("Matlab importer", true, true, true, true); + frame.setSize (500, 400); + frame.setSizeExternal(500, 400); + MainPanel mainPanel = new MainPanel(); + frame.add(mainPanel); + addIcyFrame(frame); + frame.center(); + frame.setVisible(true); + frame.requestFocus(); + _fileChooserComponent = mainPanel.getFileComponent(); + _dimensionMappingComponent = mainPanel.getDimensionMappingComponent(); + _complexModeComponent = mainPanel.getComplexModeComponent(); + _doPrefixComponent = mainPanel.getDoPrefixComponent(); + _variableListComponent = mainPanel.getVariableComponent(); + _refreshButton = mainPanel.getRefreshButton(); + _actionButton = mainPanel.getActionButton(); + retrievePersistentPrefix(); + retrievePersistentDimensionMapping(); + retrievePersistentComplexMode(); + retrievePersistentDirectory(); + + // Listeners + _fileChooserComponent.addFileChangedListener(new FileChangedListener() + { + @Override + public void actionPerformed(File newFile) { + refreshVariableList(true, null); + updatePersistentDirectory(); + } + }); + _dimensionMappingComponent.addDimensionMappingChangedListener(new DimensionMappingChangedListener() + { + @Override + public void actionPerformed() { + updatePersistentDimensionMapping(); + } + }); + _complexModeComponent.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) { + updatePersistentComplexMode(); + } + }); + _doPrefixComponent.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) { + updatePersistentPrefix(); + } + }); + _refreshButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) { + refreshVariableList(false, _variableListComponent.getSelectedVariables()); + } + }); + _actionButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) { + executeImport(); + } + }); + _variableListComponent.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) { + if(e.getButton()==1 && e.getClickCount()==2) { + executeImport(); + } + } + }); + } + + /** + * Refresh the component that display the list of importable variables in + * the currently selected .mat file + */ + private void refreshVariableList(boolean refreshFile, List<String> selectedVariables) + { + // Refresh the file from the dedicated component + if(refreshFile) { + _file = _fileChooserComponent.getSelectedFile(); + } + + // If no file is selected + _variableListComponent.clearRows(); + _importer = null; + if(_file==null) { + return; + } + + // Try to rebuild the importer + try { + _importer = new MatlabImporter(_file); + } + catch(IOException err) { + String message = "Cannot read the Matlab file " + _file.getPath(); + MessageDialog.showDialog("Error", message, MessageDialog.ERROR_MESSAGE); + return; + } + + // Refresh the list + Set<String> names = _importer.getImportableSequences(); + for(String name : names) { + MLMeta meta = _importer.getMeta(name); + _variableListComponent.addRow(meta); + } + + // Enforce the selection + if(selectedVariables!=null) { + _variableListComponent.setSelectedVariables(selectedVariables); + } + } + + /** + * Retrieve the current prefix + */ + private String getCurrentPrefix() + { + String retVal = ""; + if(_doPrefixComponent.isSelected() && _file!=null) { + retVal = _file.getName(); + int dotPosition = retVal.lastIndexOf('.'); + if(dotPosition>=0) { + retVal = retVal.substring(0, dotPosition); + } + retVal += "/"; + } + return retVal; + } + + /** + * Do the importing job + */ + private void executeImport() + { + final List<String> names = _variableListComponent.getSelectedVariables(); + _variableListComponent.clearSelection(); + for(final String name : names) { + final DimensionMapping mapping = _dimensionMappingComponent.getDimensionMapping(); + final ComplexMode complexMode = _complexModeComponent.getComplexMode(); + final String prefix = getCurrentPrefix(); + final Controller controller = new Controller(); + final ProgressFrame progressFrame = new MatlabProgressFrame("Importing sequence " + name, controller); + ThreadUtil.bgRun(new Runnable() + { + @Override + public void run() { + try { + Sequence seq = _importer.getSequence(name, mapping, complexMode, prefix+name, controller); + addSequence(seq); + } + catch(final IOException err) { + ThreadUtil.invokeLater(new Runnable() + { + @Override + public void run() { + System.err.println(err); + String message = "Cannot import the variable '" + name + "'"; + MessageDialog.showDialog("Error", message, MessageDialog.ERROR_MESSAGE); + } + }); + } + catch(Controller.CanceledByUser err) {} + finally { + ThreadUtil.invokeLater(new Runnable() + { + @Override + public void run() { + progressFrame.close(); + } + }); + } + } + }); + } + } + + /** + * Retrieve the persistent dimension mapping parameters + */ + private void retrievePersistentDimensionMapping() + { + XMLPreferences prefs = getPreferences("DimensionMapping"); + DimensionMapping mapping = _dimensionMappingComponent.getDimensionMapping(); + int dimX = mapping.getDimensionX(); + int dimY = mapping.getDimensionY(); + int dimZ = mapping.getDimensionZ(); + int dimT = mapping.getDimensionT(); + int dimC = mapping.getDimensionC(); + dimX = prefs.getInt("DimX", dimX); + dimY = prefs.getInt("DimY", dimY); + dimZ = prefs.getInt("DimZ", dimZ); + dimT = prefs.getInt("DimT", dimT); + dimC = prefs.getInt("DimC", dimC); + _dimensionMappingComponent.setDimensions(dimX, dimY, dimZ, dimT, dimC); + } + + /** + * Update the persistent dimension mapping parameters + */ + private void updatePersistentDimensionMapping() + { + XMLPreferences prefs = getPreferences("DimensionMapping"); + DimensionMapping mapping = _dimensionMappingComponent.getDimensionMapping(); + prefs.putInt("DimX", mapping.getDimensionX()); + prefs.putInt("DimY", mapping.getDimensionY()); + prefs.putInt("DimZ", mapping.getDimensionZ()); + prefs.putInt("DimT", mapping.getDimensionT()); + prefs.putInt("DimC", mapping.getDimensionC()); + } + + /** + * Retrieve the persistent directory + */ + private void retrievePersistentDirectory() + { + XMLPreferences prefs = getPreferences("Directory"); + String directoryPath = _fileChooserComponent.getCurrentDirectory().getAbsolutePath(); + directoryPath = prefs.get("Path", directoryPath); + _fileChooserComponent.setCurrentDirectory(new File(directoryPath)); + } + + /** + * Update the persistent directory + */ + private void updatePersistentDirectory() + { + XMLPreferences prefs = getPreferences("Directory"); + prefs.put("Path", _fileChooserComponent.getCurrentDirectory().getAbsolutePath()); + } + + /** + * Retrieve the persistent complex mode parameter + */ + private void retrievePersistentComplexMode() + { + XMLPreferences prefs = getPreferences("ComplexMode"); + ComplexMode complexMode = _complexModeComponent.getComplexMode(); + complexMode = ComplexMode.fromDescription(prefs.get("Name", complexMode.getDescription())); + if(complexMode!=null) { + _complexModeComponent.setComplexMode(complexMode); + } + } + + /** + * Update the persistent complex mode + */ + private void updatePersistentComplexMode() + { + XMLPreferences prefs = getPreferences("ComplexMode"); + prefs.put("Name", _complexModeComponent.getComplexMode().getDescription()); + } + + /** + * Retrieve the persistent prefix parameter + */ + private void retrievePersistentPrefix() + { + XMLPreferences prefs = getPreferences("Prefix"); + boolean doPrefix = prefs.getBoolean("UseFilename", _doPrefixComponent.isSelected()); + _doPrefixComponent.setSelected(doPrefix); + } + + /** + * Update the persistent prefix parameter + */ + private void updatePersistentPrefix() + { + XMLPreferences prefs = getPreferences("Prefix"); + prefs.putBoolean("UseFilename", _doPrefixComponent.isSelected()); + } +} diff --git a/src/main/java/plugins/ylemontag/matlabimporter/VariableListComponent.java b/src/main/java/plugins/ylemontag/matlabimporter/VariableListComponent.java new file mode 100644 index 0000000..e5aedea --- /dev/null +++ b/src/main/java/plugins/ylemontag/matlabimporter/VariableListComponent.java @@ -0,0 +1,129 @@ +package plugins.ylemontag.matlabimporter; + +import java.util.LinkedList; +import java.util.List; + +import javax.swing.JTable; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; + +import plugins.ylemontag.matlabio.lib.MLMeta; + +/** + * + * @author Yoann Le Montagner + * + * Component used to display a list of Matlab objects contained in a .mat file + */ +public class VariableListComponent extends JTable +{ + private static final long serialVersionUID = 1L; + + private DefaultTableModel _model; + + /** + * Constructor + */ + public VariableListComponent() + { + super(); + _model = new Model(); + setModel(_model); + TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(_model); + setRowSorter(sorter); + } + + /** + * Return the variable name corresponding to a given row. The row index is + * relative to the JTable (not to the model) + */ + public String getVariableAt(int rowIndex) + { + return (String)getValueAt(rowIndex, 0); + } + + /** + * Return the list of selected variables + */ + public List<String> getSelectedVariables() + { + int[] selectedRows = getSelectedRows(); + List<String> retVal = new LinkedList<String>(); + for(int selectedRow : selectedRows) { + retVal.add(getVariableAt(selectedRow)); + } + return retVal; + } + + /** + * Set the list of selected variables + */ + public void setSelectedVariables(List<String> selectedVariables) + { + clearSelection(); + for(int r=0; r<getRowCount(); ++r) { + if(selectedVariables.contains(getVariableAt(r))) { + getSelectionModel().addSelectionInterval(r, r); + } + } + } + + /** + * Add a row the table + */ + public void addRow(MLMeta meta) + { + String name = meta.getName(); + String dims = meta.getDimensionsAsString(); + Boolean cplx = meta.getIsComplex(); + String type = meta.getType().toString(); + _model.addRow(new Object[] {name, type, cplx, dims}); + } + + /** + * Remove all the rows + */ + public void clearRows() + { + _model.setRowCount(0); + } + + /** + * Model used in the component + */ + private static class Model extends DefaultTableModel + { + private static final long serialVersionUID = 1L; + + /** + * Constructor + */ + public Model() + { + super(); + addColumn("Variable" ); + addColumn("Type" ); + addColumn("Complex" ); + addColumn("Dimensions"); + } + + @Override + public boolean isCellEditable(int row, int column) + { + return false; + } + + @Override + public int getColumnCount() + { + return 4; + } + + @Override + public Class<?> getColumnClass(int columnIndex) + { + return columnIndex==2 ? Boolean.class : String.class; + } + } +} diff --git a/src/main/resources/icon-Matlab-function-caller.png b/src/main/resources/icon-Matlab-function-caller.png new file mode 100644 index 0000000000000000000000000000000000000000..d66688871bd6a31c7e2aaaf1fe366264097df00d GIT binary patch literal 4052 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 GLSTY({mohc literal 0 HcmV?d00001 diff --git a/src/main/resources/tooltip-Matlab-function-caller.png b/src/main/resources/tooltip-Matlab-function-caller.png new file mode 100644 index 0000000000000000000000000000000000000000..bfcfb9de757eeb8e787ae894aeb0b567fda8ac93 GIT binary patch literal 21881 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 literal 0 HcmV?d00001 -- GitLab