diff --git a/README.md b/README.md index aef75e0..653686b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,45 @@ # miscJar -Ensemble de fonctions pratiques pour programme java \ No newline at end of file +Misc est un atelier pour le développement d'application Java pouvant servir à l'illustration pédagogique. + + + +Objets de pratiques + + * Log : fonction de trace + * Config : gestion de valeurs initiales + * Bundle : internationalisation + * UpdateSender : alerte sur modification de valeur + * ColorCheckedLine : gestion des couleurs d'un terminal pour testes unitaires en mode texte + +Fonctions récurrentes + + * Util : ensemble de fonction (souvent graphique) pour factoriser du code + +Éléments graphiques + + * SpinnerSlider : couplage d'un curseur (slider) et d'un champ numérique (spinner) + * DatePanel : saisie de date + * HourPanel : saisie d'heure + * ImagePreview : accessoire de visualisation d'image pour dialogue de choix de fichier. + * TitledDialog : fenêtre avec gestion d'icône de l'application + * HtmlDialog : affichage d'une page HTML dans une application + * Guide : outil d’auto-documentation pour surlignant l'action à réaliser dans un scénario d'utilisation + +Modèle MVC + + * ApplicationManager : associe la gestion de bouton dans des menus déroulant ou des boîte d'icône + * HelpManager : menu d'aide standard avec gestion de langue + * ToolBarManager : boîte d'icône détachable + * Controler : contrôleur du modèle MVC + * ProgressState : gestion de modification du modèle pour connexion d'une barre de progression + +Contournement + + * XML : contournement d'un bug d'une version de Java + +Réseau + + * CommandLineServer : fonctions générique d'un serveur socket en mode texte + * CommandLineWaiter : classe générique à spécialiser en fonction du serveur souhaité + diff --git a/ant/build.xml b/ant/build.xml new file mode 100644 index 0000000..74fc2f8 --- /dev/null +++ b/ant/build.xml @@ -0,0 +1,459 @@ + + + + + + + + + + Java Practice by Dr. F. Merciol (c) 2008 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Misc source files]]> + Copyright © 2008 Dr. F. Merciol. Tous droits réservés.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/config/BundleManager.xml b/data/config/BundleManager.xml new file mode 100644 index 0000000..c223597 --- /dev/null +++ b/data/config/BundleManager.xml @@ -0,0 +1,38 @@ + + + +This file is automaticaly generated by BundleManager application at 6:58 PM on Aug 17, 2018. +0 +2 +North +/mnt/f/home/felix/Travail/IUT/M3101/data/config +false +false + +[x=50,y=50] +data/images/bundle/bundle.png +false +false +1 +true +[x=50,y=50] +false +/mnt/sata/stock/nomade/IRISA-UBS/ImageByTrees/src/java/imageByTrees/app +true +[x=50,y=50] +North +[x=50,y=50] +North + +[x=50,y=50] +data/log +true +[x=50,y=50] +[x=50,y=50] + +false +South +true +true +false + diff --git a/data/config/BundleManager_en_US.properties b/data/config/BundleManager_en_US.properties new file mode 100644 index 0000000..6425997 --- /dev/null +++ b/data/config/BundleManager_en_US.properties @@ -0,0 +1,44 @@ +#Produit automatiquement par BundleManager +#Thu Jun 30 17:35:12 CEST 2011 +ActionAddKey=add Key +ActionAddLocale=add Locale +ActionChangeLocale=Change Locale +ActionEmpty=New +ActionFile=File +ActionInit=Select sources \u2026 +ActionMerge=Merge +ActionNext=Next String +ActionOpen=Open \u2026 +ActionQuit=Quit +ActionRemoveKey=remove Key +ActionRemoveLocale=remove Locale +ActionRenameLocale=rename Locale +ActionSave=Save +ActionSaveAs=Save As \u2026 +AddKeyTitle=Add new key +AddLocaleTitle=New locale name\: +BundleEditorTitle=\ Bundle Editor ({0}) +BundleNotSavedTitle=Bundle not saved +BundleTitle=\ Bundle Editor +ExceptionCantLoad=Bundle {0} can''t be loaded\! +ExceptionCantSave=Bundle {0} can''t be saved\! +ExceptionDuplicatedKey=Duplicated key {0}\! +ExceptionDuplicatedLocale=Duplicated locale {0}\! +HeaderBundleFile=This file is generated automatically by BundleManager +LabelBundelPropertieFile=bundle properties file +LabelFileName=File name +LabelKeys=keys +LabelLine=Line +LabelLineNumber=Line Number +LabelNewKey=New key name\: +LabelRealyDiscardBundle=Do you want to discard current bundle ? +LabelSaveBundle=Do you want to save bundle ? +MessageAddLocale=\ Add Locale +MessageNoKey=You must have to select one key line\! +MessageNoLocale=You must have to select one locale column\! +MessageNoSourceDir=You must choose a directory source before (use Select Sources ...)\! +MessageRenameLocale=\ Rename Locale +NoKeyTitle=Can''t determine key +NoLocaleTitle=Can''t determine locale +NoSourceTitle=No source +RenameLocaleTitle=Rename locale diff --git a/data/config/BundleManager_fr_FR.properties b/data/config/BundleManager_fr_FR.properties new file mode 100644 index 0000000..1f55e26 --- /dev/null +++ b/data/config/BundleManager_fr_FR.properties @@ -0,0 +1,44 @@ +#Produit automatiquement par BundleManager +#Thu Jun 30 17:35:12 CEST 2011 +ActionAddKey=Ajouter R\u00E9f\u00E9rence +ActionAddLocale=Ajouter Langue +ActionChangeLocale=Changer de Langue +ActionEmpty=Nouveau +ActionFile=Fichiers +ActionInit=Choisir Sources \u2026 +ActionMerge=Fusione +ActionNext=Cha\u00EEne Suivante +ActionOpen=Ouvrir \u2026 +ActionQuit=Quitter +ActionRemoveKey=Supprimer R\u00E9f\u00E9rence +ActionRemoveLocale=Supprimer Langue +ActionRenameLocale=Renomer Langue +ActionSave=Sauver +ActionSaveAs=Sauver sous \u2026 +AddKeyTitle=AJouter une nouvelle r\u00E9f\u00E9rence +AddLocaleTitle=Nouvelle langue \: +BundleEditorTitle=\ Editeur de Baluchon (\= propri\u00E9t\u00E9s du programme) ({0}) +BundleNotSavedTitle=Baluchon non sauv\u00E9 +BundleTitle=\ Editeur de baluchon +ExceptionCantLoad=Impossible de charger {0} \! +ExceptionCantSave=Impossible de sauver {0} \! +ExceptionDuplicatedKey=R\u00E9f\u00E9rence {0} en double \! +ExceptionDuplicatedLocale=Localisation {0} en double \! +HeaderBundleFile=Produit automatiquement par BundleManager +LabelBundelPropertieFile=Baluchon de propri\u00E9t\u00E9s (Langue) +LabelFileName=Nom du fichier +LabelKeys=R\u00E9f\u00E9rences +LabelLine=Ligne +LabelLineNumber=Num\u00E9ro de Ligne +LabelNewKey=Nouvelle r\u00E9f\u00E9rences \: +LabelRealyDiscardBundle=Voulez-vous vraiment abandonner les modifications ? +LabelSaveBundle=Vouslez-vous sauver le baluchon ? +MessageAddLocale=\ Ajout d''une langue +MessageNoKey=Vous devez selectionner une ligne de key \! +MessageNoLocale=Vous devez selectionner une colonne de langue \! +MessageNoSourceDir=Vous devez d''abord choisir un r\u00E9pertoire de source en utilisant "Choisir Sources ..." \! +MessageRenameLocale=\ Changement de langue +NoKeyTitle=Impossible de trouver la r\u00E9f\u00E9rence +NoLocaleTitle=Impossible de trouver la langue +NoSourceTitle=Pas de source +RenameLocaleTitle=Renommer une langue diff --git a/data/config/Chat.xml b/data/config/Chat.xml new file mode 100644 index 0000000..095ad39 --- /dev/null +++ b/data/config/Chat.xml @@ -0,0 +1,60 @@ + + + +This file is automaticaly generated by Chat application at 8:59 AM on Aug 17, 2018. +1 +projets.iut-info-vannes.net +127.0.0.10 +3 +10 +data/images/chat/chat.png +false +/ +squidva.univ-ubs.fr +true +felix +10 +[x=50,y=50] +80 +false +[x=50,y=50] +1 + + +data/log +/home/felix +[x=50,y=50] +2 +10 + +10 +1 +64 +false +8080 +8080 +2 +localhost +2 +10 + + +data/log + +10 +127.0.0.1 +3 +false +false +[x=50,y=50] +isa +2 +inconnu_2212 +SetDodwan +18 +9090 +10 +10 +3128 +10 + diff --git a/data/config/Chat_en_AU.properties b/data/config/Chat_en_AU.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_AU.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_en_CA.properties b/data/config/Chat_en_CA.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_CA.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_en_GB.properties b/data/config/Chat_en_GB.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_GB.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_en_IE.properties b/data/config/Chat_en_IE.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_IE.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_en_IN.properties b/data/config/Chat_en_IN.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_IN.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_en_MT.properties b/data/config/Chat_en_MT.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_MT.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_en_NZ.properties b/data/config/Chat_en_NZ.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_NZ.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_en_PH.properties b/data/config/Chat_en_PH.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_PH.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_en_SG.properties b/data/config/Chat_en_SG.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_SG.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_en_US.properties b/data/config/Chat_en_US.properties new file mode 100755 index 0000000..baa6df9 --- /dev/null +++ b/data/config/Chat_en_US.properties @@ -0,0 +1,12 @@ +#Produit automatiquement par BundleManager +#Thu Jun 30 17:41:47 CEST 2011 +ActionClear=Clear +ActionManageExtention=Managed extention file +ActionPseudo=Pseudonyme +ChangePseudoTitle=\ Change login +ChatNotSavedTitle=\ Chat not saved +ChatTitle=\ Chat ({0}) +LabelChatFilter=Chat text (.chat) +LabelNewPseudo=New login +MessageRealyClearChat=Do you realy want to clear Chat? +MessageSaveChat=Do you want to save chat? diff --git a/data/config/Chat_en_ZA.properties b/data/config/Chat_en_ZA.properties new file mode 120000 index 0000000..bd679fa --- /dev/null +++ b/data/config/Chat_en_ZA.properties @@ -0,0 +1 @@ +Chat_en_US.properties \ No newline at end of file diff --git a/data/config/Chat_fr_BE.properties b/data/config/Chat_fr_BE.properties new file mode 120000 index 0000000..f2d6181 --- /dev/null +++ b/data/config/Chat_fr_BE.properties @@ -0,0 +1 @@ +Chat_fr_FR.properties \ No newline at end of file diff --git a/data/config/Chat_fr_CA.properties b/data/config/Chat_fr_CA.properties new file mode 120000 index 0000000..f2d6181 --- /dev/null +++ b/data/config/Chat_fr_CA.properties @@ -0,0 +1 @@ +Chat_fr_FR.properties \ No newline at end of file diff --git a/data/config/Chat_fr_CH.properties b/data/config/Chat_fr_CH.properties new file mode 120000 index 0000000..f2d6181 --- /dev/null +++ b/data/config/Chat_fr_CH.properties @@ -0,0 +1 @@ +Chat_fr_FR.properties \ No newline at end of file diff --git a/data/config/Chat_fr_FR.properties b/data/config/Chat_fr_FR.properties new file mode 100755 index 0000000..8a1c5c6 --- /dev/null +++ b/data/config/Chat_fr_FR.properties @@ -0,0 +1,12 @@ +#Produit automatiquement par BundleManager +#Thu Jun 30 17:41:47 CEST 2011 +ActionClear=Effacer +ActionManageExtention=G\u00E9rer l''extension de fichier +ActionPseudo=Pseudonyme +ChangePseudoTitle=\ Changer de pseudo +ChatNotSavedTitle=\ Clavardage non sauv\u00E9 +ChatTitle=\ Clavardage ({0}) +LabelChatFilter=Clavardage (.chat) +LabelNewPseudo=Nouveau pseudo +MessageRealyClearChat=Voulez-vous vraiment oublier ce clavardage ? +MessageSaveChat=Vouvez-vous sauvegarder ce clavardage ? diff --git a/data/config/Chat_fr_LU.properties b/data/config/Chat_fr_LU.properties new file mode 120000 index 0000000..f2d6181 --- /dev/null +++ b/data/config/Chat_fr_LU.properties @@ -0,0 +1 @@ +Chat_fr_FR.properties \ No newline at end of file diff --git a/data/config/Controller_br_FR_breton.properties b/data/config/Controller_br_FR_breton.properties new file mode 100644 index 0000000..961e5c9 --- /dev/null +++ b/data/config/Controller_br_FR_breton.properties @@ -0,0 +1,10 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:34:22 CEST 2015 +ActionSave=Enrolla\u00F1\u2026 +ActionEmpty=Nevez +FileTitle=\ Fichenn +ActionLoadMessage=Karga\u00F1 ar gemenn\u2026 +ActionSaveAll=Enrolla\u00F1 holl\u2026 +ActionQuit=Kuitaat +ActionSaveAs=Enrolla\u00F1 dindan\u2026 +ActionOpen=Digeri\u00F1\u2026 diff --git a/data/config/Controller_br_FR_gallo.properties b/data/config/Controller_br_FR_gallo.properties new file mode 100644 index 0000000..085333e --- /dev/null +++ b/data/config/Controller_br_FR_gallo.properties @@ -0,0 +1,10 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:34:22 CEST 2015 +ActionSave=Garder\u2026 +ActionEmpty=Nouviao +FileTitle=\ Fichier +ActionLoadMessage=Charjer le messaije\u2026 +ActionQuit=Qhiter +ActionSaveAll=Garder tout +ActionSaveAs=Garder sou\u2026 +ActionOpen=Ouvri\u2026 diff --git a/data/config/Controller_en_US.properties b/data/config/Controller_en_US.properties new file mode 100644 index 0000000..2153a07 --- /dev/null +++ b/data/config/Controller_en_US.properties @@ -0,0 +1,10 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:34:22 CEST 2015 +ActionSave=Save\u2026 +ActionEmpty=Empty +ActionLoadMessage=Load message\u2026 +FileTitle=\ File +ActionQuit=Exit +ActionSaveAll=Save all +ActionSaveAs=Save as\u2026 +ActionOpen=Open\u2026 diff --git a/data/config/Controller_es_ES.properties b/data/config/Controller_es_ES.properties new file mode 100644 index 0000000..8ad5ac2 --- /dev/null +++ b/data/config/Controller_es_ES.properties @@ -0,0 +1,10 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:34:22 CEST 2015 +ActionEmpty=Nuevo +ActionLoadMessage=Carga el mensaje\u2026 +ActionOpen=Abrir\u2026 +ActionQuit=Dejar +ActionSave=Salveguardar\u2026 +ActionSaveAll=Salveguardar todo +ActionSaveAs=Salveguardar como\u2026 +FileTitle=\ Archivo diff --git a/data/config/Controller_fr_FR.properties b/data/config/Controller_fr_FR.properties new file mode 100644 index 0000000..6868a6e --- /dev/null +++ b/data/config/Controller_fr_FR.properties @@ -0,0 +1,10 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:34:22 CEST 2015 +ActionSave=Sauver\u2026 +ActionEmpty=Nouveau +ActionLoadMessage=Charge le message\u2026 +FileTitle=\ Fichier +ActionQuit=Quitter +ActionSaveAll=Sauver tout +ActionSaveAs=Sauver sous\u2026 +ActionOpen=Ouvrir\u2026 diff --git a/data/config/Help_br_FR_breton.properties b/data/config/Help_br_FR_breton.properties new file mode 100644 index 0000000..e9571d3 --- /dev/null +++ b/data/config/Help_br_FR_breton.properties @@ -0,0 +1,18 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:40:37 CEST 2015 +AboutTitle=\ Diwar-benn +ActionAbout=Diwar-benn\u2026 +ActionBugReport=Digontammadur\u2026 +ActionClear=FR Effacer +ActionForcePack=FR Ajuste la fen\u00EAtre +ActionJConsole=FR Console +ActionLicence=Aotre +ActionLocale=Yezh +ChangeLocaleTitle=\ Che\u00F1chamant yezh +DumpTitle=\ Choaz ur fichenn roudo\u00F9 +HelpTitle=\ Sikour +JConsoleTitle=FR Console Java +LabelDumpFilter=Fichenn roud (.log) +LicenceTitle=\ Aotre +LocalizedTitle=\ Etrebroadel +MessageChooseLocale=Choaz ur yezh diff --git a/data/config/Help_br_FR_gallo.properties b/data/config/Help_br_FR_gallo.properties new file mode 100644 index 0000000..65bcae2 --- /dev/null +++ b/data/config/Help_br_FR_gallo.properties @@ -0,0 +1,18 @@ +#Produit automatiquement par BundleManager +#Tue Oct 13 20:07:38 CEST 2015 +AboutTitle=\ Entour du lojici\u00EB ; raport ao... +ActionAbout=Entour\u2026 +ActionBugReport=D\u00E9pou\u00E9zoner\u2026 +ActionClear=FR Effacer +ActionForcePack=FR Ajuste la fen\u00EAtre +ActionJConsole=FR Console +ActionLicence=Licence +ActionLocale=Langaije +ChangeLocaleTitle=\ Chanjer de langue +DumpTitle=\ chou\u00E9zi un fichier de routes +HelpTitle=\ A\u00EFde +JConsoleTitle=FR Console Java +LabelDumpFilter=Fichier de route (.log) +LicenceTitle=\ Licence +LocalizedTitle=\ Empllat +MessageChooseLocale=Chou\u00E9zi une langue diff --git a/data/config/Help_en_AU.properties b/data/config/Help_en_AU.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_AU.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_BG.properties b/data/config/Help_en_BG.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_BG.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_CA.properties b/data/config/Help_en_CA.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_CA.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_GB.properties b/data/config/Help_en_GB.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_GB.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_IE.properties b/data/config/Help_en_IE.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_IE.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_IN.properties b/data/config/Help_en_IN.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_IN.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_MT.properties b/data/config/Help_en_MT.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_MT.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_NZ.properties b/data/config/Help_en_NZ.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_NZ.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_PH.properties b/data/config/Help_en_PH.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_PH.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_SG.properties b/data/config/Help_en_SG.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_SG.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_en_US.properties b/data/config/Help_en_US.properties new file mode 100755 index 0000000..96cb7ae --- /dev/null +++ b/data/config/Help_en_US.properties @@ -0,0 +1,19 @@ +#Produit automatiquement par BundleManager +#Sat Jun 25 19:24:13 CEST 2011 +#ActionPackBug=Bug: Window.pack +AboutTitle=\ About +ActionAbout=About\u2026 +ActionBugReport=Bug repport\u2026 +ActionClear=Clear +ActionForcePack=Adjust window +ActionJConsole=Console +ActionLicence=Licence +ActionLocale=Locale +ChangeLocaleTitle=\ Change local +DumpTitle=\ Dump file chossen +HelpTitle=\ Help +JConsoleTitle=Java Console +LabelDumpFilter=Log file (.log) +LicenceTitle=\ Licence +LocalizedTitle= Localized label +MessageChooseLocale=Choose new locale diff --git a/data/config/Help_en_ZA.properties b/data/config/Help_en_ZA.properties new file mode 120000 index 0000000..d0fdcbd --- /dev/null +++ b/data/config/Help_en_ZA.properties @@ -0,0 +1 @@ +Help_en_US.properties \ No newline at end of file diff --git a/data/config/Help_es_ES.properties b/data/config/Help_es_ES.properties new file mode 100644 index 0000000..c5041b8 --- /dev/null +++ b/data/config/Help_es_ES.properties @@ -0,0 +1,18 @@ +#Produit automatiquement par BundleManager +#Sun Feb 04 15:57:59 CET 2018 +LocalizedTitle=\ Internacionalizaci\u00F3n +ActionAbout=A prop\u00F3sito de\u2026 +ActionLicence=Licencia +JConsoleTitle=Consola Java +ActionClear=Borrar +MessageChooseLocale=Selecciona unidioma +ActionLocale=Idioma +ChangeLocaleTitle=\ Cambio de idioma +LabelDumpFilter=Archivo Log (.log) +HelpTitle=\ Ayuda +DumpTitle=\ Elecci\u00F3n de un archivo dump +LicenceTitle=\ Licencia +ActionJConsole=Consola +AboutTitle=\ A prop\u00F3sito de +ActionBugReport=Depuraci\u00F3n\u2026 +ActionForcePack=Ajusta la ventana diff --git a/data/config/Help_fr_BE.properties b/data/config/Help_fr_BE.properties new file mode 120000 index 0000000..40b2de9 --- /dev/null +++ b/data/config/Help_fr_BE.properties @@ -0,0 +1 @@ +Help_fr_FR.properties \ No newline at end of file diff --git a/data/config/Help_fr_CA.properties b/data/config/Help_fr_CA.properties new file mode 120000 index 0000000..40b2de9 --- /dev/null +++ b/data/config/Help_fr_CA.properties @@ -0,0 +1 @@ +Help_fr_FR.properties \ No newline at end of file diff --git a/data/config/Help_fr_CH.properties b/data/config/Help_fr_CH.properties new file mode 120000 index 0000000..40b2de9 --- /dev/null +++ b/data/config/Help_fr_CH.properties @@ -0,0 +1 @@ +Help_fr_FR.properties \ No newline at end of file diff --git a/data/config/Help_fr_FR.properties b/data/config/Help_fr_FR.properties new file mode 100755 index 0000000..1c30112 --- /dev/null +++ b/data/config/Help_fr_FR.properties @@ -0,0 +1,19 @@ +#Produit automatiquement par BundleManager +#Sat Jun 25 19:24:13 CEST 2011 +#ActionPackBug=Anomalie : Window.pack +AboutTitle=\ A propos +ActionAbout=A propos\u2026 +ActionBugReport=D\u00E9verminage\u2026 +ActionClear=Effacer +ActionForcePack=Ajuste la fen\u00EAtre +ActionJConsole=Console +ActionLicence=Licence +ActionLocale=Langage +ChangeLocaleTitle=\ Changement de langue +DumpTitle=\ Choix d''un fichier de traces +HelpTitle=\ Aide +JConsoleTitle=Console Java +LabelDumpFilter=Fichier de trace (.log) +LicenceTitle=\ Licence +LocalizedTitle=\ Internationalisation +MessageChooseLocale=Choisissez une langue diff --git a/data/config/Help_fr_LU.properties b/data/config/Help_fr_LU.properties new file mode 120000 index 0000000..40b2de9 --- /dev/null +++ b/data/config/Help_fr_LU.properties @@ -0,0 +1 @@ +Help_fr_FR.properties \ No newline at end of file diff --git a/data/config/Login.xml b/data/config/Login.xml new file mode 100644 index 0000000..72cc4df --- /dev/null +++ b/data/config/Login.xml @@ -0,0 +1,69 @@ + + + +This file is automaticaly generated by Login application at 8:54 AM on Aug 17, 2018. +1 +0 +3 +127.0.0.10 +data/images/login/login.png +2 +[x=50,y=50] +10 +false +true +felix +10 +true +[x=50,y=50] +false +[x=50,y=50] +false +1 + + +data/log +[x=50,y=50] +1 +10 + +10 +1 +true +false +1 +true +80 +8080 +1 +North +/ +localhost +1 +10 +false +North + + +data/log + +10 +projets.iut-info-vannes.net +false +1 +2 +false +false +North +[x=50,y=50] +2 +[x=50,y=50] +North +SetClient +[x=50,y=50] +10 +10 +10 +[x=50,y=50] +false + diff --git a/data/config/Login_en_US.properties b/data/config/Login_en_US.properties new file mode 100644 index 0000000..3b48ee1 --- /dev/null +++ b/data/config/Login_en_US.properties @@ -0,0 +1,38 @@ +#Produit automatiquement par BundleManager +#Thu Jun 30 17:50:09 CEST 2011 +ActionAddAdmin=Add administrator +ActionAddMember=Add member +#ActionConnect=Connect +ActionCreateGroup=Create group +#ActionDisconnect=Disconnect +ActionEditGroup=Edit group +ActionEditLogin=Edit profile +ActionJoinGroup=Join group +ActionLeftGroup=Left group +ActionLog=Log +ActionRemoveAdmin=Remove administrator +ActionRemoveGroup=Remove group +ActionRemoveMember=Remove member +ActionUnlog=Unlog +GroupTitle=\ Group +LabelConfirmPasswd=Confirm password\: +LabelCreatedAt=Created at\: +LabelEmail=Email\: +LabelGroup=Group\: +LabelGroupName=Group name\: +LabelJoinGroup=Choose a group to join\: +LabelLogged=Logged\: +LabelLogin=Login\: +LabelNewPasswd=New password\: +LabelOldPasswd=Old password\: +LabelPassword=Password\: +LabelState=State\: +LabelTry=Try n\u00B0\: +LabelUpdatedAt=Updated at\: +LabelUpdatedBy=By\: +LabelUpdatedByIP=Created by (IP@)\: +LoginTitle=\ Login ({0}) +ConnectionTitle=\ Connection +ManageGroupTitle=\ Manage Group +ManageLoginTitle=Manage profile +anonymous=anonymous diff --git a/data/config/Login_fr_FR.properties b/data/config/Login_fr_FR.properties new file mode 100644 index 0000000..cc64cab --- /dev/null +++ b/data/config/Login_fr_FR.properties @@ -0,0 +1,38 @@ +#Produit automatiquement par BundleManager +#Thu Jun 30 17:50:09 CEST 2011 +#ActionConnect=Connecter +#ActionDisconnect=D\u00E9connecter +ActionAddAdmin=Ajoute un administrateur +ActionAddMember=Ajoute un membre +ActionCreateGroup=Cr\u00E9er un nouveau groupe +ActionEditGroup=Modifie un group +ActionEditLogin=Modifie le profil +ActionJoinGroup=Rejoindre un groupe +ActionLeftGroup=Quitter un groupe +ActionLog=Indetification +ActionRemoveAdmin=Retire un administrateur +ActionRemoveGroup=supprime un groupe +ActionRemoveMember=Retire un membre +ActionUnlog=Retrai d''identit\u00E9 +ConnectionTitle=\ Connexion +GroupTitle=\ Groupe +LabelConfirmPasswd=V\u00E9rification du mot de passe \: +LabelCreatedAt=Cr\u00E9\u00E9 le \: +LabelEmail=Mel \: +LabelGroup=Groupe \: +LabelGroupName=Nom du groupe \: +LabelJoinGroup=Choisissez un groupe \u00E0 rejoindre \: +LabelLogged=Connect\u00E9 \: +LabelLogin=Pseudonyme \: +LabelNewPasswd=Nouveau mot de passe \: +LabelOldPasswd=Ancien mot de passe \: +LabelPassword=Mot de passe \: +LabelState=Etat \: +LabelTry=Essai n\u00B0 \: +LabelUpdatedAt=Mise \u00E0 jour le \: +LabelUpdatedBy=Par \: +LabelUpdatedByIP=Cr\u00E9\u00E9 par (@ IP) \: +LoginTitle=\ Connexion ({0}) +ManageGroupTitle=\ Gestion des groupes +ManageLoginTitle=Gestion du profil +anonymous=inconnu diff --git a/data/config/Misc.xml b/data/config/Misc.xml new file mode 100644 index 0000000..e0657a8 --- /dev/null +++ b/data/config/Misc.xml @@ -0,0 +1,17 @@ + + + +This file is automaticaly generated by Misc application at 10:48 AM on Jan 28, 2021. + +[x=50,y=50] +[x=50,y=50] +[x=50,y=50] +false +false +false +[x=50,y=50] + + +data/images/misc.png +false + diff --git a/data/config/Protocol_en_US.properties b/data/config/Protocol_en_US.properties new file mode 100644 index 0000000..89f0268 --- /dev/null +++ b/data/config/Protocol_en_US.properties @@ -0,0 +1,11 @@ +ActionLinkType=Link network +ActionPublishDodwan=Publish +ActionSetClient=Client +ActionSetDodwan=Dodwan +ActionSetProxy=Proxy +ActionSetServer=Server +ActionUnsetProtocol=None +LabelAvailableNetworkCard=Your network cards\: +LabelConnectionType=What kind of connexion do you want? +LinkTypeTitle=\ Connection type manager +NetworkTitle=\ Network diff --git a/data/config/Protocol_fr_FR.properties b/data/config/Protocol_fr_FR.properties new file mode 100644 index 0000000..544ee8c --- /dev/null +++ b/data/config/Protocol_fr_FR.properties @@ -0,0 +1,11 @@ +ActionLinkType=Relier le r\u00E9seau +ActionPublishDodwan=Publier +ActionSetClient=Client +ActionSetDodwan=Dodwan +ActionSetProxy=Mandataire +ActionSetServer=Serveur +ActionUnsetProtocol=Aucun +LabelAvailableNetworkCard=Vos cartes r\u00E9seau \: +LabelConnectionType=Quel type de connexion souhaitez-vous ? +LinkTypeTitle=\ Gestionnaire du type de connexion +NetworkTitle=\ R\u00E9seau diff --git a/data/config/Proxy_br_FR_breton.properties b/data/config/Proxy_br_FR_breton.properties new file mode 100644 index 0000000..3169988 --- /dev/null +++ b/data/config/Proxy_br_FR_breton.properties @@ -0,0 +1,7 @@ +ActionSetProxy=FR Configuration r\u00E9seau\u2026 +ProxyTitle=FR Configuration R\u00e9seau (Proxy) +LabelHost=FR Serveur du mandataire \: +LabelPort=FR Port du mandataire \: +ActionNoProxy=FR Ne pas utiliser de Mandataire +ActionSystemConfigProxy=FR Utiliser la configuration Java du mandataire +ActionManualConfigProxy=FR D\u00E9finir manuellement la configuration du mandataire diff --git a/data/config/Proxy_br_FR_gallo.properties b/data/config/Proxy_br_FR_gallo.properties new file mode 100644 index 0000000..3169988 --- /dev/null +++ b/data/config/Proxy_br_FR_gallo.properties @@ -0,0 +1,7 @@ +ActionSetProxy=FR Configuration r\u00E9seau\u2026 +ProxyTitle=FR Configuration R\u00e9seau (Proxy) +LabelHost=FR Serveur du mandataire \: +LabelPort=FR Port du mandataire \: +ActionNoProxy=FR Ne pas utiliser de Mandataire +ActionSystemConfigProxy=FR Utiliser la configuration Java du mandataire +ActionManualConfigProxy=FR D\u00E9finir manuellement la configuration du mandataire diff --git a/data/config/Proxy_en_AU.properties b/data/config/Proxy_en_AU.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_AU.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_BG.properties b/data/config/Proxy_en_BG.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_BG.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_CA.properties b/data/config/Proxy_en_CA.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_CA.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_GB.properties b/data/config/Proxy_en_GB.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_GB.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_IE.properties b/data/config/Proxy_en_IE.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_IE.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_IN.properties b/data/config/Proxy_en_IN.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_IN.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_MT.properties b/data/config/Proxy_en_MT.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_MT.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_NZ.properties b/data/config/Proxy_en_NZ.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_NZ.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_PH.properties b/data/config/Proxy_en_PH.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_PH.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_SG.properties b/data/config/Proxy_en_SG.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_SG.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_en_US.properties b/data/config/Proxy_en_US.properties new file mode 100755 index 0000000..2b166ed --- /dev/null +++ b/data/config/Proxy_en_US.properties @@ -0,0 +1,7 @@ +ActionSetProxy=Network configuration +ProxyTitle=Network configuration +LabelHost=Host\: +LabelPort=Port\: +ActionNoProxy=Use no proxy +ActionSystemConfigProxy=Use Java VM proxy configuration +ActionManualConfigProxy=Manual proxy definition diff --git a/data/config/Proxy_en_ZA.properties b/data/config/Proxy_en_ZA.properties new file mode 120000 index 0000000..5050b62 --- /dev/null +++ b/data/config/Proxy_en_ZA.properties @@ -0,0 +1 @@ +Proxy_en_US.properties \ No newline at end of file diff --git a/data/config/Proxy_es_ES.properties b/data/config/Proxy_es_ES.properties new file mode 100644 index 0000000..741a2d8 --- /dev/null +++ b/data/config/Proxy_es_ES.properties @@ -0,0 +1,9 @@ +#Produit automatiquement par BundleManager +#Sun Feb 04 16:05:53 CET 2018 +ActionNoProxy=No utilizar proxy +ActionSetProxy=Configuraci\u00F3n de red +LabelHost=Servidor proxy \: +ProxyTitle=CConfiguraci\u00F3n de red (Proxy) +ActionSystemConfigProxy=Utilizar la configuraci\u00F3n Java VM del proxy +ActionManualConfigProxy=Especificar manualmente las opciones del proxy +LabelPort=Puerto de proxy \: diff --git a/data/config/Proxy_fr_BE.properties b/data/config/Proxy_fr_BE.properties new file mode 120000 index 0000000..e261976 --- /dev/null +++ b/data/config/Proxy_fr_BE.properties @@ -0,0 +1 @@ +Proxy_fr_FR.properties \ No newline at end of file diff --git a/data/config/Proxy_fr_CA.properties b/data/config/Proxy_fr_CA.properties new file mode 120000 index 0000000..e261976 --- /dev/null +++ b/data/config/Proxy_fr_CA.properties @@ -0,0 +1 @@ +Proxy_fr_FR.properties \ No newline at end of file diff --git a/data/config/Proxy_fr_CH.properties b/data/config/Proxy_fr_CH.properties new file mode 120000 index 0000000..e261976 --- /dev/null +++ b/data/config/Proxy_fr_CH.properties @@ -0,0 +1 @@ +Proxy_fr_FR.properties \ No newline at end of file diff --git a/data/config/Proxy_fr_FR.properties b/data/config/Proxy_fr_FR.properties new file mode 100755 index 0000000..3e6341a --- /dev/null +++ b/data/config/Proxy_fr_FR.properties @@ -0,0 +1,7 @@ +ActionSetProxy=Configuration r\u00E9seau\u2026 +ProxyTitle=Configuration R\u00e9seau (Proxy) +LabelHost=Serveur du mandataire \: +LabelPort=Port du mandataire \: +ActionNoProxy=Ne pas utiliser de Mandataire +ActionSystemConfigProxy=Utiliser la configuration Java du mandataire +ActionManualConfigProxy=D\u00E9finir manuellement la configuration du mandataire diff --git a/data/config/Proxy_fr_LU.properties b/data/config/Proxy_fr_LU.properties new file mode 120000 index 0000000..e261976 --- /dev/null +++ b/data/config/Proxy_fr_LU.properties @@ -0,0 +1 @@ +Proxy_fr_FR.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_br_FR_breton.properties b/data/config/RemoteUpdate_br_FR_breton.properties new file mode 100644 index 0000000..638269a --- /dev/null +++ b/data/config/RemoteUpdate_br_FR_breton.properties @@ -0,0 +1,36 @@ +#Produit automatiquement par BundleManager +#Sat Feb 03 16:17:07 CET 2018 +ActionUpload=FR T\u00E9l\u00E9versement +LabelLocalDate=FR Date local +EnumCheckPeriodMonth=Miz +EnumCheckPeriodYear=Bloavezh +LabelRemove=FR Supprim\u00E9 +ActionOnline=FR Mise en ligne\u2026 +ActionLocalRemove=FR Nettoyer fichiers locaux +MessageUploadCompleted=FR {0,choice,0\# Aucun t\u00E9l\u00E9verssement|0< {0} fichiers t\u00E9l\u00E9vers\u00E9s.} +LabelLocal=FR Local +EnumCheckPeriodWeek=Sizhun +LabelFileName=FR Fichier +EnumCheckPeriodDay=Deiz +UpdateSoftTitle=FR Mise \u00E0 jour +LabelRemoteSize=FR Taille distante +MessageDownloadInfo={0} \: {1,choice,0\# (FR pas de serveur)|0< '{2,choice,0\# 0 fichenn|1\# 1 fichenn|1< {2,number,integer} fichenno\u00F9}' ({3}o)} +LabelRemote=FR Distant +ActionDetails=FR D\u00E9tails\u2026 +DownloadTitle=\ Pellgarga\u00F1 bremanaduro\u00F9 +MessageUpload=FR T\u00E9l\u00E9versement +LabelDownload=FR T\u00E9l\u00E9charg\u00E9 +ActionUpdate=Bremanadur\u2026 +MessageDownloadCompleted={0,choice,0\# ebet pellgarga\u00F1|0< {0} fichenno\u00F9 pellgarget. Kuitaat hag adla\u00F1sa\u00F1 ar poellad.} +MessageLocalRemove=FR Nettoyage local +LabelRemoteDate=FR Date distante +ActionDownload=FR T\u00E9l\u00E9chargement +MessageUpdateSoft=FR Relancer la nouvelle version +EnumCheckPeriodNoCheck=Gwech ebet +LabelLocalSize=FR Taille local +DetailsTitle=FR D\u00E9tails +ActionRemoteRemove=FR Nettoyer fichiers distants +LabelCheckingPeriod=Gwiria\u00F1 bremanadurio\u00F9 \: +MessageDownload=Pellgarga\u00F1 +UploadTitle=FR T\u00E9l\u00E9versement +LabelUpload=FR T\u00E9l\u00E9vers\u00E9 diff --git a/data/config/RemoteUpdate_br_FR_gallo.properties b/data/config/RemoteUpdate_br_FR_gallo.properties new file mode 100644 index 0000000..0528aa6 --- /dev/null +++ b/data/config/RemoteUpdate_br_FR_gallo.properties @@ -0,0 +1,35 @@ +#Produit automatiquement par BundleManager +#Sat Feb 03 16:17:07 CET 2018 +ActionUpload=FR T\u00E9l\u00E9versement +LabelLocalDate=FR Date local +EnumCheckPeriodMonth=Mou\u00E9z +EnumCheckPeriodYear=An\u00E9e +LabelRemove=FR Supprim\u00E9 +ActionOnline=FR Mise en ligne\u2026 +ActionLocalRemove=FR Nettoyer fichiers locaux +MessageUploadCompleted=FR {0,choice,0\# Aucun t\u00E9l\u00E9verssement|0< {0} fichiers t\u00E9l\u00E9vers\u00E9s.} +LabelLocal=FR Local +EnumCheckPeriodWeek=Smaine +LabelFileName=FR Fichier +EnumCheckPeriodDay=Jou +UpdateSoftTitle=FR Mise \u00E0 jour +LabelRemoteSize=FR Taille distante +MessageDownloadInfo=FR {0} \: {1,choice,0\# pas de serveur|0< '{2,choice,0\# pas de fichier|1\# 1 fichier|1< {2,number,integer} fichiers}' ({3}o) +LabelRemote=FR Distant +DownloadTitle=\ Telecharjer les abuteries +MessageUpload=FR T\u00E9l\u00E9versement +MessageDownloadCompleted=FR {0,choice,0\# Aucun t\u00E9l\u00E9chargement|0< {0} fichiers t\u00E9l\u00E9charg\u00E9s.\n Vous devez quitter et relancer le logiciel.} +LabelDownload=FR T\u00E9l\u00E9charg\u00E9 +ActionUpdate=Abuter\u2026 +MessageLocalRemove=FR Nettoyage local +LabelRemoteDate=FR Date distante +ActionDownload=FR T\u00E9l\u00E9chargement +MessageUpdateSoft=FR Relancer la nouvelle version +EnumCheckPeriodNoCheck=Pas jam\u00E9s +LabelLocalSize=FR Taille local +DetailsTitle=FR D\u00E9tails +ActionRemoteRemove=FR Nettoyer fichiers distants +LabelCheckingPeriod=Ergarder \u00E9s abuteries \: +UploadTitle=FR T\u00E9l\u00E9versement +MessageDownload=T\u00E9l\u00E9charjer +LabelUpload=FR T\u00E9l\u00E9vers\u00E9 diff --git a/data/config/RemoteUpdate_en_AU.properties b/data/config/RemoteUpdate_en_AU.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_AU.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_BG.properties b/data/config/RemoteUpdate_en_BG.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_BG.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_CA.properties b/data/config/RemoteUpdate_en_CA.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_CA.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_GB.properties b/data/config/RemoteUpdate_en_GB.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_GB.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_IE.properties b/data/config/RemoteUpdate_en_IE.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_IE.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_IN.properties b/data/config/RemoteUpdate_en_IN.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_IN.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_MT.properties b/data/config/RemoteUpdate_en_MT.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_MT.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_NZ.properties b/data/config/RemoteUpdate_en_NZ.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_NZ.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_PH.properties b/data/config/RemoteUpdate_en_PH.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_PH.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_SG.properties b/data/config/RemoteUpdate_en_SG.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_SG.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_en_US.properties b/data/config/RemoteUpdate_en_US.properties new file mode 100755 index 0000000..86739ec --- /dev/null +++ b/data/config/RemoteUpdate_en_US.properties @@ -0,0 +1,35 @@ +#Produit automatiquement par BundleManager +#Sat Feb 03 16:17:07 CET 2018 +ActionUpload=Upload +LabelLocalDate=Local date +LabelRemove=Removed +EnumCheckPeriodMonth=Month +EnumCheckPeriodYear=Year +ActionOnline=Online\u2026 +ActionLocalRemove=Clean local files +MessageUploadCompleted={0,choice,0\# No upload|0< {0} files to upload}. +LabelLocal=Local +EnumCheckPeriodWeek=Week +LabelFileName=File +EnumCheckPeriodDay=Day +UpdateSoftTitle=Update soft needed +LabelRemoteSize=Remote size +MessageDownloadInfo={0}\: {1,choice,0\#Server not responding|0< '{2,choice,0\# no file|1\# 1 file|1< {2,number,integer} files}' ({3}B)} +LabelRemote=Remote +ActionDetails=Details\u2026 +DownloadTitle=Download update +MessageUpload=Upload +MessageDownloadCompleted={0,choice,0\# No upload|0< {0} files uploaded.} +ActionUpdate=Update\u2026 +LabelDownload=Downloaded +LabelRemoteDate=Remote date +ActionDownload=Download +MessageUpdateSoft=Restart now +LabelLocalSize=Local size +DetailsTitle=Details +EnumCheckPeriodNoCheck=Never +ActionRemoteRemove=Clean remote files +LabelCheckingPeriod=Cheking update\: +UploadTitle=\ Upload +MessageDownload=Download +LabelUpload=Uploaded diff --git a/data/config/RemoteUpdate_en_ZA.properties b/data/config/RemoteUpdate_en_ZA.properties new file mode 120000 index 0000000..057d0a9 --- /dev/null +++ b/data/config/RemoteUpdate_en_ZA.properties @@ -0,0 +1 @@ +RemoteUpdate_en_US.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_es_ES.properties b/data/config/RemoteUpdate_es_ES.properties new file mode 100644 index 0000000..1697afa --- /dev/null +++ b/data/config/RemoteUpdate_es_ES.properties @@ -0,0 +1,36 @@ +!#Produit automatiquement par BundleManager +#Sun Feb 04 16:11:51 CET 2018 +ActionUpload=Cargar +LabelLocalDate=Data local +LabelRemove=Borrado +EnumCheckPeriodMonth=Mes +EnumCheckPeriodYear=A\u00F1o +ActionOnline=Puesta en l\u00EDnea\u2026 +ActionLocalRemove=Limpiar los archives locales +MessageUploadCompleted={0,choice,0\# Ningunacarga|0< {0} archivos cargados} +LabelLocal=Local +EnumCheckPeriodWeek=Semana +LabelFileName=Archivo +EnumCheckPeriodDay=D\u00EDa +UpdateSoftTitle=UpdateSoftTitle +LabelRemoteSize=Tama\u00F1o Remoto +MessageDownloadInfo={0}\: {1,choice,0\#Server not responding|0< '{2,choice,0\# no file|1\# 1 file|1< {2,number,integer} files}' ({3}B)} +LabelRemote=Remoto +ActionDetails=Detalles\u2026 +DownloadTitle=Descarga de las actualizaciones +MessageUpload=Carga +MessageDownloadCompleted={0,choice,0\# No upload|0< {0} files uploaded.} +ActionUpdate=Actualizaci\u00F3n\u2026 +LabelDownload=Descargado +MessageLocalRemove=MessageLocalRemove +LabelRemoteDate=Data Remoto +ActionDownload=Descarga +MessageUpdateSoft=MessageUpdateSoft +LabelLocalSize=Tama\u00F1o local +DetailsTitle=Detalles +EnumCheckPeriodNoCheck=Nunca +ActionRemoteRemove=Limpiar los archivos remotos +LabelCheckingPeriod=Verificaci\u00F3n de las actualizaciones \: +UploadTitle=Carga +MessageDownload=Descarga +LabelUpload=Cargado diff --git a/data/config/RemoteUpdate_fr_BE.properties b/data/config/RemoteUpdate_fr_BE.properties new file mode 120000 index 0000000..c0a598c --- /dev/null +++ b/data/config/RemoteUpdate_fr_BE.properties @@ -0,0 +1 @@ +RemoteUpdate_fr_FR.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_fr_CA.properties b/data/config/RemoteUpdate_fr_CA.properties new file mode 120000 index 0000000..c0a598c --- /dev/null +++ b/data/config/RemoteUpdate_fr_CA.properties @@ -0,0 +1 @@ +RemoteUpdate_fr_FR.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_fr_CH.properties b/data/config/RemoteUpdate_fr_CH.properties new file mode 120000 index 0000000..c0a598c --- /dev/null +++ b/data/config/RemoteUpdate_fr_CH.properties @@ -0,0 +1 @@ +RemoteUpdate_fr_FR.properties \ No newline at end of file diff --git a/data/config/RemoteUpdate_fr_FR.properties b/data/config/RemoteUpdate_fr_FR.properties new file mode 100755 index 0000000..d0b4aca --- /dev/null +++ b/data/config/RemoteUpdate_fr_FR.properties @@ -0,0 +1,36 @@ +#Produit automatiquement par BundleManager +#Sat Feb 03 16:17:07 CET 2018 +ActionUpload=T\u00E9l\u00E9versement +LabelLocalDate=Date local +LabelRemove=Supprim\u00E9 +EnumCheckPeriodMonth=Mois +EnumCheckPeriodYear=Ann\u00E9e +ActionOnline=Mise en ligne\u2026 +ActionLocalRemove=Nettoyer fichiers locaux +MessageUploadCompleted={0,choice,0\# Aucun t\u00E9l\u00E9verssement|0< {0} fichiers t\u00E9l\u00E9vers\u00E9s}. +LabelLocal=Local +EnumCheckPeriodWeek=Semaine +LabelFileName=Fichier +EnumCheckPeriodDay=Jour +UpdateSoftTitle=Mise \u00E0 jour +LabelRemoteSize=Taille distante +MessageDownloadInfo={0} \: {1,choice,0\# Le serveur ne r\u00E9pond pas|0< '{2,choice,0\# pas de fichier|1\# 1 fichier|1< {2,number,integer} fichiers}' ({3}o)} +LabelRemote=Distant +ActionDetails=D\u00E9tails\u2026 +DownloadTitle=T\u00E9l\u00E9chargement des mises \u00E0 jour +MessageUpload=T\u00E9l\u00E9versement +MessageDownloadCompleted={0,choice,0\# Aucun t\u00E9l\u00E9chargement|0< {0} fichiers t\u00E9l\u00E9charg\u00E9s.\n Vous devez quitter et relancer le logiciel.} +ActionUpdate=Mise \u00E0 jour\u2026 +LabelDownload=T\u00E9l\u00E9charg\u00E9 +MessageLocalRemove=Nettoyage local +LabelRemoteDate=Date distante +ActionDownload=T\u00E9l\u00E9chargement +MessageUpdateSoft=Relancer la nouvelle version ? \n (si ce message revient lancez UpdatedAdecWatt.jar) +LabelLocalSize=Taille local +DetailsTitle=D\u00E9tails +EnumCheckPeriodNoCheck=Jamais +ActionRemoteRemove=Nettoyer fichiers distants +LabelCheckingPeriod=V\u00E9rification des mises \u00E0 jour \: +UploadTitle=\ T\u00E9l\u00E9versement +MessageDownload=T\u00E9l\u00E9chargement +LabelUpload=T\u00E9l\u00E9vers\u00E9 diff --git a/data/config/RemoteUpdate_fr_LU.properties b/data/config/RemoteUpdate_fr_LU.properties new file mode 120000 index 0000000..c0a598c --- /dev/null +++ b/data/config/RemoteUpdate_fr_LU.properties @@ -0,0 +1 @@ +RemoteUpdate_fr_FR.properties \ No newline at end of file diff --git a/data/config/Story_br_FR_breton.properties b/data/config/Story_br_FR_breton.properties new file mode 100644 index 0000000..8bae49f --- /dev/null +++ b/data/config/Story_br_FR_breton.properties @@ -0,0 +1,4 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:46:00 CEST 2015 +ActionUndo=Dispenn +ActionRedo=Adkregi\u00F1 diff --git a/data/config/Story_br_FR_gallo.properties b/data/config/Story_br_FR_gallo.properties new file mode 100644 index 0000000..d803e64 --- /dev/null +++ b/data/config/Story_br_FR_gallo.properties @@ -0,0 +1,4 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:46:00 CEST 2015 +ActionUndo=D\u00E9marrer +ActionRedo=Erf\u00E9re diff --git a/data/config/Story_en_AU.properties b/data/config/Story_en_AU.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_AU.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_BG.properties b/data/config/Story_en_BG.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_BG.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_CA.properties b/data/config/Story_en_CA.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_CA.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_GB.properties b/data/config/Story_en_GB.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_GB.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_IE.properties b/data/config/Story_en_IE.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_IE.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_IN.properties b/data/config/Story_en_IN.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_IN.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_MT.properties b/data/config/Story_en_MT.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_MT.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_NZ.properties b/data/config/Story_en_NZ.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_NZ.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_PH.properties b/data/config/Story_en_PH.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_PH.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_SG.properties b/data/config/Story_en_SG.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_SG.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_en_US.properties b/data/config/Story_en_US.properties new file mode 100755 index 0000000..c2af387 --- /dev/null +++ b/data/config/Story_en_US.properties @@ -0,0 +1,2 @@ +ActionRedo=Redo +ActionUndo=Undo diff --git a/data/config/Story_en_ZA.properties b/data/config/Story_en_ZA.properties new file mode 120000 index 0000000..a4ee065 --- /dev/null +++ b/data/config/Story_en_ZA.properties @@ -0,0 +1 @@ +Story_en_US.properties \ No newline at end of file diff --git a/data/config/Story_es_ES.properties b/data/config/Story_es_ES.properties new file mode 100644 index 0000000..728059a --- /dev/null +++ b/data/config/Story_es_ES.properties @@ -0,0 +1,2 @@ +ActionRedo=Repetir +ActionUndo=Deshacer diff --git a/data/config/Story_fr_BE.properties b/data/config/Story_fr_BE.properties new file mode 120000 index 0000000..9f4d1cc --- /dev/null +++ b/data/config/Story_fr_BE.properties @@ -0,0 +1 @@ +Story_fr_FR.properties \ No newline at end of file diff --git a/data/config/Story_fr_CA.properties b/data/config/Story_fr_CA.properties new file mode 120000 index 0000000..9f4d1cc --- /dev/null +++ b/data/config/Story_fr_CA.properties @@ -0,0 +1 @@ +Story_fr_FR.properties \ No newline at end of file diff --git a/data/config/Story_fr_CH.properties b/data/config/Story_fr_CH.properties new file mode 120000 index 0000000..9f4d1cc --- /dev/null +++ b/data/config/Story_fr_CH.properties @@ -0,0 +1 @@ +Story_fr_FR.properties \ No newline at end of file diff --git a/data/config/Story_fr_FR.properties b/data/config/Story_fr_FR.properties new file mode 100755 index 0000000..2931fb4 --- /dev/null +++ b/data/config/Story_fr_FR.properties @@ -0,0 +1,2 @@ +ActionRedo=Refaire +ActionUndo=D\u00E9faire diff --git a/data/config/Story_fr_LU.properties b/data/config/Story_fr_LU.properties new file mode 120000 index 0000000..9f4d1cc --- /dev/null +++ b/data/config/Story_fr_LU.properties @@ -0,0 +1 @@ +Story_fr_FR.properties \ No newline at end of file diff --git a/data/config/ToolBar_br_FR_breton.properties b/data/config/ToolBar_br_FR_breton.properties new file mode 100644 index 0000000..c38e2f9 --- /dev/null +++ b/data/config/ToolBar_br_FR_breton.properties @@ -0,0 +1,9 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:49:15 CEST 2015 +NotCardinalPoint=FR {0} n''est pas un point cardinal \! +ActionUp=Pignat +AlreadyRegistredTool=Barre d\u00E9tachable {0} d\u00E9j\u00E0 enregistr\u00E9e au {1} \! +LabelToolBarNamed=Barrenno\u00F9 oustilho\u00F9 +ActionDown=Diskenn +LabelToolBarShowed=Diskouez +ActionToolBarProfil=Mererezh barrenno\u00F9 oustilho\u00F9\u2026 diff --git a/data/config/ToolBar_br_FR_gallo.properties b/data/config/ToolBar_br_FR_gallo.properties new file mode 100644 index 0000000..bbe3f6a --- /dev/null +++ b/data/config/ToolBar_br_FR_gallo.properties @@ -0,0 +1,9 @@ +#Produit automatiquement par BundleManager +#Sat Oct 03 19:49:15 CEST 2015 +NotCardinalPoint=FR {0} n''est pas un point cardinal \! +ActionUp=Aler olmont +AlreadyRegistredTool=FR Barre d\u00E9tachable {0} d\u00E9j\u00E0 enregistr\u00E9e au {1} \! +ActionDown=Aler olva +LabelToolBarNamed=Bare \u00E9s outis +LabelToolBarShowed=Amontr\u00EB +ActionToolBarProfil=Se chevi de la bare \u00E9s outis\u2026 diff --git a/data/config/ToolBar_en_AU.properties b/data/config/ToolBar_en_AU.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_AU.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_BG.properties b/data/config/ToolBar_en_BG.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_BG.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_CA.properties b/data/config/ToolBar_en_CA.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_CA.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_GB.properties b/data/config/ToolBar_en_GB.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_GB.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_IE.properties b/data/config/ToolBar_en_IE.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_IE.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_IN.properties b/data/config/ToolBar_en_IN.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_IN.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_MT.properties b/data/config/ToolBar_en_MT.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_MT.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_NZ.properties b/data/config/ToolBar_en_NZ.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_NZ.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_PH.properties b/data/config/ToolBar_en_PH.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_PH.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_SG.properties b/data/config/ToolBar_en_SG.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_SG.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_en_US.properties b/data/config/ToolBar_en_US.properties new file mode 100755 index 0000000..8160ed2 --- /dev/null +++ b/data/config/ToolBar_en_US.properties @@ -0,0 +1,9 @@ +#Produit automatiquement par BundleManager +#Fri Oct 02 10:48:46 CEST 2015 +NotCardinalPoint={0} is not a cardinal point\! +AlreadyRegistredTool=ToolBar {0} already regitered at {1}\! +ActionUp=Up +ActionDown=Down +LabelToolBarNamed=Tool bar +LabelToolBarShowed=Showed +ActionToolBarProfil=Tool bar managment\u2026 diff --git a/data/config/ToolBar_en_ZA.properties b/data/config/ToolBar_en_ZA.properties new file mode 120000 index 0000000..bac78cd --- /dev/null +++ b/data/config/ToolBar_en_ZA.properties @@ -0,0 +1 @@ +ToolBar_en_US.properties \ No newline at end of file diff --git a/data/config/ToolBar_es_ES.properties b/data/config/ToolBar_es_ES.properties new file mode 100644 index 0000000..ea29cbe --- /dev/null +++ b/data/config/ToolBar_es_ES.properties @@ -0,0 +1,9 @@ +#Produit automatiquement par BundleManager +#Sun Feb 04 16:01:44 CET 2018 +NotCardinalPoint={0} no es un punto cardinal \! +ActionUp=Arriba +AlreadyRegistredTool=Barra de herramientas {0} ya registrada en {1} \! +LabelToolBarNamed=Barra de herramientas +ActionDown=Abajo +LabelToolBarShowed=Ense\u00F1a +ActionToolBarProfil=Gesti\u00F3n de las barras de herramientas\u2026 diff --git a/data/config/ToolBar_fr_BE.properties b/data/config/ToolBar_fr_BE.properties new file mode 120000 index 0000000..7a113da --- /dev/null +++ b/data/config/ToolBar_fr_BE.properties @@ -0,0 +1 @@ +ToolBar_fr_FR.properties \ No newline at end of file diff --git a/data/config/ToolBar_fr_CA.properties b/data/config/ToolBar_fr_CA.properties new file mode 120000 index 0000000..7a113da --- /dev/null +++ b/data/config/ToolBar_fr_CA.properties @@ -0,0 +1 @@ +ToolBar_fr_FR.properties \ No newline at end of file diff --git a/data/config/ToolBar_fr_CH.properties b/data/config/ToolBar_fr_CH.properties new file mode 120000 index 0000000..7a113da --- /dev/null +++ b/data/config/ToolBar_fr_CH.properties @@ -0,0 +1 @@ +ToolBar_fr_FR.properties \ No newline at end of file diff --git a/data/config/ToolBar_fr_FR.properties b/data/config/ToolBar_fr_FR.properties new file mode 100755 index 0000000..c5f9b55 --- /dev/null +++ b/data/config/ToolBar_fr_FR.properties @@ -0,0 +1,9 @@ +#Produit automatiquement par BundleManager +#Fri Oct 02 10:48:46 CEST 2015 +NotCardinalPoint={0} n''est pas un point cardinal \! +AlreadyRegistredTool=Barre d\u00E9tachable {0} d\u00E9j\u00E0 enregistr\u00E9e au {1} \! +ActionUp=Monter +ActionDown=Descendre +LabelToolBarNamed=Barre d''outils +LabelToolBarShowed=Montr\u00E9e +ActionToolBarProfil=Gestion des barres d''outils \u2026 diff --git a/data/config/ToolBar_fr_LU.properties b/data/config/ToolBar_fr_LU.properties new file mode 120000 index 0000000..7a113da --- /dev/null +++ b/data/config/ToolBar_fr_LU.properties @@ -0,0 +1 @@ +ToolBar_fr_FR.properties \ No newline at end of file diff --git a/data/images/bundle/bundle.png b/data/images/bundle/bundle.png new file mode 100644 index 0000000..829f1f9 Binary files /dev/null and b/data/images/bundle/bundle.png differ diff --git a/data/images/button/Abort.png b/data/images/button/Abort.png new file mode 100644 index 0000000..d76f030 Binary files /dev/null and b/data/images/button/Abort.png differ diff --git a/data/images/button/About.png b/data/images/button/About.png new file mode 100644 index 0000000..04e71de Binary files /dev/null and b/data/images/button/About.png differ diff --git a/data/images/button/AddKey.png b/data/images/button/AddKey.png new file mode 100644 index 0000000..fa0510a Binary files /dev/null and b/data/images/button/AddKey.png differ diff --git a/data/images/button/AddLocale.png b/data/images/button/AddLocale.png new file mode 100644 index 0000000..71fb5e4 Binary files /dev/null and b/data/images/button/AddLocale.png differ diff --git a/data/images/button/Bad.png b/data/images/button/Bad.png new file mode 100644 index 0000000..5196d43 Binary files /dev/null and b/data/images/button/Bad.png differ diff --git a/data/images/button/Begin.png b/data/images/button/Begin.png new file mode 100644 index 0000000..5f06866 Binary files /dev/null and b/data/images/button/Begin.png differ diff --git a/data/images/button/Black.png b/data/images/button/Black.png new file mode 100644 index 0000000..b0974cb Binary files /dev/null and b/data/images/button/Black.png differ diff --git a/data/images/button/BugReport.png b/data/images/button/BugReport.png new file mode 100644 index 0000000..44c7ae1 Binary files /dev/null and b/data/images/button/BugReport.png differ diff --git a/data/images/button/Clear.png b/data/images/button/Clear.png new file mode 100644 index 0000000..19a1665 Binary files /dev/null and b/data/images/button/Clear.png differ diff --git a/data/images/button/Clock.png b/data/images/button/Clock.png new file mode 100644 index 0000000..28de8fe Binary files /dev/null and b/data/images/button/Clock.png differ diff --git a/data/images/button/Computer.png b/data/images/button/Computer.png new file mode 100644 index 0000000..04c4598 Binary files /dev/null and b/data/images/button/Computer.png differ diff --git a/data/images/button/ComputerOff.png b/data/images/button/ComputerOff.png new file mode 100644 index 0000000..d333330 Binary files /dev/null and b/data/images/button/ComputerOff.png differ diff --git a/data/images/button/ComputerOn.png b/data/images/button/ComputerOn.png new file mode 100644 index 0000000..f69e182 Binary files /dev/null and b/data/images/button/ComputerOn.png differ diff --git a/data/images/button/Connect.png b/data/images/button/Connect.png new file mode 100644 index 0000000..4f031b7 Binary files /dev/null and b/data/images/button/Connect.png differ diff --git a/data/images/button/Cut.png b/data/images/button/Cut.png new file mode 100644 index 0000000..f702874 Binary files /dev/null and b/data/images/button/Cut.png differ diff --git a/data/images/button/Details.png b/data/images/button/Details.png new file mode 100644 index 0000000..04e71de Binary files /dev/null and b/data/images/button/Details.png differ diff --git a/data/images/button/Disconnect.png b/data/images/button/Disconnect.png new file mode 100644 index 0000000..bc9a1b7 Binary files /dev/null and b/data/images/button/Disconnect.png differ diff --git a/data/images/button/Down.png b/data/images/button/Down.png new file mode 100644 index 0000000..bf8cc2a Binary files /dev/null and b/data/images/button/Down.png differ diff --git a/data/images/button/DownloadOff.png b/data/images/button/DownloadOff.png new file mode 100644 index 0000000..b70fa32 Binary files /dev/null and b/data/images/button/DownloadOff.png differ diff --git a/data/images/button/DownloadOn.png b/data/images/button/DownloadOn.png new file mode 100644 index 0000000..b0aab71 Binary files /dev/null and b/data/images/button/DownloadOn.png differ diff --git a/data/images/button/DrawAgreement.png b/data/images/button/DrawAgreement.png new file mode 100644 index 0000000..9a6f857 Binary files /dev/null and b/data/images/button/DrawAgreement.png differ diff --git a/data/images/button/DrawReject.png b/data/images/button/DrawReject.png new file mode 100644 index 0000000..41c1663 Binary files /dev/null and b/data/images/button/DrawReject.png differ diff --git a/data/images/button/EditGroup.png b/data/images/button/EditGroup.png new file mode 100644 index 0000000..2015688 Binary files /dev/null and b/data/images/button/EditGroup.png differ diff --git a/data/images/button/EditLogin.png b/data/images/button/EditLogin.png new file mode 100644 index 0000000..1188fca Binary files /dev/null and b/data/images/button/EditLogin.png differ diff --git a/data/images/button/Empty.png b/data/images/button/Empty.png new file mode 100644 index 0000000..36ce140 Binary files /dev/null and b/data/images/button/Empty.png differ diff --git a/data/images/button/End.png b/data/images/button/End.png new file mode 100644 index 0000000..628c7ee Binary files /dev/null and b/data/images/button/End.png differ diff --git a/data/images/button/Find.png b/data/images/button/Find.png new file mode 100644 index 0000000..bc123a8 Binary files /dev/null and b/data/images/button/Find.png differ diff --git a/data/images/button/ForcePack.png b/data/images/button/ForcePack.png new file mode 100644 index 0000000..6dea0c2 Binary files /dev/null and b/data/images/button/ForcePack.png differ diff --git a/data/images/button/Good.png b/data/images/button/Good.png new file mode 100644 index 0000000..790f5e1 Binary files /dev/null and b/data/images/button/Good.png differ diff --git a/data/images/button/Help.png b/data/images/button/Help.png new file mode 100644 index 0000000..8542495 Binary files /dev/null and b/data/images/button/Help.png differ diff --git a/data/images/button/Human.png b/data/images/button/Human.png new file mode 100644 index 0000000..b50ec42 Binary files /dev/null and b/data/images/button/Human.png differ diff --git a/data/images/button/HumanOff.png b/data/images/button/HumanOff.png new file mode 100644 index 0000000..b62655e Binary files /dev/null and b/data/images/button/HumanOff.png differ diff --git a/data/images/button/HumanOn.png b/data/images/button/HumanOn.png new file mode 100644 index 0000000..43887aa Binary files /dev/null and b/data/images/button/HumanOn.png differ diff --git a/data/images/button/Info.png b/data/images/button/Info.png new file mode 100644 index 0000000..8eeebdd Binary files /dev/null and b/data/images/button/Info.png differ diff --git a/data/images/button/JConsole.png b/data/images/button/JConsole.png new file mode 100644 index 0000000..5eac167 Binary files /dev/null and b/data/images/button/JConsole.png differ diff --git a/data/images/button/JConsoleOff.png b/data/images/button/JConsoleOff.png new file mode 100644 index 0000000..5eac167 Binary files /dev/null and b/data/images/button/JConsoleOff.png differ diff --git a/data/images/button/JConsoleOn.png b/data/images/button/JConsoleOn.png new file mode 100644 index 0000000..5eac167 Binary files /dev/null and b/data/images/button/JConsoleOn.png differ diff --git a/data/images/button/JoinGroup.png b/data/images/button/JoinGroup.png new file mode 100644 index 0000000..6e497e7 Binary files /dev/null and b/data/images/button/JoinGroup.png differ diff --git a/data/images/button/LeftGroup.png b/data/images/button/LeftGroup.png new file mode 100644 index 0000000..bfea85d Binary files /dev/null and b/data/images/button/LeftGroup.png differ diff --git a/data/images/button/Licence.png b/data/images/button/Licence.png new file mode 100644 index 0000000..6a3f5a7 Binary files /dev/null and b/data/images/button/Licence.png differ diff --git a/data/images/button/LinkType.png b/data/images/button/LinkType.png new file mode 100644 index 0000000..d376dfb Binary files /dev/null and b/data/images/button/LinkType.png differ diff --git a/data/images/button/LocalRemoveOff.png b/data/images/button/LocalRemoveOff.png new file mode 100644 index 0000000..3a5110c Binary files /dev/null and b/data/images/button/LocalRemoveOff.png differ diff --git a/data/images/button/LocalRemoveOn.png b/data/images/button/LocalRemoveOn.png new file mode 100644 index 0000000..19a1665 Binary files /dev/null and b/data/images/button/LocalRemoveOn.png differ diff --git a/data/images/button/Locale.png b/data/images/button/Locale.png new file mode 100644 index 0000000..77ea807 Binary files /dev/null and b/data/images/button/Locale.png differ diff --git a/data/images/button/Log.png b/data/images/button/Log.png new file mode 100644 index 0000000..38dbb26 Binary files /dev/null and b/data/images/button/Log.png differ diff --git a/data/images/button/Manual.png b/data/images/button/Manual.png new file mode 100644 index 0000000..88e438e Binary files /dev/null and b/data/images/button/Manual.png differ diff --git a/data/images/button/Merge.png b/data/images/button/Merge.png new file mode 100644 index 0000000..2771bbe Binary files /dev/null and b/data/images/button/Merge.png differ diff --git a/data/images/button/Network.png b/data/images/button/Network.png new file mode 100644 index 0000000..8d9a0e7 Binary files /dev/null and b/data/images/button/Network.png differ diff --git a/data/images/button/NetworkOff.png b/data/images/button/NetworkOff.png new file mode 100644 index 0000000..0110f89 Binary files /dev/null and b/data/images/button/NetworkOff.png differ diff --git a/data/images/button/NetworkOn.png b/data/images/button/NetworkOn.png new file mode 100644 index 0000000..36fff6d Binary files /dev/null and b/data/images/button/NetworkOn.png differ diff --git a/data/images/button/Next.png b/data/images/button/Next.png new file mode 100644 index 0000000..0011e67 Binary files /dev/null and b/data/images/button/Next.png differ diff --git a/data/images/button/Online.png b/data/images/button/Online.png new file mode 100644 index 0000000..f30f95a Binary files /dev/null and b/data/images/button/Online.png differ diff --git a/data/images/button/Open.png b/data/images/button/Open.png new file mode 100644 index 0000000..c3bcfcd Binary files /dev/null and b/data/images/button/Open.png differ diff --git a/data/images/button/PackBug.png b/data/images/button/PackBug.png new file mode 100644 index 0000000..8933c01 Binary files /dev/null and b/data/images/button/PackBug.png differ diff --git a/data/images/button/PackBugOff.png b/data/images/button/PackBugOff.png new file mode 100644 index 0000000..1de84d5 Binary files /dev/null and b/data/images/button/PackBugOff.png differ diff --git a/data/images/button/PackBugOn.png b/data/images/button/PackBugOn.png new file mode 100644 index 0000000..8933c01 Binary files /dev/null and b/data/images/button/PackBugOn.png differ diff --git a/data/images/button/Players.png b/data/images/button/Players.png new file mode 100644 index 0000000..42acdfc Binary files /dev/null and b/data/images/button/Players.png differ diff --git a/data/images/button/Previous.png b/data/images/button/Previous.png new file mode 100644 index 0000000..d3bc514 Binary files /dev/null and b/data/images/button/Previous.png differ diff --git a/data/images/button/Pseudo.png b/data/images/button/Pseudo.png new file mode 100644 index 0000000..38dbb26 Binary files /dev/null and b/data/images/button/Pseudo.png differ diff --git a/data/images/button/Quit.png b/data/images/button/Quit.png new file mode 100644 index 0000000..22a65a4 Binary files /dev/null and b/data/images/button/Quit.png differ diff --git a/data/images/button/Redo.png b/data/images/button/Redo.png new file mode 100644 index 0000000..c03bba0 Binary files /dev/null and b/data/images/button/Redo.png differ diff --git a/data/images/button/RemoteRemoveOff.png b/data/images/button/RemoteRemoveOff.png new file mode 100644 index 0000000..3a5110c Binary files /dev/null and b/data/images/button/RemoteRemoveOff.png differ diff --git a/data/images/button/RemoteRemoveOn.png b/data/images/button/RemoteRemoveOn.png new file mode 100644 index 0000000..19a1665 Binary files /dev/null and b/data/images/button/RemoteRemoveOn.png differ diff --git a/data/images/button/RemoveKey.png b/data/images/button/RemoveKey.png new file mode 100644 index 0000000..ee80592 Binary files /dev/null and b/data/images/button/RemoveKey.png differ diff --git a/data/images/button/RemoveLocale.png b/data/images/button/RemoveLocale.png new file mode 100644 index 0000000..60575e9 Binary files /dev/null and b/data/images/button/RemoveLocale.png differ diff --git a/data/images/button/RenameLocale.png b/data/images/button/RenameLocale.png new file mode 100644 index 0000000..7f6fcf1 Binary files /dev/null and b/data/images/button/RenameLocale.png differ diff --git a/data/images/button/Resign.png b/data/images/button/Resign.png new file mode 100644 index 0000000..322f1b4 Binary files /dev/null and b/data/images/button/Resign.png differ diff --git a/data/images/button/Save.png b/data/images/button/Save.png new file mode 100644 index 0000000..7da1d7c Binary files /dev/null and b/data/images/button/Save.png differ diff --git a/data/images/button/SaveAs.png b/data/images/button/SaveAs.png new file mode 100644 index 0000000..a838052 Binary files /dev/null and b/data/images/button/SaveAs.png differ diff --git a/data/images/button/SetProxy.png b/data/images/button/SetProxy.png new file mode 100644 index 0000000..111c493 Binary files /dev/null and b/data/images/button/SetProxy.png differ diff --git a/data/images/button/Stop.png b/data/images/button/Stop.png new file mode 100644 index 0000000..217e33b Binary files /dev/null and b/data/images/button/Stop.png differ diff --git a/data/images/button/ToolBarProfil.png b/data/images/button/ToolBarProfil.png new file mode 100644 index 0000000..bd731b3 Binary files /dev/null and b/data/images/button/ToolBarProfil.png differ diff --git a/data/images/button/Undo.png b/data/images/button/Undo.png new file mode 100644 index 0000000..f123942 Binary files /dev/null and b/data/images/button/Undo.png differ diff --git a/data/images/button/Unlog.png b/data/images/button/Unlog.png new file mode 100644 index 0000000..7a2f365 Binary files /dev/null and b/data/images/button/Unlog.png differ diff --git a/data/images/button/Up.png b/data/images/button/Up.png new file mode 100644 index 0000000..afca099 Binary files /dev/null and b/data/images/button/Up.png differ diff --git a/data/images/button/Update.png b/data/images/button/Update.png new file mode 100644 index 0000000..67f739e Binary files /dev/null and b/data/images/button/Update.png differ diff --git a/data/images/button/UploadOff.png b/data/images/button/UploadOff.png new file mode 100644 index 0000000..7816e7b Binary files /dev/null and b/data/images/button/UploadOff.png differ diff --git a/data/images/button/UploadOn.png b/data/images/button/UploadOn.png new file mode 100644 index 0000000..f30f95a Binary files /dev/null and b/data/images/button/UploadOn.png differ diff --git a/data/images/button/Users.png b/data/images/button/Users.png new file mode 100644 index 0000000..010a249 Binary files /dev/null and b/data/images/button/Users.png differ diff --git a/data/images/button/Warning.png b/data/images/button/Warning.png new file mode 100644 index 0000000..4a5c3df Binary files /dev/null and b/data/images/button/Warning.png differ diff --git a/data/images/button/White.png b/data/images/button/White.png new file mode 100644 index 0000000..310a75b Binary files /dev/null and b/data/images/button/White.png differ diff --git a/data/images/button/develop.png b/data/images/button/develop.png new file mode 100644 index 0000000..a437e0a Binary files /dev/null and b/data/images/button/develop.png differ diff --git a/data/images/chat/chat.png b/data/images/chat/chat.png new file mode 100644 index 0000000..ed2d33b Binary files /dev/null and b/data/images/chat/chat.png differ diff --git a/data/images/flags/ad.png b/data/images/flags/ad.png new file mode 100644 index 0000000..5d17202 Binary files /dev/null and b/data/images/flags/ad.png differ diff --git a/data/images/flags/ae.png b/data/images/flags/ae.png new file mode 100644 index 0000000..007dc36 Binary files /dev/null and b/data/images/flags/ae.png differ diff --git a/data/images/flags/af.png b/data/images/flags/af.png new file mode 100644 index 0000000..9f9cd77 Binary files /dev/null and b/data/images/flags/af.png differ diff --git a/data/images/flags/ag.png b/data/images/flags/ag.png new file mode 100644 index 0000000..de7b76c Binary files /dev/null and b/data/images/flags/ag.png differ diff --git a/data/images/flags/ai.png b/data/images/flags/ai.png new file mode 100644 index 0000000..bbbd7b1 Binary files /dev/null and b/data/images/flags/ai.png differ diff --git a/data/images/flags/al.png b/data/images/flags/al.png new file mode 100644 index 0000000..c5d218c Binary files /dev/null and b/data/images/flags/al.png differ diff --git a/data/images/flags/am.png b/data/images/flags/am.png new file mode 100644 index 0000000..5154516 Binary files /dev/null and b/data/images/flags/am.png differ diff --git a/data/images/flags/an.png b/data/images/flags/an.png new file mode 100644 index 0000000..28d1f98 Binary files /dev/null and b/data/images/flags/an.png differ diff --git a/data/images/flags/ao.png b/data/images/flags/ao.png new file mode 100644 index 0000000..9dc5ab7 Binary files /dev/null and b/data/images/flags/ao.png differ diff --git a/data/images/flags/aq.png b/data/images/flags/aq.png new file mode 100644 index 0000000..7caeae0 Binary files /dev/null and b/data/images/flags/aq.png differ diff --git a/data/images/flags/ar.png b/data/images/flags/ar.png new file mode 100644 index 0000000..dcabc83 Binary files /dev/null and b/data/images/flags/ar.png differ diff --git a/data/images/flags/as.png b/data/images/flags/as.png new file mode 100644 index 0000000..d198709 Binary files /dev/null and b/data/images/flags/as.png differ diff --git a/data/images/flags/at.png b/data/images/flags/at.png new file mode 100644 index 0000000..fadb6a2 Binary files /dev/null and b/data/images/flags/at.png differ diff --git a/data/images/flags/au.png b/data/images/flags/au.png new file mode 100644 index 0000000..c8b5599 Binary files /dev/null and b/data/images/flags/au.png differ diff --git a/data/images/flags/aw.png b/data/images/flags/aw.png new file mode 100644 index 0000000..c66e1f1 Binary files /dev/null and b/data/images/flags/aw.png differ diff --git a/data/images/flags/ax.png b/data/images/flags/ax.png new file mode 100644 index 0000000..917ff47 Binary files /dev/null and b/data/images/flags/ax.png differ diff --git a/data/images/flags/az.png b/data/images/flags/az.png new file mode 100644 index 0000000..381598a Binary files /dev/null and b/data/images/flags/az.png differ diff --git a/data/images/flags/ba.png b/data/images/flags/ba.png new file mode 100644 index 0000000..6eff4f0 Binary files /dev/null and b/data/images/flags/ba.png differ diff --git a/data/images/flags/bb.png b/data/images/flags/bb.png new file mode 100644 index 0000000..18a1e0d Binary files /dev/null and b/data/images/flags/bb.png differ diff --git a/data/images/flags/bd.png b/data/images/flags/bd.png new file mode 100644 index 0000000..00770ad Binary files /dev/null and b/data/images/flags/bd.png differ diff --git a/data/images/flags/be.png b/data/images/flags/be.png new file mode 100644 index 0000000..b09b3d6 Binary files /dev/null and b/data/images/flags/be.png differ diff --git a/data/images/flags/bf.png b/data/images/flags/bf.png new file mode 100644 index 0000000..fc47278 Binary files /dev/null and b/data/images/flags/bf.png differ diff --git a/data/images/flags/bg.png b/data/images/flags/bg.png new file mode 100644 index 0000000..d92441b Binary files /dev/null and b/data/images/flags/bg.png differ diff --git a/data/images/flags/bh.png b/data/images/flags/bh.png new file mode 100644 index 0000000..4f6d433 Binary files /dev/null and b/data/images/flags/bh.png differ diff --git a/data/images/flags/bi.png b/data/images/flags/bi.png new file mode 100644 index 0000000..3832f22 Binary files /dev/null and b/data/images/flags/bi.png differ diff --git a/data/images/flags/bj.png b/data/images/flags/bj.png new file mode 100644 index 0000000..dd3c688 Binary files /dev/null and b/data/images/flags/bj.png differ diff --git a/data/images/flags/bl.png b/data/images/flags/bl.png new file mode 100644 index 0000000..692f8de Binary files /dev/null and b/data/images/flags/bl.png differ diff --git a/data/images/flags/bm.png b/data/images/flags/bm.png new file mode 100644 index 0000000..b53c416 Binary files /dev/null and b/data/images/flags/bm.png differ diff --git a/data/images/flags/bn.png b/data/images/flags/bn.png new file mode 100644 index 0000000..d8a6875 Binary files /dev/null and b/data/images/flags/bn.png differ diff --git a/data/images/flags/bo.png b/data/images/flags/bo.png new file mode 100644 index 0000000..c8686b5 Binary files /dev/null and b/data/images/flags/bo.png differ diff --git a/data/images/flags/br.png b/data/images/flags/br.png new file mode 100644 index 0000000..9a9d5e6 Binary files /dev/null and b/data/images/flags/br.png differ diff --git a/data/images/flags/br2.png b/data/images/flags/br2.png new file mode 100644 index 0000000..22a9868 Binary files /dev/null and b/data/images/flags/br2.png differ diff --git a/data/images/flags/bs.png b/data/images/flags/bs.png new file mode 100644 index 0000000..dce7bcf Binary files /dev/null and b/data/images/flags/bs.png differ diff --git a/data/images/flags/bt.png b/data/images/flags/bt.png new file mode 100644 index 0000000..b87008a Binary files /dev/null and b/data/images/flags/bt.png differ diff --git a/data/images/flags/bv.png b/data/images/flags/bv.png new file mode 100644 index 0000000..3f24fb5 Binary files /dev/null and b/data/images/flags/bv.png differ diff --git a/data/images/flags/bw.png b/data/images/flags/bw.png new file mode 100644 index 0000000..46a79b4 Binary files /dev/null and b/data/images/flags/bw.png differ diff --git a/data/images/flags/by.png b/data/images/flags/by.png new file mode 100644 index 0000000..030b0f6 Binary files /dev/null and b/data/images/flags/by.png differ diff --git a/data/images/flags/bz.png b/data/images/flags/bz.png new file mode 100644 index 0000000..7ee942e Binary files /dev/null and b/data/images/flags/bz.png differ diff --git a/data/images/flags/ca.png b/data/images/flags/ca.png new file mode 100644 index 0000000..d08962d Binary files /dev/null and b/data/images/flags/ca.png differ diff --git a/data/images/flags/cc.png b/data/images/flags/cc.png new file mode 100644 index 0000000..e61fa76 Binary files /dev/null and b/data/images/flags/cc.png differ diff --git a/data/images/flags/cd.png b/data/images/flags/cd.png new file mode 100644 index 0000000..d39385d Binary files /dev/null and b/data/images/flags/cd.png differ diff --git a/data/images/flags/cf.png b/data/images/flags/cf.png new file mode 100644 index 0000000..f136158 Binary files /dev/null and b/data/images/flags/cf.png differ diff --git a/data/images/flags/cg.png b/data/images/flags/cg.png new file mode 100644 index 0000000..2ec7d0e Binary files /dev/null and b/data/images/flags/cg.png differ diff --git a/data/images/flags/ch.png b/data/images/flags/ch.png new file mode 100644 index 0000000..e8b1cfc Binary files /dev/null and b/data/images/flags/ch.png differ diff --git a/data/images/flags/ci.png b/data/images/flags/ci.png new file mode 100644 index 0000000..c7644cb Binary files /dev/null and b/data/images/flags/ci.png differ diff --git a/data/images/flags/ck.png b/data/images/flags/ck.png new file mode 100644 index 0000000..a896cc2 Binary files /dev/null and b/data/images/flags/ck.png differ diff --git a/data/images/flags/cl.png b/data/images/flags/cl.png new file mode 100644 index 0000000..a113a2b Binary files /dev/null and b/data/images/flags/cl.png differ diff --git a/data/images/flags/cm.png b/data/images/flags/cm.png new file mode 100644 index 0000000..a5d828f Binary files /dev/null and b/data/images/flags/cm.png differ diff --git a/data/images/flags/cn.png b/data/images/flags/cn.png new file mode 100644 index 0000000..2ad03f6 Binary files /dev/null and b/data/images/flags/cn.png differ diff --git a/data/images/flags/co.png b/data/images/flags/co.png new file mode 100644 index 0000000..362f006 Binary files /dev/null and b/data/images/flags/co.png differ diff --git a/data/images/flags/cr.png b/data/images/flags/cr.png new file mode 100644 index 0000000..a184a8d Binary files /dev/null and b/data/images/flags/cr.png differ diff --git a/data/images/flags/cs.png b/data/images/flags/cs.png new file mode 100644 index 0000000..7a89a78 Binary files /dev/null and b/data/images/flags/cs.png differ diff --git a/data/images/flags/cu.png b/data/images/flags/cu.png new file mode 100644 index 0000000..19c6f80 Binary files /dev/null and b/data/images/flags/cu.png differ diff --git a/data/images/flags/cv.png b/data/images/flags/cv.png new file mode 100644 index 0000000..4f6bf24 Binary files /dev/null and b/data/images/flags/cv.png differ diff --git a/data/images/flags/cx.png b/data/images/flags/cx.png new file mode 100644 index 0000000..5c858ca Binary files /dev/null and b/data/images/flags/cx.png differ diff --git a/data/images/flags/cy.png b/data/images/flags/cy.png new file mode 100644 index 0000000..97ed7e8 Binary files /dev/null and b/data/images/flags/cy.png differ diff --git a/data/images/flags/cz.png b/data/images/flags/cz.png new file mode 100644 index 0000000..97ed7e8 Binary files /dev/null and b/data/images/flags/cz.png differ diff --git a/data/images/flags/de.png b/data/images/flags/de.png new file mode 100644 index 0000000..24b07b5 Binary files /dev/null and b/data/images/flags/de.png differ diff --git a/data/images/flags/dj.png b/data/images/flags/dj.png new file mode 100644 index 0000000..791c251 Binary files /dev/null and b/data/images/flags/dj.png differ diff --git a/data/images/flags/dk.png b/data/images/flags/dk.png new file mode 100644 index 0000000..2be6ab1 Binary files /dev/null and b/data/images/flags/dk.png differ diff --git a/data/images/flags/dm.png b/data/images/flags/dm.png new file mode 100644 index 0000000..edd7fff Binary files /dev/null and b/data/images/flags/dm.png differ diff --git a/data/images/flags/do.png b/data/images/flags/do.png new file mode 100644 index 0000000..6222762 Binary files /dev/null and b/data/images/flags/do.png differ diff --git a/data/images/flags/dz.png b/data/images/flags/dz.png new file mode 100644 index 0000000..743cb1f Binary files /dev/null and b/data/images/flags/dz.png differ diff --git a/data/images/flags/ec.png b/data/images/flags/ec.png new file mode 100644 index 0000000..cb87ce8 Binary files /dev/null and b/data/images/flags/ec.png differ diff --git a/data/images/flags/ee.png b/data/images/flags/ee.png new file mode 100644 index 0000000..94e5106 Binary files /dev/null and b/data/images/flags/ee.png differ diff --git a/data/images/flags/eg.png b/data/images/flags/eg.png new file mode 100644 index 0000000..dba11a2 Binary files /dev/null and b/data/images/flags/eg.png differ diff --git a/data/images/flags/eh.png b/data/images/flags/eh.png new file mode 100644 index 0000000..9fb7b1d Binary files /dev/null and b/data/images/flags/eh.png differ diff --git a/data/images/flags/er.png b/data/images/flags/er.png new file mode 100644 index 0000000..43225fa Binary files /dev/null and b/data/images/flags/er.png differ diff --git a/data/images/flags/es.png b/data/images/flags/es.png new file mode 100644 index 0000000..4b30c49 Binary files /dev/null and b/data/images/flags/es.png differ diff --git a/data/images/flags/et.png b/data/images/flags/et.png new file mode 100644 index 0000000..7275abf Binary files /dev/null and b/data/images/flags/et.png differ diff --git a/data/images/flags/fi.png b/data/images/flags/fi.png new file mode 100644 index 0000000..8574dec Binary files /dev/null and b/data/images/flags/fi.png differ diff --git a/data/images/flags/fj.png b/data/images/flags/fj.png new file mode 100644 index 0000000..dd021d5 Binary files /dev/null and b/data/images/flags/fj.png differ diff --git a/data/images/flags/fk.png b/data/images/flags/fk.png new file mode 100644 index 0000000..7c277bc Binary files /dev/null and b/data/images/flags/fk.png differ diff --git a/data/images/flags/fm.png b/data/images/flags/fm.png new file mode 100644 index 0000000..6c4e137 Binary files /dev/null and b/data/images/flags/fm.png differ diff --git a/data/images/flags/fo.png b/data/images/flags/fo.png new file mode 100644 index 0000000..8eeb7cc Binary files /dev/null and b/data/images/flags/fo.png differ diff --git a/data/images/flags/fr.png b/data/images/flags/fr.png new file mode 100644 index 0000000..798f5ec Binary files /dev/null and b/data/images/flags/fr.png differ diff --git a/data/images/flags/fx.png b/data/images/flags/fx.png new file mode 100644 index 0000000..c1a5395 Binary files /dev/null and b/data/images/flags/fx.png differ diff --git a/data/images/flags/ga.png b/data/images/flags/ga.png new file mode 100644 index 0000000..9c72bec Binary files /dev/null and b/data/images/flags/ga.png differ diff --git a/data/images/flags/gb.png b/data/images/flags/gb.png new file mode 100644 index 0000000..8558aa6 Binary files /dev/null and b/data/images/flags/gb.png differ diff --git a/data/images/flags/gd.png b/data/images/flags/gd.png new file mode 100644 index 0000000..7535381 Binary files /dev/null and b/data/images/flags/gd.png differ diff --git a/data/images/flags/ge.png b/data/images/flags/ge.png new file mode 100644 index 0000000..352d0f3 Binary files /dev/null and b/data/images/flags/ge.png differ diff --git a/data/images/flags/gf.png b/data/images/flags/gf.png new file mode 100644 index 0000000..798f5ec Binary files /dev/null and b/data/images/flags/gf.png differ diff --git a/data/images/flags/gg.png b/data/images/flags/gg.png new file mode 100644 index 0000000..cef5a7e Binary files /dev/null and b/data/images/flags/gg.png differ diff --git a/data/images/flags/gh.png b/data/images/flags/gh.png new file mode 100644 index 0000000..f9aa766 Binary files /dev/null and b/data/images/flags/gh.png differ diff --git a/data/images/flags/gi.png b/data/images/flags/gi.png new file mode 100644 index 0000000..5de3e79 Binary files /dev/null and b/data/images/flags/gi.png differ diff --git a/data/images/flags/gl.png b/data/images/flags/gl.png new file mode 100644 index 0000000..f10fc56 Binary files /dev/null and b/data/images/flags/gl.png differ diff --git a/data/images/flags/gm.png b/data/images/flags/gm.png new file mode 100644 index 0000000..c09dc79 Binary files /dev/null and b/data/images/flags/gm.png differ diff --git a/data/images/flags/gn.png b/data/images/flags/gn.png new file mode 100644 index 0000000..99748b5 Binary files /dev/null and b/data/images/flags/gn.png differ diff --git a/data/images/flags/gp.png b/data/images/flags/gp.png new file mode 100644 index 0000000..798f5ec Binary files /dev/null and b/data/images/flags/gp.png differ diff --git a/data/images/flags/gq.png b/data/images/flags/gq.png new file mode 100644 index 0000000..14c8b43 Binary files /dev/null and b/data/images/flags/gq.png differ diff --git a/data/images/flags/gr.png b/data/images/flags/gr.png new file mode 100644 index 0000000..35871f0 Binary files /dev/null and b/data/images/flags/gr.png differ diff --git a/data/images/flags/gs.png b/data/images/flags/gs.png new file mode 100644 index 0000000..700656b Binary files /dev/null and b/data/images/flags/gs.png differ diff --git a/data/images/flags/gt.png b/data/images/flags/gt.png new file mode 100644 index 0000000..e8cce58 Binary files /dev/null and b/data/images/flags/gt.png differ diff --git a/data/images/flags/gu.png b/data/images/flags/gu.png new file mode 100644 index 0000000..40ddc68 Binary files /dev/null and b/data/images/flags/gu.png differ diff --git a/data/images/flags/gw.png b/data/images/flags/gw.png new file mode 100644 index 0000000..ec220ce Binary files /dev/null and b/data/images/flags/gw.png differ diff --git a/data/images/flags/gy.png b/data/images/flags/gy.png new file mode 100644 index 0000000..2daa096 Binary files /dev/null and b/data/images/flags/gy.png differ diff --git a/data/images/flags/hk.png b/data/images/flags/hk.png new file mode 100644 index 0000000..a2eeca2 Binary files /dev/null and b/data/images/flags/hk.png differ diff --git a/data/images/flags/hm.png b/data/images/flags/hm.png new file mode 100644 index 0000000..c8b5599 Binary files /dev/null and b/data/images/flags/hm.png differ diff --git a/data/images/flags/hn.png b/data/images/flags/hn.png new file mode 100644 index 0000000..633abb1 Binary files /dev/null and b/data/images/flags/hn.png differ diff --git a/data/images/flags/hr.png b/data/images/flags/hr.png new file mode 100644 index 0000000..ba09f12 Binary files /dev/null and b/data/images/flags/hr.png differ diff --git a/data/images/flags/ht.png b/data/images/flags/ht.png new file mode 100644 index 0000000..cf97344 Binary files /dev/null and b/data/images/flags/ht.png differ diff --git a/data/images/flags/hu.png b/data/images/flags/hu.png new file mode 100644 index 0000000..3345bde Binary files /dev/null and b/data/images/flags/hu.png differ diff --git a/data/images/flags/id.png b/data/images/flags/id.png new file mode 100644 index 0000000..6aa9fde Binary files /dev/null and b/data/images/flags/id.png differ diff --git a/data/images/flags/ie.png b/data/images/flags/ie.png new file mode 100644 index 0000000..fe977c3 Binary files /dev/null and b/data/images/flags/ie.png differ diff --git a/data/images/flags/il.png b/data/images/flags/il.png new file mode 100644 index 0000000..bc8523e Binary files /dev/null and b/data/images/flags/il.png differ diff --git a/data/images/flags/in.png b/data/images/flags/in.png new file mode 100644 index 0000000..b7c5495 Binary files /dev/null and b/data/images/flags/in.png differ diff --git a/data/images/flags/io.png b/data/images/flags/io.png new file mode 100644 index 0000000..fd5fb1c Binary files /dev/null and b/data/images/flags/io.png differ diff --git a/data/images/flags/iq.png b/data/images/flags/iq.png new file mode 100644 index 0000000..3721fd3 Binary files /dev/null and b/data/images/flags/iq.png differ diff --git a/data/images/flags/ir.png b/data/images/flags/ir.png new file mode 100644 index 0000000..4d7cb1f Binary files /dev/null and b/data/images/flags/ir.png differ diff --git a/data/images/flags/is.png b/data/images/flags/is.png new file mode 100644 index 0000000..aa90114 Binary files /dev/null and b/data/images/flags/is.png differ diff --git a/data/images/flags/it.png b/data/images/flags/it.png new file mode 100644 index 0000000..5212ade Binary files /dev/null and b/data/images/flags/it.png differ diff --git a/data/images/flags/je.png b/data/images/flags/je.png new file mode 100644 index 0000000..5930485 Binary files /dev/null and b/data/images/flags/je.png differ diff --git a/data/images/flags/jm.png b/data/images/flags/jm.png new file mode 100644 index 0000000..ccedfcd Binary files /dev/null and b/data/images/flags/jm.png differ diff --git a/data/images/flags/jo.png b/data/images/flags/jo.png new file mode 100644 index 0000000..f433260 Binary files /dev/null and b/data/images/flags/jo.png differ diff --git a/data/images/flags/jp.png b/data/images/flags/jp.png new file mode 100644 index 0000000..fb4f757 Binary files /dev/null and b/data/images/flags/jp.png differ diff --git a/data/images/flags/ke.png b/data/images/flags/ke.png new file mode 100644 index 0000000..3538e8d Binary files /dev/null and b/data/images/flags/ke.png differ diff --git a/data/images/flags/kg.png b/data/images/flags/kg.png new file mode 100644 index 0000000..889b612 Binary files /dev/null and b/data/images/flags/kg.png differ diff --git a/data/images/flags/kh.png b/data/images/flags/kh.png new file mode 100644 index 0000000..772f587 Binary files /dev/null and b/data/images/flags/kh.png differ diff --git a/data/images/flags/ki.png b/data/images/flags/ki.png new file mode 100644 index 0000000..4c8a67c Binary files /dev/null and b/data/images/flags/ki.png differ diff --git a/data/images/flags/km.png b/data/images/flags/km.png new file mode 100644 index 0000000..e704afb Binary files /dev/null and b/data/images/flags/km.png differ diff --git a/data/images/flags/kn.png b/data/images/flags/kn.png new file mode 100644 index 0000000..042232c Binary files /dev/null and b/data/images/flags/kn.png differ diff --git a/data/images/flags/kp.png b/data/images/flags/kp.png new file mode 100644 index 0000000..f8fbd30 Binary files /dev/null and b/data/images/flags/kp.png differ diff --git a/data/images/flags/kr.png b/data/images/flags/kr.png new file mode 100644 index 0000000..f83ebb3 Binary files /dev/null and b/data/images/flags/kr.png differ diff --git a/data/images/flags/kw.png b/data/images/flags/kw.png new file mode 100644 index 0000000..24748c6 Binary files /dev/null and b/data/images/flags/kw.png differ diff --git a/data/images/flags/ky.png b/data/images/flags/ky.png new file mode 100644 index 0000000..6973771 Binary files /dev/null and b/data/images/flags/ky.png differ diff --git a/data/images/flags/kz.png b/data/images/flags/kz.png new file mode 100644 index 0000000..3e102a4 Binary files /dev/null and b/data/images/flags/kz.png differ diff --git a/data/images/flags/la.png b/data/images/flags/la.png new file mode 100644 index 0000000..532e3b6 Binary files /dev/null and b/data/images/flags/la.png differ diff --git a/data/images/flags/lb.png b/data/images/flags/lb.png new file mode 100644 index 0000000..4f94e9f Binary files /dev/null and b/data/images/flags/lb.png differ diff --git a/data/images/flags/lc.png b/data/images/flags/lc.png new file mode 100644 index 0000000..a1205be Binary files /dev/null and b/data/images/flags/lc.png differ diff --git a/data/images/flags/li.png b/data/images/flags/li.png new file mode 100644 index 0000000..adef5a8 Binary files /dev/null and b/data/images/flags/li.png differ diff --git a/data/images/flags/lk.png b/data/images/flags/lk.png new file mode 100644 index 0000000..9ff43b5 Binary files /dev/null and b/data/images/flags/lk.png differ diff --git a/data/images/flags/lr.png b/data/images/flags/lr.png new file mode 100644 index 0000000..dc4c555 Binary files /dev/null and b/data/images/flags/lr.png differ diff --git a/data/images/flags/ls.png b/data/images/flags/ls.png new file mode 100644 index 0000000..fb9ec79 Binary files /dev/null and b/data/images/flags/ls.png differ diff --git a/data/images/flags/lt.png b/data/images/flags/lt.png new file mode 100644 index 0000000..e951f7d Binary files /dev/null and b/data/images/flags/lt.png differ diff --git a/data/images/flags/lu.png b/data/images/flags/lu.png new file mode 100644 index 0000000..4110349 Binary files /dev/null and b/data/images/flags/lu.png differ diff --git a/data/images/flags/lv.png b/data/images/flags/lv.png new file mode 100644 index 0000000..9921615 Binary files /dev/null and b/data/images/flags/lv.png differ diff --git a/data/images/flags/ly.png b/data/images/flags/ly.png new file mode 100644 index 0000000..55f5edd Binary files /dev/null and b/data/images/flags/ly.png differ diff --git a/data/images/flags/ma.png b/data/images/flags/ma.png new file mode 100644 index 0000000..f6f9d1f Binary files /dev/null and b/data/images/flags/ma.png differ diff --git a/data/images/flags/mc.png b/data/images/flags/mc.png new file mode 100644 index 0000000..05b3bb4 Binary files /dev/null and b/data/images/flags/mc.png differ diff --git a/data/images/flags/md.png b/data/images/flags/md.png new file mode 100644 index 0000000..81c36fa Binary files /dev/null and b/data/images/flags/md.png differ diff --git a/data/images/flags/me.png b/data/images/flags/me.png new file mode 100644 index 0000000..5f5199e Binary files /dev/null and b/data/images/flags/me.png differ diff --git a/data/images/flags/mf.png b/data/images/flags/mf.png new file mode 100644 index 0000000..798f5ec Binary files /dev/null and b/data/images/flags/mf.png differ diff --git a/data/images/flags/mg.png b/data/images/flags/mg.png new file mode 100644 index 0000000..ee2a024 Binary files /dev/null and b/data/images/flags/mg.png differ diff --git a/data/images/flags/mh.png b/data/images/flags/mh.png new file mode 100644 index 0000000..2a87b00 Binary files /dev/null and b/data/images/flags/mh.png differ diff --git a/data/images/flags/mk.png b/data/images/flags/mk.png new file mode 100644 index 0000000..618475d Binary files /dev/null and b/data/images/flags/mk.png differ diff --git a/data/images/flags/ml.png b/data/images/flags/ml.png new file mode 100644 index 0000000..b10bcac Binary files /dev/null and b/data/images/flags/ml.png differ diff --git a/data/images/flags/mm.png b/data/images/flags/mm.png new file mode 100644 index 0000000..f969995 Binary files /dev/null and b/data/images/flags/mm.png differ diff --git a/data/images/flags/mn.png b/data/images/flags/mn.png new file mode 100644 index 0000000..2a824d5 Binary files /dev/null and b/data/images/flags/mn.png differ diff --git a/data/images/flags/mo.png b/data/images/flags/mo.png new file mode 100644 index 0000000..06a1ead Binary files /dev/null and b/data/images/flags/mo.png differ diff --git a/data/images/flags/mp.png b/data/images/flags/mp.png new file mode 100644 index 0000000..52625e3 Binary files /dev/null and b/data/images/flags/mp.png differ diff --git a/data/images/flags/mq.png b/data/images/flags/mq.png new file mode 100644 index 0000000..798f5ec Binary files /dev/null and b/data/images/flags/mq.png differ diff --git a/data/images/flags/mr.png b/data/images/flags/mr.png new file mode 100644 index 0000000..52fcfc2 Binary files /dev/null and b/data/images/flags/mr.png differ diff --git a/data/images/flags/ms.png b/data/images/flags/ms.png new file mode 100644 index 0000000..022ca1a Binary files /dev/null and b/data/images/flags/ms.png differ diff --git a/data/images/flags/mt.png b/data/images/flags/mt.png new file mode 100644 index 0000000..ada70c6 Binary files /dev/null and b/data/images/flags/mt.png differ diff --git a/data/images/flags/mu.png b/data/images/flags/mu.png new file mode 100644 index 0000000..1126c7a Binary files /dev/null and b/data/images/flags/mu.png differ diff --git a/data/images/flags/mv.png b/data/images/flags/mv.png new file mode 100644 index 0000000..8f12c5b Binary files /dev/null and b/data/images/flags/mv.png differ diff --git a/data/images/flags/mw.png b/data/images/flags/mw.png new file mode 100644 index 0000000..1ff7495 Binary files /dev/null and b/data/images/flags/mw.png differ diff --git a/data/images/flags/mx.png b/data/images/flags/mx.png new file mode 100644 index 0000000..0d7c671 Binary files /dev/null and b/data/images/flags/mx.png differ diff --git a/data/images/flags/my.png b/data/images/flags/my.png new file mode 100644 index 0000000..4eead54 Binary files /dev/null and b/data/images/flags/my.png differ diff --git a/data/images/flags/mz.png b/data/images/flags/mz.png new file mode 100644 index 0000000..10b4b8d Binary files /dev/null and b/data/images/flags/mz.png differ diff --git a/data/images/flags/na.png b/data/images/flags/na.png new file mode 100644 index 0000000..db2580e Binary files /dev/null and b/data/images/flags/na.png differ diff --git a/data/images/flags/nc.png b/data/images/flags/nc.png new file mode 100644 index 0000000..c1a5395 Binary files /dev/null and b/data/images/flags/nc.png differ diff --git a/data/images/flags/ne.png b/data/images/flags/ne.png new file mode 100644 index 0000000..fd818af Binary files /dev/null and b/data/images/flags/ne.png differ diff --git a/data/images/flags/nf.png b/data/images/flags/nf.png new file mode 100644 index 0000000..46f61f0 Binary files /dev/null and b/data/images/flags/nf.png differ diff --git a/data/images/flags/ng.png b/data/images/flags/ng.png new file mode 100644 index 0000000..e61fcbb Binary files /dev/null and b/data/images/flags/ng.png differ diff --git a/data/images/flags/ni.png b/data/images/flags/ni.png new file mode 100644 index 0000000..50bebdf Binary files /dev/null and b/data/images/flags/ni.png differ diff --git a/data/images/flags/nl.png b/data/images/flags/nl.png new file mode 100644 index 0000000..08d2f2a Binary files /dev/null and b/data/images/flags/nl.png differ diff --git a/data/images/flags/no.png b/data/images/flags/no.png new file mode 100644 index 0000000..3f24fb5 Binary files /dev/null and b/data/images/flags/no.png differ diff --git a/data/images/flags/np.png b/data/images/flags/np.png new file mode 100644 index 0000000..f561629 Binary files /dev/null and b/data/images/flags/np.png differ diff --git a/data/images/flags/nr.png b/data/images/flags/nr.png new file mode 100644 index 0000000..15474be Binary files /dev/null and b/data/images/flags/nr.png differ diff --git a/data/images/flags/nu.png b/data/images/flags/nu.png new file mode 100644 index 0000000..fd9f9e0 Binary files /dev/null and b/data/images/flags/nu.png differ diff --git a/data/images/flags/nz.png b/data/images/flags/nz.png new file mode 100644 index 0000000..5bcbefd Binary files /dev/null and b/data/images/flags/nz.png differ diff --git a/data/images/flags/om.png b/data/images/flags/om.png new file mode 100644 index 0000000..01a3db1 Binary files /dev/null and b/data/images/flags/om.png differ diff --git a/data/images/flags/pa.png b/data/images/flags/pa.png new file mode 100644 index 0000000..632bd8e Binary files /dev/null and b/data/images/flags/pa.png differ diff --git a/data/images/flags/pe.png b/data/images/flags/pe.png new file mode 100644 index 0000000..c3b6831 Binary files /dev/null and b/data/images/flags/pe.png differ diff --git a/data/images/flags/pf.png b/data/images/flags/pf.png new file mode 100644 index 0000000..c9eaf6d Binary files /dev/null and b/data/images/flags/pf.png differ diff --git a/data/images/flags/pg.png b/data/images/flags/pg.png new file mode 100644 index 0000000..1aa244e Binary files /dev/null and b/data/images/flags/pg.png differ diff --git a/data/images/flags/ph.png b/data/images/flags/ph.png new file mode 100644 index 0000000..bac753b Binary files /dev/null and b/data/images/flags/ph.png differ diff --git a/data/images/flags/pk.png b/data/images/flags/pk.png new file mode 100644 index 0000000..c77f3fd Binary files /dev/null and b/data/images/flags/pk.png differ diff --git a/data/images/flags/pl.png b/data/images/flags/pl.png new file mode 100644 index 0000000..6c83c26 Binary files /dev/null and b/data/images/flags/pl.png differ diff --git a/data/images/flags/pm.png b/data/images/flags/pm.png new file mode 100644 index 0000000..798f5ec Binary files /dev/null and b/data/images/flags/pm.png differ diff --git a/data/images/flags/pn.png b/data/images/flags/pn.png new file mode 100644 index 0000000..55f7b91 Binary files /dev/null and b/data/images/flags/pn.png differ diff --git a/data/images/flags/pr.png b/data/images/flags/pr.png new file mode 100644 index 0000000..e12c958 Binary files /dev/null and b/data/images/flags/pr.png differ diff --git a/data/images/flags/ps.png b/data/images/flags/ps.png new file mode 100644 index 0000000..fe3a676 Binary files /dev/null and b/data/images/flags/ps.png differ diff --git a/data/images/flags/pt.png b/data/images/flags/pt.png new file mode 100644 index 0000000..0911015 Binary files /dev/null and b/data/images/flags/pt.png differ diff --git a/data/images/flags/pw.png b/data/images/flags/pw.png new file mode 100644 index 0000000..89e9572 Binary files /dev/null and b/data/images/flags/pw.png differ diff --git a/data/images/flags/py.png b/data/images/flags/py.png new file mode 100644 index 0000000..78e16f1 Binary files /dev/null and b/data/images/flags/py.png differ diff --git a/data/images/flags/qa.png b/data/images/flags/qa.png new file mode 100644 index 0000000..1e63f04 Binary files /dev/null and b/data/images/flags/qa.png differ diff --git a/data/images/flags/re.png b/data/images/flags/re.png new file mode 100644 index 0000000..c1a5395 Binary files /dev/null and b/data/images/flags/re.png differ diff --git a/data/images/flags/ro.png b/data/images/flags/ro.png new file mode 100644 index 0000000..4a4f7b5 Binary files /dev/null and b/data/images/flags/ro.png differ diff --git a/data/images/flags/rs.png b/data/images/flags/rs.png new file mode 100644 index 0000000..ae9e0e1 Binary files /dev/null and b/data/images/flags/rs.png differ diff --git a/data/images/flags/ru.png b/data/images/flags/ru.png new file mode 100644 index 0000000..e6f8bb0 Binary files /dev/null and b/data/images/flags/ru.png differ diff --git a/data/images/flags/rw.png b/data/images/flags/rw.png new file mode 100644 index 0000000..05543ea Binary files /dev/null and b/data/images/flags/rw.png differ diff --git a/data/images/flags/sa.png b/data/images/flags/sa.png new file mode 100644 index 0000000..a38fc79 Binary files /dev/null and b/data/images/flags/sa.png differ diff --git a/data/images/flags/sb.png b/data/images/flags/sb.png new file mode 100644 index 0000000..f274830 Binary files /dev/null and b/data/images/flags/sb.png differ diff --git a/data/images/flags/sc.png b/data/images/flags/sc.png new file mode 100644 index 0000000..369368b Binary files /dev/null and b/data/images/flags/sc.png differ diff --git a/data/images/flags/sd.png b/data/images/flags/sd.png new file mode 100644 index 0000000..54c57bf Binary files /dev/null and b/data/images/flags/sd.png differ diff --git a/data/images/flags/se.png b/data/images/flags/se.png new file mode 100644 index 0000000..0b7156f Binary files /dev/null and b/data/images/flags/se.png differ diff --git a/data/images/flags/sg.png b/data/images/flags/sg.png new file mode 100644 index 0000000..5164779 Binary files /dev/null and b/data/images/flags/sg.png differ diff --git a/data/images/flags/sh.png b/data/images/flags/sh.png new file mode 100644 index 0000000..243cae2 Binary files /dev/null and b/data/images/flags/sh.png differ diff --git a/data/images/flags/si.png b/data/images/flags/si.png new file mode 100644 index 0000000..5c8e9a9 Binary files /dev/null and b/data/images/flags/si.png differ diff --git a/data/images/flags/sj.png b/data/images/flags/sj.png new file mode 100644 index 0000000..3f24fb5 Binary files /dev/null and b/data/images/flags/sj.png differ diff --git a/data/images/flags/sk.png b/data/images/flags/sk.png new file mode 100644 index 0000000..ee88a07 Binary files /dev/null and b/data/images/flags/sk.png differ diff --git a/data/images/flags/sl.png b/data/images/flags/sl.png new file mode 100644 index 0000000..608e0fe Binary files /dev/null and b/data/images/flags/sl.png differ diff --git a/data/images/flags/sm.png b/data/images/flags/sm.png new file mode 100644 index 0000000..e38069d Binary files /dev/null and b/data/images/flags/sm.png differ diff --git a/data/images/flags/sn.png b/data/images/flags/sn.png new file mode 100644 index 0000000..4b99a66 Binary files /dev/null and b/data/images/flags/sn.png differ diff --git a/data/images/flags/so.png b/data/images/flags/so.png new file mode 100644 index 0000000..53db903 Binary files /dev/null and b/data/images/flags/so.png differ diff --git a/data/images/flags/sr.png b/data/images/flags/sr.png new file mode 100644 index 0000000..8643fae Binary files /dev/null and b/data/images/flags/sr.png differ diff --git a/data/images/flags/st.png b/data/images/flags/st.png new file mode 100644 index 0000000..2704149 Binary files /dev/null and b/data/images/flags/st.png differ diff --git a/data/images/flags/sv.png b/data/images/flags/sv.png new file mode 100644 index 0000000..f27b9be Binary files /dev/null and b/data/images/flags/sv.png differ diff --git a/data/images/flags/sy.png b/data/images/flags/sy.png new file mode 100644 index 0000000..8816583 Binary files /dev/null and b/data/images/flags/sy.png differ diff --git a/data/images/flags/sz.png b/data/images/flags/sz.png new file mode 100644 index 0000000..fc4bb02 Binary files /dev/null and b/data/images/flags/sz.png differ diff --git a/data/images/flags/tc.png b/data/images/flags/tc.png new file mode 100644 index 0000000..162ed8a Binary files /dev/null and b/data/images/flags/tc.png differ diff --git a/data/images/flags/td.png b/data/images/flags/td.png new file mode 100644 index 0000000..3801b2f Binary files /dev/null and b/data/images/flags/td.png differ diff --git a/data/images/flags/tf.png b/data/images/flags/tf.png new file mode 100644 index 0000000..c1a5395 Binary files /dev/null and b/data/images/flags/tf.png differ diff --git a/data/images/flags/tg.png b/data/images/flags/tg.png new file mode 100644 index 0000000..2f80fe0 Binary files /dev/null and b/data/images/flags/tg.png differ diff --git a/data/images/flags/th.png b/data/images/flags/th.png new file mode 100644 index 0000000..bbe1475 Binary files /dev/null and b/data/images/flags/th.png differ diff --git a/data/images/flags/tj.png b/data/images/flags/tj.png new file mode 100644 index 0000000..1f1b0da Binary files /dev/null and b/data/images/flags/tj.png differ diff --git a/data/images/flags/tk.png b/data/images/flags/tk.png new file mode 100644 index 0000000..643c127 Binary files /dev/null and b/data/images/flags/tk.png differ diff --git a/data/images/flags/tl.png b/data/images/flags/tl.png new file mode 100644 index 0000000..1babb2b Binary files /dev/null and b/data/images/flags/tl.png differ diff --git a/data/images/flags/tm.png b/data/images/flags/tm.png new file mode 100644 index 0000000..5b75e7a Binary files /dev/null and b/data/images/flags/tm.png differ diff --git a/data/images/flags/tn.png b/data/images/flags/tn.png new file mode 100644 index 0000000..9d7f7e3 Binary files /dev/null and b/data/images/flags/tn.png differ diff --git a/data/images/flags/to.png b/data/images/flags/to.png new file mode 100644 index 0000000..bc2d043 Binary files /dev/null and b/data/images/flags/to.png differ diff --git a/data/images/flags/tp.png b/data/images/flags/tp.png new file mode 100644 index 0000000..1babb2b Binary files /dev/null and b/data/images/flags/tp.png differ diff --git a/data/images/flags/tr.png b/data/images/flags/tr.png new file mode 100644 index 0000000..a7d1558 Binary files /dev/null and b/data/images/flags/tr.png differ diff --git a/data/images/flags/tt.png b/data/images/flags/tt.png new file mode 100644 index 0000000..4d18c03 Binary files /dev/null and b/data/images/flags/tt.png differ diff --git a/data/images/flags/tv.png b/data/images/flags/tv.png new file mode 100644 index 0000000..d423b04 Binary files /dev/null and b/data/images/flags/tv.png differ diff --git a/data/images/flags/tw.png b/data/images/flags/tw.png new file mode 100644 index 0000000..f233d0a Binary files /dev/null and b/data/images/flags/tw.png differ diff --git a/data/images/flags/tz.png b/data/images/flags/tz.png new file mode 100644 index 0000000..8816583 Binary files /dev/null and b/data/images/flags/tz.png differ diff --git a/data/images/flags/ua.png b/data/images/flags/ua.png new file mode 100644 index 0000000..1ff1a18 Binary files /dev/null and b/data/images/flags/ua.png differ diff --git a/data/images/flags/ug.png b/data/images/flags/ug.png new file mode 100644 index 0000000..69c83f8 Binary files /dev/null and b/data/images/flags/ug.png differ diff --git a/data/images/flags/um.png b/data/images/flags/um.png new file mode 100644 index 0000000..ac54a98 Binary files /dev/null and b/data/images/flags/um.png differ diff --git a/data/images/flags/us.png b/data/images/flags/us.png new file mode 100644 index 0000000..ac54a98 Binary files /dev/null and b/data/images/flags/us.png differ diff --git a/data/images/flags/uy.png b/data/images/flags/uy.png new file mode 100644 index 0000000..7e58ac4 Binary files /dev/null and b/data/images/flags/uy.png differ diff --git a/data/images/flags/uz.png b/data/images/flags/uz.png new file mode 100644 index 0000000..76707c5 Binary files /dev/null and b/data/images/flags/uz.png differ diff --git a/data/images/flags/va.png b/data/images/flags/va.png new file mode 100644 index 0000000..2969236 Binary files /dev/null and b/data/images/flags/va.png differ diff --git a/data/images/flags/vc.png b/data/images/flags/vc.png new file mode 100644 index 0000000..93164d1 Binary files /dev/null and b/data/images/flags/vc.png differ diff --git a/data/images/flags/ve.png b/data/images/flags/ve.png new file mode 100644 index 0000000..92844bb Binary files /dev/null and b/data/images/flags/ve.png differ diff --git a/data/images/flags/vg.png b/data/images/flags/vg.png new file mode 100644 index 0000000..8d226ef Binary files /dev/null and b/data/images/flags/vg.png differ diff --git a/data/images/flags/vi.png b/data/images/flags/vi.png new file mode 100644 index 0000000..7916f3a Binary files /dev/null and b/data/images/flags/vi.png differ diff --git a/data/images/flags/vn.png b/data/images/flags/vn.png new file mode 100644 index 0000000..538b081 Binary files /dev/null and b/data/images/flags/vn.png differ diff --git a/data/images/flags/vu.png b/data/images/flags/vu.png new file mode 100644 index 0000000..e514f7b Binary files /dev/null and b/data/images/flags/vu.png differ diff --git a/data/images/flags/wf.png b/data/images/flags/wf.png new file mode 100644 index 0000000..2154ef8 Binary files /dev/null and b/data/images/flags/wf.png differ diff --git a/data/images/flags/ws.png b/data/images/flags/ws.png new file mode 100644 index 0000000..8a26405 Binary files /dev/null and b/data/images/flags/ws.png differ diff --git a/data/images/flags/xt.png b/data/images/flags/xt.png new file mode 100644 index 0000000..ce3d8b3 Binary files /dev/null and b/data/images/flags/xt.png differ diff --git a/data/images/flags/ye.png b/data/images/flags/ye.png new file mode 100644 index 0000000..5b4520f Binary files /dev/null and b/data/images/flags/ye.png differ diff --git a/data/images/flags/yt.png b/data/images/flags/yt.png new file mode 100644 index 0000000..c1a5395 Binary files /dev/null and b/data/images/flags/yt.png differ diff --git a/data/images/flags/yu.png b/data/images/flags/yu.png new file mode 100644 index 0000000..7a89a78 Binary files /dev/null and b/data/images/flags/yu.png differ diff --git a/data/images/flags/za.png b/data/images/flags/za.png new file mode 100644 index 0000000..38a122c Binary files /dev/null and b/data/images/flags/za.png differ diff --git a/data/images/flags/zm.png b/data/images/flags/zm.png new file mode 100644 index 0000000..7503819 Binary files /dev/null and b/data/images/flags/zm.png differ diff --git a/data/images/flags/zw.png b/data/images/flags/zw.png new file mode 100644 index 0000000..0ccea68 Binary files /dev/null and b/data/images/flags/zw.png differ diff --git a/data/images/login/login.png b/data/images/login/login.png new file mode 100644 index 0000000..b50ec42 Binary files /dev/null and b/data/images/login/login.png differ diff --git a/data/images/misc.png b/data/images/misc.png new file mode 100644 index 0000000..b94c2cf Binary files /dev/null and b/data/images/misc.png differ diff --git a/data/texts/AboutBundleManager.html b/data/texts/AboutBundleManager.html new file mode 100644 index 0000000..7157d1e --- /dev/null +++ b/data/texts/AboutBundleManager.html @@ -0,0 +1,36 @@ + + + + + A propos de BundleManager V1 + + + +

BundleManager V1

+
+

+ Ce programme est libre, a été conçu pour illustrer l'apprentissage du langage Java. +

+

+ Tout ou partie de ce programme à vocation à êre recopié. + En revanche, les explications qui accompagnent ce programme ne sont pas libre de droits. +

+
+

misc@merciol.fr

+
+ + diff --git a/data/texts/AboutChat.html b/data/texts/AboutChat.html new file mode 100644 index 0000000..d2cae99 --- /dev/null +++ b/data/texts/AboutChat.html @@ -0,0 +1,36 @@ + + + + + A propos de BundleManager V1 + + + +

Chat V1

+
+

+ Ce programme est libre, a été conçu pour illustrer l'apprentissage du langage Java. +

+

+ Tout ou partie de ce programme à vocation à êre recopié. + En revanche, les explications qui accompagnent ce programme ne sont pas libre de droits. +

+
+

misc@merciol.fr

+
+ + diff --git a/data/texts/AboutLogin.html b/data/texts/AboutLogin.html new file mode 100644 index 0000000..34ece3f --- /dev/null +++ b/data/texts/AboutLogin.html @@ -0,0 +1,36 @@ + + + + + A propos de BundleManager V1 + + + +

Login V1

+
+

+ Ce programme est libre, a été conçu pour illustrer l'apprentissage du langage Java. +

+

+ Tout ou partie de ce programme à vocation à êre recopié. + En revanche, les explications qui accompagnent ce programme ne sont pas libre de droits. +

+
+

misc@merciol.fr

+
+ + diff --git a/data/texts/AboutMisc.html b/data/texts/AboutMisc.html new file mode 100644 index 0000000..baeda63 --- /dev/null +++ b/data/texts/AboutMisc.html @@ -0,0 +1,39 @@ + + + + + A propos de Misc + + + +

Misc (23/05/2016)

+
+

+ Ce programme est libre et a été conçu pour illustrer l'apprentissage du langage Java. +

+

+ Tout ou partie de ce programme à vocation à êre recopié. + En revanche, les explications qui accompagnent ce programme ne sont pas libre de droits. +

+

+ http://misc.parlenet.org/ +

+
+

misc@merciol.fr

+
+ + diff --git a/data/texts/BundleManagerLicence.html b/data/texts/BundleManagerLicence.html new file mode 100644 index 0000000..b6f1e61 --- /dev/null +++ b/data/texts/BundleManagerLicence.html @@ -0,0 +1,653 @@ + + + + + CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B + + + + +

CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B

+ +
+

Avertissement

+ +

Ce contrat est une licence de logiciel libre issue d'une + concertation entre ses auteurs afin que le respect de deux + grands principes préside à sa rédaction:

+ + +

Les auteurs de la licence + CeCILL-B1 sont:

+ +

Commissariat à l'Energie Atomique - CEA, établissement public + de recherche à caractère scientifique, technique et + industriel, dont le siège est situé 25 rue Leblanc, immeuble + Le Ponant D, 75015 Paris.

+

Centre National de la Recherche Scientifique - CNRS, + établissement public à caractère scientifique et + technologique, dont le siège est situé 3 rue Michel-Ange, + 75794 Paris cedex 16.

+ +

Institut National de Recherche en Informatique et en + Automatique - INRIA, établissement public à caractère + scientifique et technologique, dont le siège est situé Domaine + de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay cedex.

+ +
+
+

Préambule

+

Ce contrat est une licence de logiciel libre dont l'objectif + est de conférer aux utilisateurs une très large liberté de + modification et de redistribution du logiciel régi par cette + licence.

+

L'exercice de cette liberté est assorti d'une obligation + forte de citation à la charge de ceux qui distribueraient un + logiciel incorporant un logiciel régi par la présente licence + afin d'assurer que les contributions de tous soient + correctement identifiées et reconnues.

+

L'accessibilité au code source et les droits de copie, de + modification et de redistribution qui découlent de ce contrat + ont pour contrepartie de n'offrir aux utilisateurs qu'une + garantie limitée et de ne faire peser sur l'auteur du + logiciel, le titulaire des droits patrimoniaux et les + concédants successifs qu'une responsabilité restreinte.

+

A cet égard l'attention de l'utilisateur est attirée sur les + risques associés au chargement, à l'utilisation, à la + modification et/ou au développement et à la reproduction du + logiciel par l'utilisateur étant donné sa spécificité de + logiciel libre, qui peut le rendre complexe à manipuler et qui + le réserve donc à des développeurs ou des professionnels + avertis possédant des connaissances informatiques + approfondies. Les utilisateurs sont donc invités à charger et + tester l'adéquation du logiciel à leurs besoins dans des + conditions permettant d'assurer la sécurité de leurs systèmes + et/ou de leurs données et, plus généralement, à l'utiliser et + l'exploiter dans les mêmes conditions de sécurité. Ce contrat + peut être reproduit et diffusé librement, sous réserve de le + conserver en l'état, sans ajout ni suppression de + clauses.

+

Ce contrat est susceptible de s'appliquer à tout logiciel + dont le titulaire des droits patrimoniaux décide de soumettre + l'exploitation aux dispositions qu'il contient.

+ +
+
+

Article 1 - DEFINITIONS

+

Dans ce contrat, les termes suivants, lorsqu'ils seront + écrits avec une lettre capitale, auront la signification + suivante:

+

Contrat: + désigne le présent contrat de licence, ses éventuelles + versions postérieures et annexes.

+

Logiciel: + désigne le logiciel sous sa forme de Code Objet et/ou de Code + Source et le cas échéant sa documentation, dans leur état au + moment de l'acceptation du Contrat par le Licencié.

+

Logiciel + Initial: désigne le Logiciel sous sa forme de Code + Source et éventuellement de Code Objet et le cas échéant sa + documentation, dans leur état au moment de leur première + diffusion sous les termes du Contrat.

+

Logiciel + Modifié: désigne le Logiciel modifié par au moins une + Contribution.

+

Code + Source: désigne l'ensemble des instructions et des + lignes de programme du Logiciel et auquel l'accès est + nécessaire en vue de modifier le Logiciel.

+

Code + Objet: désigne les fichiers binaires issus de la + compilation du Code Source.

+

Titulaire: + désigne le ou les détenteurs des droits patrimoniaux d'auteur + sur le Logiciel Initial.

+

Licencié: + désigne le ou les utilisateurs du Logiciel ayant accepté le + Contrat.

+

Contributeur: + désigne le Licencié auteur d'au moins une Contribution.

+

Concédant: + désigne le Titulaire ou toute personne physique ou morale + distribuant le Logiciel sous le Contrat.

+

Contribution: + désigne l'ensemble des modifications, corrections, + traductions, adaptations et/ou nouvelles fonctionnalités + intégrées dans le Logiciel par tout Contributeur, ainsi que + tout Module Interne.

+

Module: + désigne un ensemble de fichiers sources y compris leur + documentation qui permet de réaliser des fonctionnalités ou + services supplémentaires à ceux fournis par le Logiciel.

+

Module + Externe: désigne tout Module, non dérivé du Logiciel, tel + que ce Module et le Logiciel s'exécutent dans des espaces + d'adressage différents, l'un appelant l'autre au moment de leur + exécution.

+

Module + Interne: désigne tout Module lié au Logiciel de telle + sorte qu'ils s'exécutent dans le même espace + d'adressage.

+

Parties: + désigne collectivement le Licencié et le Concédant.

+

Ces termes s'entendent au singulier comme au pluriel.

+
+
+

Article 2 - OBJET

+

Le Contrat a pour objet la concession par le Concédant au + Licencié d'une licence non exclusive, cessible et mondiale du + Logiciel telle que définie ci-après à + l'article 5 + pour toute la durée de protection des droits portant sur ce + Logiciel.

+
+
+

Article 3 - ACCEPTATION

+
+

3.1 + L'acceptation par le Licencié des termes du Contrat est + réputée acquise du fait du premier des faits suivants:

+ +
+
+

3.2 Un exemplaire du + Contrat, contenant notamment un avertissement relatif aux + spécificités du Logiciel, à la restriction de garantie et à + la limitation à un usage par des utilisateurs expérimentés a + été mis à disposition du Licencié préalablement à son + acceptation telle que définie à + l'article 3.1 + ci dessus et le Licencié reconnaît en avoir pris + connaissance.

+
+
+
+

Article 4 - ENTREE EN VIGUEUR ET DUREE

+
+

4.1 ENTREE EN VIGUEUR

+

Le Contrat entre en vigueur à la date de son acceptation + par le Licencié telle que définie + en 3.1.

+
+
+

4.2 DUREE

+

Le Contrat produira ses effets pendant toute la durée + légale de protection des droits patrimoniaux portant sur le + Logiciel.

+
+
+
+

+ Article 5 - ETENDUE DES DROITS + CONCEDES

+

Le Concédant concède au Licencié, qui accepte, les droits + suivants sur le Logiciel pour toutes destinations et pour la + durée du Contrat dans les conditions ci-après + détaillées.

+

Par ailleurs, si le Concédant détient ou venait à détenir un + ou plusieurs brevets d'invention protégeant tout ou partie des + fonctionnalités du Logiciel ou de ses composants, il s'engage + à ne pas opposer les éventuels droits conférés par ces brevets + aux Licenciés successifs qui utiliseraient, exploiteraient ou + modifieraient le Logiciel. En cas de cession de ces brevets, + le Concédant s'engage à faire reprendre les obligations du + présent alinéa aux cessionnaires.

+
+

5.1 DROIT + D'UTILISATION

+ +

Le Licencié est autorisé à utiliser le Logiciel, sans + restriction quant aux domaines d'application, étant ci-après + précisé que cela comporte:

+
    +
  1. la reproduction permanente ou provisoire du Logiciel + en tout ou partie par tout moyen et sous toute + forme.

  2. +
  3. le chargement, l'affichage, l'exécution, ou le + stockage du Logiciel sur tout support.

  4. +
  5. la possibilité d'en observer, d'en étudier, ou d'en + tester le fonctionnement afin de déterminer les idées et + principes qui sont à la base de n'importe quel élément + de ce Logiciel; et ceci, lorsque le Licencié effectue + toute opération de chargement, d'affichage, d'exécution, + de transmission ou de stockage du Logiciel qu'il est en + droit d'effectuer en vertu du Contrat.

  6. +
+
+
+

5.2 DROIT D'APPORTER DES + CONTRIBUTIONS

+

Le droit d'apporter des Contributions comporte le droit de + traduire, d'adapter, d'arranger ou d'apporter toute autre + modification au Logiciel et le droit de reproduire le + logiciel en résultant.

+

Le Licencié est autorisé à apporter toute Contribution au + Logiciel sous réserve de mentionner, de façon explicite, son + nom en tant qu'auteur de cette Contribution et la date de + création de celle-ci.

+
+
+

5.3 DROIT DE + DISTRIBUTION

+

Le droit de distribution comporte notamment le droit de + diffuser, de transmettre et de communiquer le Logiciel au + public sur tout support et par tout moyen ainsi que le droit + de mettre sur le marché à titre onéreux ou gratuit, un ou + des exemplaires du Logiciel par tout procédé.

+

Le Licencié est autorisé à distribuer des copies du + Logiciel, modifié ou non, à des tiers dans les conditions + ci-après détaillées.

+
+

5.3.1 DISTRIBUTION DU + LOGICIEL SANS MODIFICATION

+

Le Licencié est autorisé à distribuer des copies + conformes du Logiciel, sous forme de Code Source ou de + Code Objet, à condition que cette distribution respecte + les dispositions du Contrat dans leur totalité et soit + accompagnée:

+
    +
  1. d'un exemplaire du Contrat,

  2. +
  3. d'un avertissement relatif à la restriction de + garantie et de responsabilité du Concédant telle que + prévue aux + articles 8 + et 9,

  4. +
+

et que, dans le cas où seul le Code Objet du Logiciel est + redistribué, le Licencié permette un accès effectif au + Code Source complet du Logiciel pendant au moins toute la + durée de sa distribution du Logiciel, étant entendu que le + coût additionnel d'acquisition du Code Source ne devra pas + excéder le simple coût de transfert des données.

+
+
+

+ 5.3.2 DISTRIBUTION DU LOGICIEL MODIFIE

+

Lorsque le Licencié apporte une Contribution au Logiciel, + le Logiciel Modifié peut être distribué sous un contrat de + licence autre que le présent Contrat sous réserve du + respect des dispositions de l'article + 5.3.4.

+
+
+

5.3.3 DISTRIBUTION DES + MODULES EXTERNES

+

Lorsque le Licencié a développé un Module Externe les + conditions du Contrat ne s'appliquent pas à ce Module + Externe, qui peut être distribué sous un contrat de + licence différent.

+
+
+

5.3.4 CITATIONS

+

Le Licencié qui distribue un Logiciel Modifié s'engage + expressément:

+
    +
  1. à indiquer dans sa documentation qu'il a été réalisé + à partir du Logiciel régi par le Contrat, en + reproduisant les mentions de propriété intellectuelle + du Logiciel,

  2. + +
  3. à faire en sorte que l'utilisation du Logiciel, ses + mentions de propriété intellectuelle et le fait qu'il + est régi par le Contrat soient indiqués dans un texte + facilement accessible depuis l'interface du Logiciel + Modifié,

  4. +
  5. à mentionner, sur un site Web librement accessible + décrivant le Logiciel Modifié, et pendant au moins + toute la durée de sa distribution, qu'il a été réalisé + à partir du Logiciel régi par le Contrat, en + reproduisant les mentions de propriété intellectuelle + du Logiciel,

  6. +
  7. lorsqu'il le distribue à un tiers susceptible de + distribuer lui-même un Logiciel Modifié, sans avoir à + en distribuer le code source, à faire ses meilleurs + efforts pour que les obligations du présent + article 5.3.4 + soient reprises par le dit tiers.

  8. +
+

Lorsque le Logiciel modifié ou non est distribué avec un + Module Externe qui a été conçu pour l'utiliser, le + Licencié doit soumettre le dit Module Externe aux + obligations précédentes.

+
+
+

5.3.5 COMPATIBILITE + AVEC LES LICENCES CeCILL et CeCILL-C

+

Lorsqu'un Logiciel Modifié contient une Contribution + soumise au contrat de licence CeCILL, les stipulations + prévues à + l'article 5.3.4 + sont facultatives.

+

Un Logiciel Modifié peut être distribué sous le contrat + de licence CeCILL-C. Les stipulations prévues à + l'article 5.3.4 + sont alors facultatives.

+
+
+
+
+

Article 6 - PROPRIETE INTELLECTUELLE

+
+

6.1 SUR LE LOGICIEL + INITIAL

+

Le Titulaire est détenteur des droits patrimoniaux sur le + Logiciel Initial. Toute utilisation du Logiciel Initial est + soumise au respect des conditions dans lesquelles le + Titulaire a choisi de diffuser son oeuvre et nul autre n'a + la faculté de modifier les conditions de diffusion de ce + Logiciel Initial.

+

Le Titulaire s'engage à ce que le Logiciel Initial reste au + moins régi par le Contrat et ce, pour la durée visée à + l'article 4.2.

+
+
+

6.2 SUR LES + CONTRIBUTIONS

+

Le Licencié qui a développé une Contribution est titulaire + sur celle-ci des droits de propriété intellectuelle dans les + conditions définies par la législation applicable.

+
+
+

6.3 SUR LES MODULES + EXTERNES

+

Le Licencié qui a développé un Module Externe est + titulaire sur celui-ci des droits de propriété + intellectuelle dans les conditions définies par la + législation applicable et reste libre du choix du contrat + régissant sa diffusion.

+
+
+

6.4 DISPOSITIONS + COMMUNES

+
+

Le Licencié s'engage expressément:

+
    +
  1. à ne pas supprimer ou modifier de quelque manière + que ce soit les mentions de propriété intellectuelle + apposées sur le Logiciel;

  2. +
  3. à reproduire à l'identique lesdites mentions de + propriété intellectuelle sur les copies du Logiciel + modifié ou non.

  4. +
+
+
+

Le Licencié s'engage à ne pas porter atteinte, + directement ou indirectement, aux droits de propriété + intellectuelle du Titulaire et/ou des Contributeurs sur le + Logiciel et à prendre, le cas échéant, à l'égard de son + personnel toutes les mesures nécessaires pour assurer le + respect des dits droits de propriété intellectuelle du + Titulaire et/ou des Contributeurs.

+
+
+
+
+

Article 7 - SERVICES ASSOCIES

+
+

7.1 Le Contrat n'oblige en + aucun cas le Concédant à la réalisation de prestations + d'assistance technique ou de maintenance du Logiciel.

+

Cependant le Concédant reste libre de proposer ce type de + services. Les termes et conditions d'une telle assistance + technique et/ou d'une telle maintenance seront alors + déterminés dans un acte séparé. Ces actes de maintenance + et/ou assistance technique n'engageront que la seule + responsabilité du Concédant qui les propose.

+
+
+

7.2 De même, tout Concédant + est libre de proposer, sous sa seule responsabilité, à ses + licenciés une garantie, qui n'engagera que lui, lors de la + redistribution du Logiciel et/ou du Logiciel Modifié et ce, + dans les conditions qu'il souhaite. Cette garantie et les + modalités financières de son application feront l'objet d'un + acte séparé entre le Concédant et le Licencié.

+
+
+
+

+ Article 8 - + RESPONSABILITE

+
+

8.1 Sous réserve des + dispositions de + l'article 8.2, + le Licencié a la faculté, sous réserve de prouver la faute + du Concédant concerné, de solliciter la réparation du + préjudice direct qu'il subirait du fait du Logiciel et dont + il apportera la preuve.

+
+
+

8.2 + La responsabilité du Concédant est limitée aux engagements + pris en application du Contrat et ne saurait être engagée en + raison notamment: (i) des dommages dus à l'inexécution, + totale ou partielle, de ses obligations par le Licencié, + (ii) des dommages directs ou indirects découlant de + l'utilisation ou des performances du Logiciel subis par le + Licencié et (iii) plus généralement d'un quelconque dommage + indirect. En particulier, les Parties conviennent + expressément que tout préjudice financier ou commercial (par + exemple perte de données, perte de bénéfices, perte + d'exploitation, perte de clientèle ou de commandes, manque à + gagner, trouble commercial quelconque) ou toute action + dirigée contre le Licencié par un tiers, constitue un + dommage indirect et n'ouvre pas droit à réparation par le + Concédant.

+
+
+
+

+ Article 9 - GARANTIE

+
+

9.1 Le Licencié reconnaît + que l'état actuel des connaissances scientifiques et + techniques au moment de la mise en circulation du Logiciel + ne permet pas d'en tester et d'en vérifier toutes les + utilisations ni de détecter l'existence d'éventuels défauts. + L'attention du Licencié a été attirée sur ce point sur les + risques associés au chargement, à l'utilisation, la + modification et/ou au développement et à la reproduction du + Logiciel qui sont réservés à des utilisateurs avertis.

+

Il relève de la responsabilité du Licencié de contrôler, + par tous moyens, l'adéquation du produit à ses besoins, son + bon fonctionnement et de s'assurer qu'il ne causera pas de + dommages aux personnes et aux biens.

+
+
+

9.2 + Le Concédant déclare de bonne foi être en droit de concéder + l'ensemble des droits attachés au Logiciel (comprenant + notamment les droits visés à l'article + 5).

+
+
+

9.3 Le Licencié reconnaît + que le Logiciel est fourni "en l'état" par le Concédant sans + autre garantie, expresse ou tacite, que celle prévue à + l'article 9.2 + et notamment sans aucune garantie sur sa valeur commerciale, + son caractère sécurisé, innovant ou pertinent.

+

En particulier, le Concédant ne garantit pas que le + Logiciel est exempt d'erreur, qu'il fonctionnera sans + interruption, qu'il sera compatible avec l'équipement du + Licencié et sa configuration logicielle ni qu'il remplira + les besoins du Licencié.

+
+
+

9.4 Le Concédant ne garantit + pas, de manière expresse ou tacite, que le Logiciel ne porte + pas atteinte à un quelconque droit de propriété + intellectuelle d'un tiers portant sur un brevet, un logiciel + ou sur tout autre droit de propriété. Ainsi, le Concédant + exclut toute garantie au profit du Licencié contre les + actions en contrefaçon qui pourraient être diligentées au + titre de l'utilisation, de la modification, et de la + redistribution du Logiciel. Néanmoins, si de telles actions + sont exercées contre le Licencié, le Concédant lui apportera + son aide technique et juridique pour sa défense. Cette aide + technique et juridique est déterminée au cas par cas entre + le Concédant concerné et le Licencié dans le cadre d'un + protocole d'accord. Le Concédant dégage toute responsabilité + quant à l'utilisation de la dénomination du Logiciel par le + Licencié. Aucune garantie n'est apportée quant à l'existence + de droits antérieurs sur le nom du Logiciel et sur + l'existence d'une marque.

+
+
+
+

Article 10 - + RESILIATION

+
+

10.1 En cas de manquement + par le Licencié aux obligations mises à sa charge par le + Contrat, le Concédant pourra résilier de plein droit le + Contrat trente (30) jours après notification adressée au + Licencié et restée sans effet.

+
+
+

10.2 Le Licencié dont le + Contrat est résilié n'est plus autorisé à utiliser, modifier + ou distribuer le Logiciel. Cependant, toutes les licences + qu'il aura concédées antérieurement à la résiliation du + Contrat resteront valides sous réserve qu'elles aient été + effectuées en conformité avec le Contrat.

+
+
+
+

Article 11 - DISPOSITIONS + DIVERSES

+
+

+ 11.1 CAUSE EXTERIEURE

+

Aucune + des Parties ne sera responsable d'un retard ou d'une + défaillance d'exécution du Contrat qui serait dû + à un cas de force majeure, un cas fortuit ou une cause + extérieure, telle que, notamment, le mauvais fonctionnement + ou les interruptions du réseau électrique ou de + télécommunication, la paralysie du réseau liée + à une attaque informatique, l'intervention des + autorités gouvernementales, les catastrophes naturelles, les + dégâts des eaux, les tremblements de terre, le feu, les + explosions, les grèves et les conflits sociaux, l'état + de guerre...

+
+
+

11.2 Le + fait, par l'une ou l'autre des Parties, d'omettre + en une ou plusieurs occasions de se prévaloir d'une ou + plusieurs dispositions du Contrat, ne pourra en aucun cas impliquer + renonciation par la Partie intéressée à s'en + prévaloir ultérieurement.

+
+
+

11.3 Le Contrat annule et + remplace toute convention antérieure, écrite ou orale, entre + les Parties sur le même objet et constitue l'accord entier + entre les Parties sur cet objet. Aucune addition ou + modification aux termes du Contrat n'aura d'effet à l'égard + des Parties à moins d'être faite par écrit et signée par + leurs représentants dûment habilités.

+
+
+

11.4 Dans l'hypothèse où une + ou plusieurs des dispositions du Contrat s'avèrerait + contraire à une loi ou à un texte applicable, existants ou + futurs, cette loi ou ce texte prévaudrait, et les Parties + feraient les amendements nécessaires pour se conformer à + cette loi ou à ce texte. Toutes les autres dispositions + resteront en vigueur. De même, la nullité, pour quelque + raison que ce soit, d'une des dispositions du Contrat ne + saurait entraîner la nullité de l'ensemble du Contrat.

+
+
+

+ 11.5 LANGUE

+

Le Contrat est rédigé en langue française et en langue + anglaise, ces deux versions faisant également foi. +

+
+
+
+

Article 12 - NOUVELLES + VERSIONS DU CONTRAT

+
+

12.1 Toute personne est + autorisée à copier et distribuer des copies de ce + Contrat.

+
+
+

12.2 Afin d'en préserver la + cohérence, le texte du Contrat est protégé et ne peut être + modifié que par les auteurs de la licence, lesquels se + réservent le droit de publier périodiquement des mises à + jour ou de nouvelles versions du Contrat, qui posséderont + chacune un numéro distinct. Ces versions ultérieures seront + susceptibles de prendre en compte de nouvelles + problématiques rencontrées par les logiciels libres.

+
+
+

12.3 Tout Logiciel diffusé + sous une version donnée du Contrat ne pourra faire l'objet + d'une diffusion ultérieure que sous la même version du + Contrat ou une version postérieure.

+
+
+
+

Article 13 - LOI APPLICABLE + ET COMPETENCE TERRITORIALE

+
+

13.1 Le Contrat est régi + par la loi française. Les Parties conviennent de tenter de + régler à l'amiable les différends ou litiges qui viendraient + à se produire par suite ou à l'occasion du Contrat. +

+
+
+

13.2 A défaut d'accord + amiable dans un délai de deux (2) mois à compter de leur + survenance et sauf situation relevant d'une procédure + d'urgence, les différends ou litiges seront portés par la + Partie la plus diligente devant les Tribunaux compétents de + Paris.

+
+
+
+

1 CeCILL est pour Ce(a) C(nrs) I(nria) L(ogiciel) L(ibre)

+
+
Version 1.0 du 2006-09-05.
+ + diff --git a/data/texts/ChatLicence.html b/data/texts/ChatLicence.html new file mode 100644 index 0000000..b6f1e61 --- /dev/null +++ b/data/texts/ChatLicence.html @@ -0,0 +1,653 @@ + + + + + CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B + + + + +

CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B

+ +
+

Avertissement

+ +

Ce contrat est une licence de logiciel libre issue d'une + concertation entre ses auteurs afin que le respect de deux + grands principes préside à sa rédaction:

+ + +

Les auteurs de la licence + CeCILL-B1 sont:

+ +

Commissariat à l'Energie Atomique - CEA, établissement public + de recherche à caractère scientifique, technique et + industriel, dont le siège est situé 25 rue Leblanc, immeuble + Le Ponant D, 75015 Paris.

+

Centre National de la Recherche Scientifique - CNRS, + établissement public à caractère scientifique et + technologique, dont le siège est situé 3 rue Michel-Ange, + 75794 Paris cedex 16.

+ +

Institut National de Recherche en Informatique et en + Automatique - INRIA, établissement public à caractère + scientifique et technologique, dont le siège est situé Domaine + de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay cedex.

+ +
+
+

Préambule

+

Ce contrat est une licence de logiciel libre dont l'objectif + est de conférer aux utilisateurs une très large liberté de + modification et de redistribution du logiciel régi par cette + licence.

+

L'exercice de cette liberté est assorti d'une obligation + forte de citation à la charge de ceux qui distribueraient un + logiciel incorporant un logiciel régi par la présente licence + afin d'assurer que les contributions de tous soient + correctement identifiées et reconnues.

+

L'accessibilité au code source et les droits de copie, de + modification et de redistribution qui découlent de ce contrat + ont pour contrepartie de n'offrir aux utilisateurs qu'une + garantie limitée et de ne faire peser sur l'auteur du + logiciel, le titulaire des droits patrimoniaux et les + concédants successifs qu'une responsabilité restreinte.

+

A cet égard l'attention de l'utilisateur est attirée sur les + risques associés au chargement, à l'utilisation, à la + modification et/ou au développement et à la reproduction du + logiciel par l'utilisateur étant donné sa spécificité de + logiciel libre, qui peut le rendre complexe à manipuler et qui + le réserve donc à des développeurs ou des professionnels + avertis possédant des connaissances informatiques + approfondies. Les utilisateurs sont donc invités à charger et + tester l'adéquation du logiciel à leurs besoins dans des + conditions permettant d'assurer la sécurité de leurs systèmes + et/ou de leurs données et, plus généralement, à l'utiliser et + l'exploiter dans les mêmes conditions de sécurité. Ce contrat + peut être reproduit et diffusé librement, sous réserve de le + conserver en l'état, sans ajout ni suppression de + clauses.

+

Ce contrat est susceptible de s'appliquer à tout logiciel + dont le titulaire des droits patrimoniaux décide de soumettre + l'exploitation aux dispositions qu'il contient.

+ +
+
+

Article 1 - DEFINITIONS

+

Dans ce contrat, les termes suivants, lorsqu'ils seront + écrits avec une lettre capitale, auront la signification + suivante:

+

Contrat: + désigne le présent contrat de licence, ses éventuelles + versions postérieures et annexes.

+

Logiciel: + désigne le logiciel sous sa forme de Code Objet et/ou de Code + Source et le cas échéant sa documentation, dans leur état au + moment de l'acceptation du Contrat par le Licencié.

+

Logiciel + Initial: désigne le Logiciel sous sa forme de Code + Source et éventuellement de Code Objet et le cas échéant sa + documentation, dans leur état au moment de leur première + diffusion sous les termes du Contrat.

+

Logiciel + Modifié: désigne le Logiciel modifié par au moins une + Contribution.

+

Code + Source: désigne l'ensemble des instructions et des + lignes de programme du Logiciel et auquel l'accès est + nécessaire en vue de modifier le Logiciel.

+

Code + Objet: désigne les fichiers binaires issus de la + compilation du Code Source.

+

Titulaire: + désigne le ou les détenteurs des droits patrimoniaux d'auteur + sur le Logiciel Initial.

+

Licencié: + désigne le ou les utilisateurs du Logiciel ayant accepté le + Contrat.

+

Contributeur: + désigne le Licencié auteur d'au moins une Contribution.

+

Concédant: + désigne le Titulaire ou toute personne physique ou morale + distribuant le Logiciel sous le Contrat.

+

Contribution: + désigne l'ensemble des modifications, corrections, + traductions, adaptations et/ou nouvelles fonctionnalités + intégrées dans le Logiciel par tout Contributeur, ainsi que + tout Module Interne.

+

Module: + désigne un ensemble de fichiers sources y compris leur + documentation qui permet de réaliser des fonctionnalités ou + services supplémentaires à ceux fournis par le Logiciel.

+

Module + Externe: désigne tout Module, non dérivé du Logiciel, tel + que ce Module et le Logiciel s'exécutent dans des espaces + d'adressage différents, l'un appelant l'autre au moment de leur + exécution.

+

Module + Interne: désigne tout Module lié au Logiciel de telle + sorte qu'ils s'exécutent dans le même espace + d'adressage.

+

Parties: + désigne collectivement le Licencié et le Concédant.

+

Ces termes s'entendent au singulier comme au pluriel.

+
+
+

Article 2 - OBJET

+

Le Contrat a pour objet la concession par le Concédant au + Licencié d'une licence non exclusive, cessible et mondiale du + Logiciel telle que définie ci-après à + l'article 5 + pour toute la durée de protection des droits portant sur ce + Logiciel.

+
+
+

Article 3 - ACCEPTATION

+
+

3.1 + L'acceptation par le Licencié des termes du Contrat est + réputée acquise du fait du premier des faits suivants:

+ +
+
+

3.2 Un exemplaire du + Contrat, contenant notamment un avertissement relatif aux + spécificités du Logiciel, à la restriction de garantie et à + la limitation à un usage par des utilisateurs expérimentés a + été mis à disposition du Licencié préalablement à son + acceptation telle que définie à + l'article 3.1 + ci dessus et le Licencié reconnaît en avoir pris + connaissance.

+
+
+
+

Article 4 - ENTREE EN VIGUEUR ET DUREE

+
+

4.1 ENTREE EN VIGUEUR

+

Le Contrat entre en vigueur à la date de son acceptation + par le Licencié telle que définie + en 3.1.

+
+
+

4.2 DUREE

+

Le Contrat produira ses effets pendant toute la durée + légale de protection des droits patrimoniaux portant sur le + Logiciel.

+
+
+
+

+ Article 5 - ETENDUE DES DROITS + CONCEDES

+

Le Concédant concède au Licencié, qui accepte, les droits + suivants sur le Logiciel pour toutes destinations et pour la + durée du Contrat dans les conditions ci-après + détaillées.

+

Par ailleurs, si le Concédant détient ou venait à détenir un + ou plusieurs brevets d'invention protégeant tout ou partie des + fonctionnalités du Logiciel ou de ses composants, il s'engage + à ne pas opposer les éventuels droits conférés par ces brevets + aux Licenciés successifs qui utiliseraient, exploiteraient ou + modifieraient le Logiciel. En cas de cession de ces brevets, + le Concédant s'engage à faire reprendre les obligations du + présent alinéa aux cessionnaires.

+
+

5.1 DROIT + D'UTILISATION

+ +

Le Licencié est autorisé à utiliser le Logiciel, sans + restriction quant aux domaines d'application, étant ci-après + précisé que cela comporte:

+
    +
  1. la reproduction permanente ou provisoire du Logiciel + en tout ou partie par tout moyen et sous toute + forme.

  2. +
  3. le chargement, l'affichage, l'exécution, ou le + stockage du Logiciel sur tout support.

  4. +
  5. la possibilité d'en observer, d'en étudier, ou d'en + tester le fonctionnement afin de déterminer les idées et + principes qui sont à la base de n'importe quel élément + de ce Logiciel; et ceci, lorsque le Licencié effectue + toute opération de chargement, d'affichage, d'exécution, + de transmission ou de stockage du Logiciel qu'il est en + droit d'effectuer en vertu du Contrat.

  6. +
+
+
+

5.2 DROIT D'APPORTER DES + CONTRIBUTIONS

+

Le droit d'apporter des Contributions comporte le droit de + traduire, d'adapter, d'arranger ou d'apporter toute autre + modification au Logiciel et le droit de reproduire le + logiciel en résultant.

+

Le Licencié est autorisé à apporter toute Contribution au + Logiciel sous réserve de mentionner, de façon explicite, son + nom en tant qu'auteur de cette Contribution et la date de + création de celle-ci.

+
+
+

5.3 DROIT DE + DISTRIBUTION

+

Le droit de distribution comporte notamment le droit de + diffuser, de transmettre et de communiquer le Logiciel au + public sur tout support et par tout moyen ainsi que le droit + de mettre sur le marché à titre onéreux ou gratuit, un ou + des exemplaires du Logiciel par tout procédé.

+

Le Licencié est autorisé à distribuer des copies du + Logiciel, modifié ou non, à des tiers dans les conditions + ci-après détaillées.

+
+

5.3.1 DISTRIBUTION DU + LOGICIEL SANS MODIFICATION

+

Le Licencié est autorisé à distribuer des copies + conformes du Logiciel, sous forme de Code Source ou de + Code Objet, à condition que cette distribution respecte + les dispositions du Contrat dans leur totalité et soit + accompagnée:

+
    +
  1. d'un exemplaire du Contrat,

  2. +
  3. d'un avertissement relatif à la restriction de + garantie et de responsabilité du Concédant telle que + prévue aux + articles 8 + et 9,

  4. +
+

et que, dans le cas où seul le Code Objet du Logiciel est + redistribué, le Licencié permette un accès effectif au + Code Source complet du Logiciel pendant au moins toute la + durée de sa distribution du Logiciel, étant entendu que le + coût additionnel d'acquisition du Code Source ne devra pas + excéder le simple coût de transfert des données.

+
+
+

+ 5.3.2 DISTRIBUTION DU LOGICIEL MODIFIE

+

Lorsque le Licencié apporte une Contribution au Logiciel, + le Logiciel Modifié peut être distribué sous un contrat de + licence autre que le présent Contrat sous réserve du + respect des dispositions de l'article + 5.3.4.

+
+
+

5.3.3 DISTRIBUTION DES + MODULES EXTERNES

+

Lorsque le Licencié a développé un Module Externe les + conditions du Contrat ne s'appliquent pas à ce Module + Externe, qui peut être distribué sous un contrat de + licence différent.

+
+
+

5.3.4 CITATIONS

+

Le Licencié qui distribue un Logiciel Modifié s'engage + expressément:

+
    +
  1. à indiquer dans sa documentation qu'il a été réalisé + à partir du Logiciel régi par le Contrat, en + reproduisant les mentions de propriété intellectuelle + du Logiciel,

  2. + +
  3. à faire en sorte que l'utilisation du Logiciel, ses + mentions de propriété intellectuelle et le fait qu'il + est régi par le Contrat soient indiqués dans un texte + facilement accessible depuis l'interface du Logiciel + Modifié,

  4. +
  5. à mentionner, sur un site Web librement accessible + décrivant le Logiciel Modifié, et pendant au moins + toute la durée de sa distribution, qu'il a été réalisé + à partir du Logiciel régi par le Contrat, en + reproduisant les mentions de propriété intellectuelle + du Logiciel,

  6. +
  7. lorsqu'il le distribue à un tiers susceptible de + distribuer lui-même un Logiciel Modifié, sans avoir à + en distribuer le code source, à faire ses meilleurs + efforts pour que les obligations du présent + article 5.3.4 + soient reprises par le dit tiers.

  8. +
+

Lorsque le Logiciel modifié ou non est distribué avec un + Module Externe qui a été conçu pour l'utiliser, le + Licencié doit soumettre le dit Module Externe aux + obligations précédentes.

+
+
+

5.3.5 COMPATIBILITE + AVEC LES LICENCES CeCILL et CeCILL-C

+

Lorsqu'un Logiciel Modifié contient une Contribution + soumise au contrat de licence CeCILL, les stipulations + prévues à + l'article 5.3.4 + sont facultatives.

+

Un Logiciel Modifié peut être distribué sous le contrat + de licence CeCILL-C. Les stipulations prévues à + l'article 5.3.4 + sont alors facultatives.

+
+
+
+
+

Article 6 - PROPRIETE INTELLECTUELLE

+
+

6.1 SUR LE LOGICIEL + INITIAL

+

Le Titulaire est détenteur des droits patrimoniaux sur le + Logiciel Initial. Toute utilisation du Logiciel Initial est + soumise au respect des conditions dans lesquelles le + Titulaire a choisi de diffuser son oeuvre et nul autre n'a + la faculté de modifier les conditions de diffusion de ce + Logiciel Initial.

+

Le Titulaire s'engage à ce que le Logiciel Initial reste au + moins régi par le Contrat et ce, pour la durée visée à + l'article 4.2.

+
+
+

6.2 SUR LES + CONTRIBUTIONS

+

Le Licencié qui a développé une Contribution est titulaire + sur celle-ci des droits de propriété intellectuelle dans les + conditions définies par la législation applicable.

+
+
+

6.3 SUR LES MODULES + EXTERNES

+

Le Licencié qui a développé un Module Externe est + titulaire sur celui-ci des droits de propriété + intellectuelle dans les conditions définies par la + législation applicable et reste libre du choix du contrat + régissant sa diffusion.

+
+
+

6.4 DISPOSITIONS + COMMUNES

+
+

Le Licencié s'engage expressément:

+
    +
  1. à ne pas supprimer ou modifier de quelque manière + que ce soit les mentions de propriété intellectuelle + apposées sur le Logiciel;

  2. +
  3. à reproduire à l'identique lesdites mentions de + propriété intellectuelle sur les copies du Logiciel + modifié ou non.

  4. +
+
+
+

Le Licencié s'engage à ne pas porter atteinte, + directement ou indirectement, aux droits de propriété + intellectuelle du Titulaire et/ou des Contributeurs sur le + Logiciel et à prendre, le cas échéant, à l'égard de son + personnel toutes les mesures nécessaires pour assurer le + respect des dits droits de propriété intellectuelle du + Titulaire et/ou des Contributeurs.

+
+
+
+
+

Article 7 - SERVICES ASSOCIES

+
+

7.1 Le Contrat n'oblige en + aucun cas le Concédant à la réalisation de prestations + d'assistance technique ou de maintenance du Logiciel.

+

Cependant le Concédant reste libre de proposer ce type de + services. Les termes et conditions d'une telle assistance + technique et/ou d'une telle maintenance seront alors + déterminés dans un acte séparé. Ces actes de maintenance + et/ou assistance technique n'engageront que la seule + responsabilité du Concédant qui les propose.

+
+
+

7.2 De même, tout Concédant + est libre de proposer, sous sa seule responsabilité, à ses + licenciés une garantie, qui n'engagera que lui, lors de la + redistribution du Logiciel et/ou du Logiciel Modifié et ce, + dans les conditions qu'il souhaite. Cette garantie et les + modalités financières de son application feront l'objet d'un + acte séparé entre le Concédant et le Licencié.

+
+
+
+

+ Article 8 - + RESPONSABILITE

+
+

8.1 Sous réserve des + dispositions de + l'article 8.2, + le Licencié a la faculté, sous réserve de prouver la faute + du Concédant concerné, de solliciter la réparation du + préjudice direct qu'il subirait du fait du Logiciel et dont + il apportera la preuve.

+
+
+

8.2 + La responsabilité du Concédant est limitée aux engagements + pris en application du Contrat et ne saurait être engagée en + raison notamment: (i) des dommages dus à l'inexécution, + totale ou partielle, de ses obligations par le Licencié, + (ii) des dommages directs ou indirects découlant de + l'utilisation ou des performances du Logiciel subis par le + Licencié et (iii) plus généralement d'un quelconque dommage + indirect. En particulier, les Parties conviennent + expressément que tout préjudice financier ou commercial (par + exemple perte de données, perte de bénéfices, perte + d'exploitation, perte de clientèle ou de commandes, manque à + gagner, trouble commercial quelconque) ou toute action + dirigée contre le Licencié par un tiers, constitue un + dommage indirect et n'ouvre pas droit à réparation par le + Concédant.

+
+
+
+

+ Article 9 - GARANTIE

+
+

9.1 Le Licencié reconnaît + que l'état actuel des connaissances scientifiques et + techniques au moment de la mise en circulation du Logiciel + ne permet pas d'en tester et d'en vérifier toutes les + utilisations ni de détecter l'existence d'éventuels défauts. + L'attention du Licencié a été attirée sur ce point sur les + risques associés au chargement, à l'utilisation, la + modification et/ou au développement et à la reproduction du + Logiciel qui sont réservés à des utilisateurs avertis.

+

Il relève de la responsabilité du Licencié de contrôler, + par tous moyens, l'adéquation du produit à ses besoins, son + bon fonctionnement et de s'assurer qu'il ne causera pas de + dommages aux personnes et aux biens.

+
+
+

9.2 + Le Concédant déclare de bonne foi être en droit de concéder + l'ensemble des droits attachés au Logiciel (comprenant + notamment les droits visés à l'article + 5).

+
+
+

9.3 Le Licencié reconnaît + que le Logiciel est fourni "en l'état" par le Concédant sans + autre garantie, expresse ou tacite, que celle prévue à + l'article 9.2 + et notamment sans aucune garantie sur sa valeur commerciale, + son caractère sécurisé, innovant ou pertinent.

+

En particulier, le Concédant ne garantit pas que le + Logiciel est exempt d'erreur, qu'il fonctionnera sans + interruption, qu'il sera compatible avec l'équipement du + Licencié et sa configuration logicielle ni qu'il remplira + les besoins du Licencié.

+
+
+

9.4 Le Concédant ne garantit + pas, de manière expresse ou tacite, que le Logiciel ne porte + pas atteinte à un quelconque droit de propriété + intellectuelle d'un tiers portant sur un brevet, un logiciel + ou sur tout autre droit de propriété. Ainsi, le Concédant + exclut toute garantie au profit du Licencié contre les + actions en contrefaçon qui pourraient être diligentées au + titre de l'utilisation, de la modification, et de la + redistribution du Logiciel. Néanmoins, si de telles actions + sont exercées contre le Licencié, le Concédant lui apportera + son aide technique et juridique pour sa défense. Cette aide + technique et juridique est déterminée au cas par cas entre + le Concédant concerné et le Licencié dans le cadre d'un + protocole d'accord. Le Concédant dégage toute responsabilité + quant à l'utilisation de la dénomination du Logiciel par le + Licencié. Aucune garantie n'est apportée quant à l'existence + de droits antérieurs sur le nom du Logiciel et sur + l'existence d'une marque.

+
+
+
+

Article 10 - + RESILIATION

+
+

10.1 En cas de manquement + par le Licencié aux obligations mises à sa charge par le + Contrat, le Concédant pourra résilier de plein droit le + Contrat trente (30) jours après notification adressée au + Licencié et restée sans effet.

+
+
+

10.2 Le Licencié dont le + Contrat est résilié n'est plus autorisé à utiliser, modifier + ou distribuer le Logiciel. Cependant, toutes les licences + qu'il aura concédées antérieurement à la résiliation du + Contrat resteront valides sous réserve qu'elles aient été + effectuées en conformité avec le Contrat.

+
+
+
+

Article 11 - DISPOSITIONS + DIVERSES

+
+

+ 11.1 CAUSE EXTERIEURE

+

Aucune + des Parties ne sera responsable d'un retard ou d'une + défaillance d'exécution du Contrat qui serait dû + à un cas de force majeure, un cas fortuit ou une cause + extérieure, telle que, notamment, le mauvais fonctionnement + ou les interruptions du réseau électrique ou de + télécommunication, la paralysie du réseau liée + à une attaque informatique, l'intervention des + autorités gouvernementales, les catastrophes naturelles, les + dégâts des eaux, les tremblements de terre, le feu, les + explosions, les grèves et les conflits sociaux, l'état + de guerre...

+
+
+

11.2 Le + fait, par l'une ou l'autre des Parties, d'omettre + en une ou plusieurs occasions de se prévaloir d'une ou + plusieurs dispositions du Contrat, ne pourra en aucun cas impliquer + renonciation par la Partie intéressée à s'en + prévaloir ultérieurement.

+
+
+

11.3 Le Contrat annule et + remplace toute convention antérieure, écrite ou orale, entre + les Parties sur le même objet et constitue l'accord entier + entre les Parties sur cet objet. Aucune addition ou + modification aux termes du Contrat n'aura d'effet à l'égard + des Parties à moins d'être faite par écrit et signée par + leurs représentants dûment habilités.

+
+
+

11.4 Dans l'hypothèse où une + ou plusieurs des dispositions du Contrat s'avèrerait + contraire à une loi ou à un texte applicable, existants ou + futurs, cette loi ou ce texte prévaudrait, et les Parties + feraient les amendements nécessaires pour se conformer à + cette loi ou à ce texte. Toutes les autres dispositions + resteront en vigueur. De même, la nullité, pour quelque + raison que ce soit, d'une des dispositions du Contrat ne + saurait entraîner la nullité de l'ensemble du Contrat.

+
+
+

+ 11.5 LANGUE

+

Le Contrat est rédigé en langue française et en langue + anglaise, ces deux versions faisant également foi. +

+
+
+
+

Article 12 - NOUVELLES + VERSIONS DU CONTRAT

+
+

12.1 Toute personne est + autorisée à copier et distribuer des copies de ce + Contrat.

+
+
+

12.2 Afin d'en préserver la + cohérence, le texte du Contrat est protégé et ne peut être + modifié que par les auteurs de la licence, lesquels se + réservent le droit de publier périodiquement des mises à + jour ou de nouvelles versions du Contrat, qui posséderont + chacune un numéro distinct. Ces versions ultérieures seront + susceptibles de prendre en compte de nouvelles + problématiques rencontrées par les logiciels libres.

+
+
+

12.3 Tout Logiciel diffusé + sous une version donnée du Contrat ne pourra faire l'objet + d'une diffusion ultérieure que sous la même version du + Contrat ou une version postérieure.

+
+
+
+

Article 13 - LOI APPLICABLE + ET COMPETENCE TERRITORIALE

+
+

13.1 Le Contrat est régi + par la loi française. Les Parties conviennent de tenter de + régler à l'amiable les différends ou litiges qui viendraient + à se produire par suite ou à l'occasion du Contrat. +

+
+
+

13.2 A défaut d'accord + amiable dans un délai de deux (2) mois à compter de leur + survenance et sauf situation relevant d'une procédure + d'urgence, les différends ou litiges seront portés par la + Partie la plus diligente devant les Tribunaux compétents de + Paris.

+
+
+
+

1 CeCILL est pour Ce(a) C(nrs) I(nria) L(ogiciel) L(ibre)

+
+
Version 1.0 du 2006-09-05.
+ + diff --git a/data/texts/LoginLicence.html b/data/texts/LoginLicence.html new file mode 100644 index 0000000..b6f1e61 --- /dev/null +++ b/data/texts/LoginLicence.html @@ -0,0 +1,653 @@ + + + + + CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B + + + + +

CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B

+ +
+

Avertissement

+ +

Ce contrat est une licence de logiciel libre issue d'une + concertation entre ses auteurs afin que le respect de deux + grands principes préside à sa rédaction:

+ + +

Les auteurs de la licence + CeCILL-B1 sont:

+ +

Commissariat à l'Energie Atomique - CEA, établissement public + de recherche à caractère scientifique, technique et + industriel, dont le siège est situé 25 rue Leblanc, immeuble + Le Ponant D, 75015 Paris.

+

Centre National de la Recherche Scientifique - CNRS, + établissement public à caractère scientifique et + technologique, dont le siège est situé 3 rue Michel-Ange, + 75794 Paris cedex 16.

+ +

Institut National de Recherche en Informatique et en + Automatique - INRIA, établissement public à caractère + scientifique et technologique, dont le siège est situé Domaine + de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay cedex.

+ +
+
+

Préambule

+

Ce contrat est une licence de logiciel libre dont l'objectif + est de conférer aux utilisateurs une très large liberté de + modification et de redistribution du logiciel régi par cette + licence.

+

L'exercice de cette liberté est assorti d'une obligation + forte de citation à la charge de ceux qui distribueraient un + logiciel incorporant un logiciel régi par la présente licence + afin d'assurer que les contributions de tous soient + correctement identifiées et reconnues.

+

L'accessibilité au code source et les droits de copie, de + modification et de redistribution qui découlent de ce contrat + ont pour contrepartie de n'offrir aux utilisateurs qu'une + garantie limitée et de ne faire peser sur l'auteur du + logiciel, le titulaire des droits patrimoniaux et les + concédants successifs qu'une responsabilité restreinte.

+

A cet égard l'attention de l'utilisateur est attirée sur les + risques associés au chargement, à l'utilisation, à la + modification et/ou au développement et à la reproduction du + logiciel par l'utilisateur étant donné sa spécificité de + logiciel libre, qui peut le rendre complexe à manipuler et qui + le réserve donc à des développeurs ou des professionnels + avertis possédant des connaissances informatiques + approfondies. Les utilisateurs sont donc invités à charger et + tester l'adéquation du logiciel à leurs besoins dans des + conditions permettant d'assurer la sécurité de leurs systèmes + et/ou de leurs données et, plus généralement, à l'utiliser et + l'exploiter dans les mêmes conditions de sécurité. Ce contrat + peut être reproduit et diffusé librement, sous réserve de le + conserver en l'état, sans ajout ni suppression de + clauses.

+

Ce contrat est susceptible de s'appliquer à tout logiciel + dont le titulaire des droits patrimoniaux décide de soumettre + l'exploitation aux dispositions qu'il contient.

+ +
+
+

Article 1 - DEFINITIONS

+

Dans ce contrat, les termes suivants, lorsqu'ils seront + écrits avec une lettre capitale, auront la signification + suivante:

+

Contrat: + désigne le présent contrat de licence, ses éventuelles + versions postérieures et annexes.

+

Logiciel: + désigne le logiciel sous sa forme de Code Objet et/ou de Code + Source et le cas échéant sa documentation, dans leur état au + moment de l'acceptation du Contrat par le Licencié.

+

Logiciel + Initial: désigne le Logiciel sous sa forme de Code + Source et éventuellement de Code Objet et le cas échéant sa + documentation, dans leur état au moment de leur première + diffusion sous les termes du Contrat.

+

Logiciel + Modifié: désigne le Logiciel modifié par au moins une + Contribution.

+

Code + Source: désigne l'ensemble des instructions et des + lignes de programme du Logiciel et auquel l'accès est + nécessaire en vue de modifier le Logiciel.

+

Code + Objet: désigne les fichiers binaires issus de la + compilation du Code Source.

+

Titulaire: + désigne le ou les détenteurs des droits patrimoniaux d'auteur + sur le Logiciel Initial.

+

Licencié: + désigne le ou les utilisateurs du Logiciel ayant accepté le + Contrat.

+

Contributeur: + désigne le Licencié auteur d'au moins une Contribution.

+

Concédant: + désigne le Titulaire ou toute personne physique ou morale + distribuant le Logiciel sous le Contrat.

+

Contribution: + désigne l'ensemble des modifications, corrections, + traductions, adaptations et/ou nouvelles fonctionnalités + intégrées dans le Logiciel par tout Contributeur, ainsi que + tout Module Interne.

+

Module: + désigne un ensemble de fichiers sources y compris leur + documentation qui permet de réaliser des fonctionnalités ou + services supplémentaires à ceux fournis par le Logiciel.

+

Module + Externe: désigne tout Module, non dérivé du Logiciel, tel + que ce Module et le Logiciel s'exécutent dans des espaces + d'adressage différents, l'un appelant l'autre au moment de leur + exécution.

+

Module + Interne: désigne tout Module lié au Logiciel de telle + sorte qu'ils s'exécutent dans le même espace + d'adressage.

+

Parties: + désigne collectivement le Licencié et le Concédant.

+

Ces termes s'entendent au singulier comme au pluriel.

+
+
+

Article 2 - OBJET

+

Le Contrat a pour objet la concession par le Concédant au + Licencié d'une licence non exclusive, cessible et mondiale du + Logiciel telle que définie ci-après à + l'article 5 + pour toute la durée de protection des droits portant sur ce + Logiciel.

+
+
+

Article 3 - ACCEPTATION

+
+

3.1 + L'acceptation par le Licencié des termes du Contrat est + réputée acquise du fait du premier des faits suivants:

+ +
+
+

3.2 Un exemplaire du + Contrat, contenant notamment un avertissement relatif aux + spécificités du Logiciel, à la restriction de garantie et à + la limitation à un usage par des utilisateurs expérimentés a + été mis à disposition du Licencié préalablement à son + acceptation telle que définie à + l'article 3.1 + ci dessus et le Licencié reconnaît en avoir pris + connaissance.

+
+
+
+

Article 4 - ENTREE EN VIGUEUR ET DUREE

+
+

4.1 ENTREE EN VIGUEUR

+

Le Contrat entre en vigueur à la date de son acceptation + par le Licencié telle que définie + en 3.1.

+
+
+

4.2 DUREE

+

Le Contrat produira ses effets pendant toute la durée + légale de protection des droits patrimoniaux portant sur le + Logiciel.

+
+
+
+

+ Article 5 - ETENDUE DES DROITS + CONCEDES

+

Le Concédant concède au Licencié, qui accepte, les droits + suivants sur le Logiciel pour toutes destinations et pour la + durée du Contrat dans les conditions ci-après + détaillées.

+

Par ailleurs, si le Concédant détient ou venait à détenir un + ou plusieurs brevets d'invention protégeant tout ou partie des + fonctionnalités du Logiciel ou de ses composants, il s'engage + à ne pas opposer les éventuels droits conférés par ces brevets + aux Licenciés successifs qui utiliseraient, exploiteraient ou + modifieraient le Logiciel. En cas de cession de ces brevets, + le Concédant s'engage à faire reprendre les obligations du + présent alinéa aux cessionnaires.

+
+

5.1 DROIT + D'UTILISATION

+ +

Le Licencié est autorisé à utiliser le Logiciel, sans + restriction quant aux domaines d'application, étant ci-après + précisé que cela comporte:

+
    +
  1. la reproduction permanente ou provisoire du Logiciel + en tout ou partie par tout moyen et sous toute + forme.

  2. +
  3. le chargement, l'affichage, l'exécution, ou le + stockage du Logiciel sur tout support.

  4. +
  5. la possibilité d'en observer, d'en étudier, ou d'en + tester le fonctionnement afin de déterminer les idées et + principes qui sont à la base de n'importe quel élément + de ce Logiciel; et ceci, lorsque le Licencié effectue + toute opération de chargement, d'affichage, d'exécution, + de transmission ou de stockage du Logiciel qu'il est en + droit d'effectuer en vertu du Contrat.

  6. +
+
+
+

5.2 DROIT D'APPORTER DES + CONTRIBUTIONS

+

Le droit d'apporter des Contributions comporte le droit de + traduire, d'adapter, d'arranger ou d'apporter toute autre + modification au Logiciel et le droit de reproduire le + logiciel en résultant.

+

Le Licencié est autorisé à apporter toute Contribution au + Logiciel sous réserve de mentionner, de façon explicite, son + nom en tant qu'auteur de cette Contribution et la date de + création de celle-ci.

+
+
+

5.3 DROIT DE + DISTRIBUTION

+

Le droit de distribution comporte notamment le droit de + diffuser, de transmettre et de communiquer le Logiciel au + public sur tout support et par tout moyen ainsi que le droit + de mettre sur le marché à titre onéreux ou gratuit, un ou + des exemplaires du Logiciel par tout procédé.

+

Le Licencié est autorisé à distribuer des copies du + Logiciel, modifié ou non, à des tiers dans les conditions + ci-après détaillées.

+
+

5.3.1 DISTRIBUTION DU + LOGICIEL SANS MODIFICATION

+

Le Licencié est autorisé à distribuer des copies + conformes du Logiciel, sous forme de Code Source ou de + Code Objet, à condition que cette distribution respecte + les dispositions du Contrat dans leur totalité et soit + accompagnée:

+
    +
  1. d'un exemplaire du Contrat,

  2. +
  3. d'un avertissement relatif à la restriction de + garantie et de responsabilité du Concédant telle que + prévue aux + articles 8 + et 9,

  4. +
+

et que, dans le cas où seul le Code Objet du Logiciel est + redistribué, le Licencié permette un accès effectif au + Code Source complet du Logiciel pendant au moins toute la + durée de sa distribution du Logiciel, étant entendu que le + coût additionnel d'acquisition du Code Source ne devra pas + excéder le simple coût de transfert des données.

+
+
+

+ 5.3.2 DISTRIBUTION DU LOGICIEL MODIFIE

+

Lorsque le Licencié apporte une Contribution au Logiciel, + le Logiciel Modifié peut être distribué sous un contrat de + licence autre que le présent Contrat sous réserve du + respect des dispositions de l'article + 5.3.4.

+
+
+

5.3.3 DISTRIBUTION DES + MODULES EXTERNES

+

Lorsque le Licencié a développé un Module Externe les + conditions du Contrat ne s'appliquent pas à ce Module + Externe, qui peut être distribué sous un contrat de + licence différent.

+
+
+

5.3.4 CITATIONS

+

Le Licencié qui distribue un Logiciel Modifié s'engage + expressément:

+
    +
  1. à indiquer dans sa documentation qu'il a été réalisé + à partir du Logiciel régi par le Contrat, en + reproduisant les mentions de propriété intellectuelle + du Logiciel,

  2. + +
  3. à faire en sorte que l'utilisation du Logiciel, ses + mentions de propriété intellectuelle et le fait qu'il + est régi par le Contrat soient indiqués dans un texte + facilement accessible depuis l'interface du Logiciel + Modifié,

  4. +
  5. à mentionner, sur un site Web librement accessible + décrivant le Logiciel Modifié, et pendant au moins + toute la durée de sa distribution, qu'il a été réalisé + à partir du Logiciel régi par le Contrat, en + reproduisant les mentions de propriété intellectuelle + du Logiciel,

  6. +
  7. lorsqu'il le distribue à un tiers susceptible de + distribuer lui-même un Logiciel Modifié, sans avoir à + en distribuer le code source, à faire ses meilleurs + efforts pour que les obligations du présent + article 5.3.4 + soient reprises par le dit tiers.

  8. +
+

Lorsque le Logiciel modifié ou non est distribué avec un + Module Externe qui a été conçu pour l'utiliser, le + Licencié doit soumettre le dit Module Externe aux + obligations précédentes.

+
+
+

5.3.5 COMPATIBILITE + AVEC LES LICENCES CeCILL et CeCILL-C

+

Lorsqu'un Logiciel Modifié contient une Contribution + soumise au contrat de licence CeCILL, les stipulations + prévues à + l'article 5.3.4 + sont facultatives.

+

Un Logiciel Modifié peut être distribué sous le contrat + de licence CeCILL-C. Les stipulations prévues à + l'article 5.3.4 + sont alors facultatives.

+
+
+
+
+

Article 6 - PROPRIETE INTELLECTUELLE

+
+

6.1 SUR LE LOGICIEL + INITIAL

+

Le Titulaire est détenteur des droits patrimoniaux sur le + Logiciel Initial. Toute utilisation du Logiciel Initial est + soumise au respect des conditions dans lesquelles le + Titulaire a choisi de diffuser son oeuvre et nul autre n'a + la faculté de modifier les conditions de diffusion de ce + Logiciel Initial.

+

Le Titulaire s'engage à ce que le Logiciel Initial reste au + moins régi par le Contrat et ce, pour la durée visée à + l'article 4.2.

+
+
+

6.2 SUR LES + CONTRIBUTIONS

+

Le Licencié qui a développé une Contribution est titulaire + sur celle-ci des droits de propriété intellectuelle dans les + conditions définies par la législation applicable.

+
+
+

6.3 SUR LES MODULES + EXTERNES

+

Le Licencié qui a développé un Module Externe est + titulaire sur celui-ci des droits de propriété + intellectuelle dans les conditions définies par la + législation applicable et reste libre du choix du contrat + régissant sa diffusion.

+
+
+

6.4 DISPOSITIONS + COMMUNES

+
+

Le Licencié s'engage expressément:

+
    +
  1. à ne pas supprimer ou modifier de quelque manière + que ce soit les mentions de propriété intellectuelle + apposées sur le Logiciel;

  2. +
  3. à reproduire à l'identique lesdites mentions de + propriété intellectuelle sur les copies du Logiciel + modifié ou non.

  4. +
+
+
+

Le Licencié s'engage à ne pas porter atteinte, + directement ou indirectement, aux droits de propriété + intellectuelle du Titulaire et/ou des Contributeurs sur le + Logiciel et à prendre, le cas échéant, à l'égard de son + personnel toutes les mesures nécessaires pour assurer le + respect des dits droits de propriété intellectuelle du + Titulaire et/ou des Contributeurs.

+
+
+
+
+

Article 7 - SERVICES ASSOCIES

+
+

7.1 Le Contrat n'oblige en + aucun cas le Concédant à la réalisation de prestations + d'assistance technique ou de maintenance du Logiciel.

+

Cependant le Concédant reste libre de proposer ce type de + services. Les termes et conditions d'une telle assistance + technique et/ou d'une telle maintenance seront alors + déterminés dans un acte séparé. Ces actes de maintenance + et/ou assistance technique n'engageront que la seule + responsabilité du Concédant qui les propose.

+
+
+

7.2 De même, tout Concédant + est libre de proposer, sous sa seule responsabilité, à ses + licenciés une garantie, qui n'engagera que lui, lors de la + redistribution du Logiciel et/ou du Logiciel Modifié et ce, + dans les conditions qu'il souhaite. Cette garantie et les + modalités financières de son application feront l'objet d'un + acte séparé entre le Concédant et le Licencié.

+
+
+
+

+ Article 8 - + RESPONSABILITE

+
+

8.1 Sous réserve des + dispositions de + l'article 8.2, + le Licencié a la faculté, sous réserve de prouver la faute + du Concédant concerné, de solliciter la réparation du + préjudice direct qu'il subirait du fait du Logiciel et dont + il apportera la preuve.

+
+
+

8.2 + La responsabilité du Concédant est limitée aux engagements + pris en application du Contrat et ne saurait être engagée en + raison notamment: (i) des dommages dus à l'inexécution, + totale ou partielle, de ses obligations par le Licencié, + (ii) des dommages directs ou indirects découlant de + l'utilisation ou des performances du Logiciel subis par le + Licencié et (iii) plus généralement d'un quelconque dommage + indirect. En particulier, les Parties conviennent + expressément que tout préjudice financier ou commercial (par + exemple perte de données, perte de bénéfices, perte + d'exploitation, perte de clientèle ou de commandes, manque à + gagner, trouble commercial quelconque) ou toute action + dirigée contre le Licencié par un tiers, constitue un + dommage indirect et n'ouvre pas droit à réparation par le + Concédant.

+
+
+
+

+ Article 9 - GARANTIE

+
+

9.1 Le Licencié reconnaît + que l'état actuel des connaissances scientifiques et + techniques au moment de la mise en circulation du Logiciel + ne permet pas d'en tester et d'en vérifier toutes les + utilisations ni de détecter l'existence d'éventuels défauts. + L'attention du Licencié a été attirée sur ce point sur les + risques associés au chargement, à l'utilisation, la + modification et/ou au développement et à la reproduction du + Logiciel qui sont réservés à des utilisateurs avertis.

+

Il relève de la responsabilité du Licencié de contrôler, + par tous moyens, l'adéquation du produit à ses besoins, son + bon fonctionnement et de s'assurer qu'il ne causera pas de + dommages aux personnes et aux biens.

+
+
+

9.2 + Le Concédant déclare de bonne foi être en droit de concéder + l'ensemble des droits attachés au Logiciel (comprenant + notamment les droits visés à l'article + 5).

+
+
+

9.3 Le Licencié reconnaît + que le Logiciel est fourni "en l'état" par le Concédant sans + autre garantie, expresse ou tacite, que celle prévue à + l'article 9.2 + et notamment sans aucune garantie sur sa valeur commerciale, + son caractère sécurisé, innovant ou pertinent.

+

En particulier, le Concédant ne garantit pas que le + Logiciel est exempt d'erreur, qu'il fonctionnera sans + interruption, qu'il sera compatible avec l'équipement du + Licencié et sa configuration logicielle ni qu'il remplira + les besoins du Licencié.

+
+
+

9.4 Le Concédant ne garantit + pas, de manière expresse ou tacite, que le Logiciel ne porte + pas atteinte à un quelconque droit de propriété + intellectuelle d'un tiers portant sur un brevet, un logiciel + ou sur tout autre droit de propriété. Ainsi, le Concédant + exclut toute garantie au profit du Licencié contre les + actions en contrefaçon qui pourraient être diligentées au + titre de l'utilisation, de la modification, et de la + redistribution du Logiciel. Néanmoins, si de telles actions + sont exercées contre le Licencié, le Concédant lui apportera + son aide technique et juridique pour sa défense. Cette aide + technique et juridique est déterminée au cas par cas entre + le Concédant concerné et le Licencié dans le cadre d'un + protocole d'accord. Le Concédant dégage toute responsabilité + quant à l'utilisation de la dénomination du Logiciel par le + Licencié. Aucune garantie n'est apportée quant à l'existence + de droits antérieurs sur le nom du Logiciel et sur + l'existence d'une marque.

+
+
+
+

Article 10 - + RESILIATION

+
+

10.1 En cas de manquement + par le Licencié aux obligations mises à sa charge par le + Contrat, le Concédant pourra résilier de plein droit le + Contrat trente (30) jours après notification adressée au + Licencié et restée sans effet.

+
+
+

10.2 Le Licencié dont le + Contrat est résilié n'est plus autorisé à utiliser, modifier + ou distribuer le Logiciel. Cependant, toutes les licences + qu'il aura concédées antérieurement à la résiliation du + Contrat resteront valides sous réserve qu'elles aient été + effectuées en conformité avec le Contrat.

+
+
+
+

Article 11 - DISPOSITIONS + DIVERSES

+
+

+ 11.1 CAUSE EXTERIEURE

+

Aucune + des Parties ne sera responsable d'un retard ou d'une + défaillance d'exécution du Contrat qui serait dû + à un cas de force majeure, un cas fortuit ou une cause + extérieure, telle que, notamment, le mauvais fonctionnement + ou les interruptions du réseau électrique ou de + télécommunication, la paralysie du réseau liée + à une attaque informatique, l'intervention des + autorités gouvernementales, les catastrophes naturelles, les + dégâts des eaux, les tremblements de terre, le feu, les + explosions, les grèves et les conflits sociaux, l'état + de guerre...

+
+
+

11.2 Le + fait, par l'une ou l'autre des Parties, d'omettre + en une ou plusieurs occasions de se prévaloir d'une ou + plusieurs dispositions du Contrat, ne pourra en aucun cas impliquer + renonciation par la Partie intéressée à s'en + prévaloir ultérieurement.

+
+
+

11.3 Le Contrat annule et + remplace toute convention antérieure, écrite ou orale, entre + les Parties sur le même objet et constitue l'accord entier + entre les Parties sur cet objet. Aucune addition ou + modification aux termes du Contrat n'aura d'effet à l'égard + des Parties à moins d'être faite par écrit et signée par + leurs représentants dûment habilités.

+
+
+

11.4 Dans l'hypothèse où une + ou plusieurs des dispositions du Contrat s'avèrerait + contraire à une loi ou à un texte applicable, existants ou + futurs, cette loi ou ce texte prévaudrait, et les Parties + feraient les amendements nécessaires pour se conformer à + cette loi ou à ce texte. Toutes les autres dispositions + resteront en vigueur. De même, la nullité, pour quelque + raison que ce soit, d'une des dispositions du Contrat ne + saurait entraîner la nullité de l'ensemble du Contrat.

+
+
+

+ 11.5 LANGUE

+

Le Contrat est rédigé en langue française et en langue + anglaise, ces deux versions faisant également foi. +

+
+
+
+

Article 12 - NOUVELLES + VERSIONS DU CONTRAT

+
+

12.1 Toute personne est + autorisée à copier et distribuer des copies de ce + Contrat.

+
+
+

12.2 Afin d'en préserver la + cohérence, le texte du Contrat est protégé et ne peut être + modifié que par les auteurs de la licence, lesquels se + réservent le droit de publier périodiquement des mises à + jour ou de nouvelles versions du Contrat, qui posséderont + chacune un numéro distinct. Ces versions ultérieures seront + susceptibles de prendre en compte de nouvelles + problématiques rencontrées par les logiciels libres.

+
+
+

12.3 Tout Logiciel diffusé + sous une version donnée du Contrat ne pourra faire l'objet + d'une diffusion ultérieure que sous la même version du + Contrat ou une version postérieure.

+
+
+
+

Article 13 - LOI APPLICABLE + ET COMPETENCE TERRITORIALE

+
+

13.1 Le Contrat est régi + par la loi française. Les Parties conviennent de tenter de + régler à l'amiable les différends ou litiges qui viendraient + à se produire par suite ou à l'occasion du Contrat. +

+
+
+

13.2 A défaut d'accord + amiable dans un délai de deux (2) mois à compter de leur + survenance et sauf situation relevant d'une procédure + d'urgence, les différends ou litiges seront portés par la + Partie la plus diligente devant les Tribunaux compétents de + Paris.

+
+
+
+

1 CeCILL est pour Ce(a) C(nrs) I(nria) L(ogiciel) L(ibre)

+
+
Version 1.0 du 2006-09-05.
+ + diff --git a/data/texts/MiscLicence.html b/data/texts/MiscLicence.html new file mode 100644 index 0000000..b6f1e61 --- /dev/null +++ b/data/texts/MiscLicence.html @@ -0,0 +1,653 @@ + + + + + CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B + + + + +

CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B

+ +
+

Avertissement

+ +

Ce contrat est une licence de logiciel libre issue d'une + concertation entre ses auteurs afin que le respect de deux + grands principes préside à sa rédaction:

+ + +

Les auteurs de la licence + CeCILL-B1 sont:

+ +

Commissariat à l'Energie Atomique - CEA, établissement public + de recherche à caractère scientifique, technique et + industriel, dont le siège est situé 25 rue Leblanc, immeuble + Le Ponant D, 75015 Paris.

+

Centre National de la Recherche Scientifique - CNRS, + établissement public à caractère scientifique et + technologique, dont le siège est situé 3 rue Michel-Ange, + 75794 Paris cedex 16.

+ +

Institut National de Recherche en Informatique et en + Automatique - INRIA, établissement public à caractère + scientifique et technologique, dont le siège est situé Domaine + de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay cedex.

+ +
+
+

Préambule

+

Ce contrat est une licence de logiciel libre dont l'objectif + est de conférer aux utilisateurs une très large liberté de + modification et de redistribution du logiciel régi par cette + licence.

+

L'exercice de cette liberté est assorti d'une obligation + forte de citation à la charge de ceux qui distribueraient un + logiciel incorporant un logiciel régi par la présente licence + afin d'assurer que les contributions de tous soient + correctement identifiées et reconnues.

+

L'accessibilité au code source et les droits de copie, de + modification et de redistribution qui découlent de ce contrat + ont pour contrepartie de n'offrir aux utilisateurs qu'une + garantie limitée et de ne faire peser sur l'auteur du + logiciel, le titulaire des droits patrimoniaux et les + concédants successifs qu'une responsabilité restreinte.

+

A cet égard l'attention de l'utilisateur est attirée sur les + risques associés au chargement, à l'utilisation, à la + modification et/ou au développement et à la reproduction du + logiciel par l'utilisateur étant donné sa spécificité de + logiciel libre, qui peut le rendre complexe à manipuler et qui + le réserve donc à des développeurs ou des professionnels + avertis possédant des connaissances informatiques + approfondies. Les utilisateurs sont donc invités à charger et + tester l'adéquation du logiciel à leurs besoins dans des + conditions permettant d'assurer la sécurité de leurs systèmes + et/ou de leurs données et, plus généralement, à l'utiliser et + l'exploiter dans les mêmes conditions de sécurité. Ce contrat + peut être reproduit et diffusé librement, sous réserve de le + conserver en l'état, sans ajout ni suppression de + clauses.

+

Ce contrat est susceptible de s'appliquer à tout logiciel + dont le titulaire des droits patrimoniaux décide de soumettre + l'exploitation aux dispositions qu'il contient.

+ +
+
+

Article 1 - DEFINITIONS

+

Dans ce contrat, les termes suivants, lorsqu'ils seront + écrits avec une lettre capitale, auront la signification + suivante:

+

Contrat: + désigne le présent contrat de licence, ses éventuelles + versions postérieures et annexes.

+

Logiciel: + désigne le logiciel sous sa forme de Code Objet et/ou de Code + Source et le cas échéant sa documentation, dans leur état au + moment de l'acceptation du Contrat par le Licencié.

+

Logiciel + Initial: désigne le Logiciel sous sa forme de Code + Source et éventuellement de Code Objet et le cas échéant sa + documentation, dans leur état au moment de leur première + diffusion sous les termes du Contrat.

+

Logiciel + Modifié: désigne le Logiciel modifié par au moins une + Contribution.

+

Code + Source: désigne l'ensemble des instructions et des + lignes de programme du Logiciel et auquel l'accès est + nécessaire en vue de modifier le Logiciel.

+

Code + Objet: désigne les fichiers binaires issus de la + compilation du Code Source.

+

Titulaire: + désigne le ou les détenteurs des droits patrimoniaux d'auteur + sur le Logiciel Initial.

+

Licencié: + désigne le ou les utilisateurs du Logiciel ayant accepté le + Contrat.

+

Contributeur: + désigne le Licencié auteur d'au moins une Contribution.

+

Concédant: + désigne le Titulaire ou toute personne physique ou morale + distribuant le Logiciel sous le Contrat.

+

Contribution: + désigne l'ensemble des modifications, corrections, + traductions, adaptations et/ou nouvelles fonctionnalités + intégrées dans le Logiciel par tout Contributeur, ainsi que + tout Module Interne.

+

Module: + désigne un ensemble de fichiers sources y compris leur + documentation qui permet de réaliser des fonctionnalités ou + services supplémentaires à ceux fournis par le Logiciel.

+

Module + Externe: désigne tout Module, non dérivé du Logiciel, tel + que ce Module et le Logiciel s'exécutent dans des espaces + d'adressage différents, l'un appelant l'autre au moment de leur + exécution.

+

Module + Interne: désigne tout Module lié au Logiciel de telle + sorte qu'ils s'exécutent dans le même espace + d'adressage.

+

Parties: + désigne collectivement le Licencié et le Concédant.

+

Ces termes s'entendent au singulier comme au pluriel.

+
+
+

Article 2 - OBJET

+

Le Contrat a pour objet la concession par le Concédant au + Licencié d'une licence non exclusive, cessible et mondiale du + Logiciel telle que définie ci-après à + l'article 5 + pour toute la durée de protection des droits portant sur ce + Logiciel.

+
+
+

Article 3 - ACCEPTATION

+
+

3.1 + L'acceptation par le Licencié des termes du Contrat est + réputée acquise du fait du premier des faits suivants:

+ +
+
+

3.2 Un exemplaire du + Contrat, contenant notamment un avertissement relatif aux + spécificités du Logiciel, à la restriction de garantie et à + la limitation à un usage par des utilisateurs expérimentés a + été mis à disposition du Licencié préalablement à son + acceptation telle que définie à + l'article 3.1 + ci dessus et le Licencié reconnaît en avoir pris + connaissance.

+
+
+
+

Article 4 - ENTREE EN VIGUEUR ET DUREE

+
+

4.1 ENTREE EN VIGUEUR

+

Le Contrat entre en vigueur à la date de son acceptation + par le Licencié telle que définie + en 3.1.

+
+
+

4.2 DUREE

+

Le Contrat produira ses effets pendant toute la durée + légale de protection des droits patrimoniaux portant sur le + Logiciel.

+
+
+
+

+ Article 5 - ETENDUE DES DROITS + CONCEDES

+

Le Concédant concède au Licencié, qui accepte, les droits + suivants sur le Logiciel pour toutes destinations et pour la + durée du Contrat dans les conditions ci-après + détaillées.

+

Par ailleurs, si le Concédant détient ou venait à détenir un + ou plusieurs brevets d'invention protégeant tout ou partie des + fonctionnalités du Logiciel ou de ses composants, il s'engage + à ne pas opposer les éventuels droits conférés par ces brevets + aux Licenciés successifs qui utiliseraient, exploiteraient ou + modifieraient le Logiciel. En cas de cession de ces brevets, + le Concédant s'engage à faire reprendre les obligations du + présent alinéa aux cessionnaires.

+
+

5.1 DROIT + D'UTILISATION

+ +

Le Licencié est autorisé à utiliser le Logiciel, sans + restriction quant aux domaines d'application, étant ci-après + précisé que cela comporte:

+
    +
  1. la reproduction permanente ou provisoire du Logiciel + en tout ou partie par tout moyen et sous toute + forme.

  2. +
  3. le chargement, l'affichage, l'exécution, ou le + stockage du Logiciel sur tout support.

  4. +
  5. la possibilité d'en observer, d'en étudier, ou d'en + tester le fonctionnement afin de déterminer les idées et + principes qui sont à la base de n'importe quel élément + de ce Logiciel; et ceci, lorsque le Licencié effectue + toute opération de chargement, d'affichage, d'exécution, + de transmission ou de stockage du Logiciel qu'il est en + droit d'effectuer en vertu du Contrat.

  6. +
+
+
+

5.2 DROIT D'APPORTER DES + CONTRIBUTIONS

+

Le droit d'apporter des Contributions comporte le droit de + traduire, d'adapter, d'arranger ou d'apporter toute autre + modification au Logiciel et le droit de reproduire le + logiciel en résultant.

+

Le Licencié est autorisé à apporter toute Contribution au + Logiciel sous réserve de mentionner, de façon explicite, son + nom en tant qu'auteur de cette Contribution et la date de + création de celle-ci.

+
+
+

5.3 DROIT DE + DISTRIBUTION

+

Le droit de distribution comporte notamment le droit de + diffuser, de transmettre et de communiquer le Logiciel au + public sur tout support et par tout moyen ainsi que le droit + de mettre sur le marché à titre onéreux ou gratuit, un ou + des exemplaires du Logiciel par tout procédé.

+

Le Licencié est autorisé à distribuer des copies du + Logiciel, modifié ou non, à des tiers dans les conditions + ci-après détaillées.

+
+

5.3.1 DISTRIBUTION DU + LOGICIEL SANS MODIFICATION

+

Le Licencié est autorisé à distribuer des copies + conformes du Logiciel, sous forme de Code Source ou de + Code Objet, à condition que cette distribution respecte + les dispositions du Contrat dans leur totalité et soit + accompagnée:

+
    +
  1. d'un exemplaire du Contrat,

  2. +
  3. d'un avertissement relatif à la restriction de + garantie et de responsabilité du Concédant telle que + prévue aux + articles 8 + et 9,

  4. +
+

et que, dans le cas où seul le Code Objet du Logiciel est + redistribué, le Licencié permette un accès effectif au + Code Source complet du Logiciel pendant au moins toute la + durée de sa distribution du Logiciel, étant entendu que le + coût additionnel d'acquisition du Code Source ne devra pas + excéder le simple coût de transfert des données.

+
+
+

+ 5.3.2 DISTRIBUTION DU LOGICIEL MODIFIE

+

Lorsque le Licencié apporte une Contribution au Logiciel, + le Logiciel Modifié peut être distribué sous un contrat de + licence autre que le présent Contrat sous réserve du + respect des dispositions de l'article + 5.3.4.

+
+
+

5.3.3 DISTRIBUTION DES + MODULES EXTERNES

+

Lorsque le Licencié a développé un Module Externe les + conditions du Contrat ne s'appliquent pas à ce Module + Externe, qui peut être distribué sous un contrat de + licence différent.

+
+
+

5.3.4 CITATIONS

+

Le Licencié qui distribue un Logiciel Modifié s'engage + expressément:

+
    +
  1. à indiquer dans sa documentation qu'il a été réalisé + à partir du Logiciel régi par le Contrat, en + reproduisant les mentions de propriété intellectuelle + du Logiciel,

  2. + +
  3. à faire en sorte que l'utilisation du Logiciel, ses + mentions de propriété intellectuelle et le fait qu'il + est régi par le Contrat soient indiqués dans un texte + facilement accessible depuis l'interface du Logiciel + Modifié,

  4. +
  5. à mentionner, sur un site Web librement accessible + décrivant le Logiciel Modifié, et pendant au moins + toute la durée de sa distribution, qu'il a été réalisé + à partir du Logiciel régi par le Contrat, en + reproduisant les mentions de propriété intellectuelle + du Logiciel,

  6. +
  7. lorsqu'il le distribue à un tiers susceptible de + distribuer lui-même un Logiciel Modifié, sans avoir à + en distribuer le code source, à faire ses meilleurs + efforts pour que les obligations du présent + article 5.3.4 + soient reprises par le dit tiers.

  8. +
+

Lorsque le Logiciel modifié ou non est distribué avec un + Module Externe qui a été conçu pour l'utiliser, le + Licencié doit soumettre le dit Module Externe aux + obligations précédentes.

+
+
+

5.3.5 COMPATIBILITE + AVEC LES LICENCES CeCILL et CeCILL-C

+

Lorsqu'un Logiciel Modifié contient une Contribution + soumise au contrat de licence CeCILL, les stipulations + prévues à + l'article 5.3.4 + sont facultatives.

+

Un Logiciel Modifié peut être distribué sous le contrat + de licence CeCILL-C. Les stipulations prévues à + l'article 5.3.4 + sont alors facultatives.

+
+
+
+
+

Article 6 - PROPRIETE INTELLECTUELLE

+
+

6.1 SUR LE LOGICIEL + INITIAL

+

Le Titulaire est détenteur des droits patrimoniaux sur le + Logiciel Initial. Toute utilisation du Logiciel Initial est + soumise au respect des conditions dans lesquelles le + Titulaire a choisi de diffuser son oeuvre et nul autre n'a + la faculté de modifier les conditions de diffusion de ce + Logiciel Initial.

+

Le Titulaire s'engage à ce que le Logiciel Initial reste au + moins régi par le Contrat et ce, pour la durée visée à + l'article 4.2.

+
+
+

6.2 SUR LES + CONTRIBUTIONS

+

Le Licencié qui a développé une Contribution est titulaire + sur celle-ci des droits de propriété intellectuelle dans les + conditions définies par la législation applicable.

+
+
+

6.3 SUR LES MODULES + EXTERNES

+

Le Licencié qui a développé un Module Externe est + titulaire sur celui-ci des droits de propriété + intellectuelle dans les conditions définies par la + législation applicable et reste libre du choix du contrat + régissant sa diffusion.

+
+
+

6.4 DISPOSITIONS + COMMUNES

+
+

Le Licencié s'engage expressément:

+
    +
  1. à ne pas supprimer ou modifier de quelque manière + que ce soit les mentions de propriété intellectuelle + apposées sur le Logiciel;

  2. +
  3. à reproduire à l'identique lesdites mentions de + propriété intellectuelle sur les copies du Logiciel + modifié ou non.

  4. +
+
+
+

Le Licencié s'engage à ne pas porter atteinte, + directement ou indirectement, aux droits de propriété + intellectuelle du Titulaire et/ou des Contributeurs sur le + Logiciel et à prendre, le cas échéant, à l'égard de son + personnel toutes les mesures nécessaires pour assurer le + respect des dits droits de propriété intellectuelle du + Titulaire et/ou des Contributeurs.

+
+
+
+
+

Article 7 - SERVICES ASSOCIES

+
+

7.1 Le Contrat n'oblige en + aucun cas le Concédant à la réalisation de prestations + d'assistance technique ou de maintenance du Logiciel.

+

Cependant le Concédant reste libre de proposer ce type de + services. Les termes et conditions d'une telle assistance + technique et/ou d'une telle maintenance seront alors + déterminés dans un acte séparé. Ces actes de maintenance + et/ou assistance technique n'engageront que la seule + responsabilité du Concédant qui les propose.

+
+
+

7.2 De même, tout Concédant + est libre de proposer, sous sa seule responsabilité, à ses + licenciés une garantie, qui n'engagera que lui, lors de la + redistribution du Logiciel et/ou du Logiciel Modifié et ce, + dans les conditions qu'il souhaite. Cette garantie et les + modalités financières de son application feront l'objet d'un + acte séparé entre le Concédant et le Licencié.

+
+
+
+

+ Article 8 - + RESPONSABILITE

+
+

8.1 Sous réserve des + dispositions de + l'article 8.2, + le Licencié a la faculté, sous réserve de prouver la faute + du Concédant concerné, de solliciter la réparation du + préjudice direct qu'il subirait du fait du Logiciel et dont + il apportera la preuve.

+
+
+

8.2 + La responsabilité du Concédant est limitée aux engagements + pris en application du Contrat et ne saurait être engagée en + raison notamment: (i) des dommages dus à l'inexécution, + totale ou partielle, de ses obligations par le Licencié, + (ii) des dommages directs ou indirects découlant de + l'utilisation ou des performances du Logiciel subis par le + Licencié et (iii) plus généralement d'un quelconque dommage + indirect. En particulier, les Parties conviennent + expressément que tout préjudice financier ou commercial (par + exemple perte de données, perte de bénéfices, perte + d'exploitation, perte de clientèle ou de commandes, manque à + gagner, trouble commercial quelconque) ou toute action + dirigée contre le Licencié par un tiers, constitue un + dommage indirect et n'ouvre pas droit à réparation par le + Concédant.

+
+
+
+

+ Article 9 - GARANTIE

+
+

9.1 Le Licencié reconnaît + que l'état actuel des connaissances scientifiques et + techniques au moment de la mise en circulation du Logiciel + ne permet pas d'en tester et d'en vérifier toutes les + utilisations ni de détecter l'existence d'éventuels défauts. + L'attention du Licencié a été attirée sur ce point sur les + risques associés au chargement, à l'utilisation, la + modification et/ou au développement et à la reproduction du + Logiciel qui sont réservés à des utilisateurs avertis.

+

Il relève de la responsabilité du Licencié de contrôler, + par tous moyens, l'adéquation du produit à ses besoins, son + bon fonctionnement et de s'assurer qu'il ne causera pas de + dommages aux personnes et aux biens.

+
+
+

9.2 + Le Concédant déclare de bonne foi être en droit de concéder + l'ensemble des droits attachés au Logiciel (comprenant + notamment les droits visés à l'article + 5).

+
+
+

9.3 Le Licencié reconnaît + que le Logiciel est fourni "en l'état" par le Concédant sans + autre garantie, expresse ou tacite, que celle prévue à + l'article 9.2 + et notamment sans aucune garantie sur sa valeur commerciale, + son caractère sécurisé, innovant ou pertinent.

+

En particulier, le Concédant ne garantit pas que le + Logiciel est exempt d'erreur, qu'il fonctionnera sans + interruption, qu'il sera compatible avec l'équipement du + Licencié et sa configuration logicielle ni qu'il remplira + les besoins du Licencié.

+
+
+

9.4 Le Concédant ne garantit + pas, de manière expresse ou tacite, que le Logiciel ne porte + pas atteinte à un quelconque droit de propriété + intellectuelle d'un tiers portant sur un brevet, un logiciel + ou sur tout autre droit de propriété. Ainsi, le Concédant + exclut toute garantie au profit du Licencié contre les + actions en contrefaçon qui pourraient être diligentées au + titre de l'utilisation, de la modification, et de la + redistribution du Logiciel. Néanmoins, si de telles actions + sont exercées contre le Licencié, le Concédant lui apportera + son aide technique et juridique pour sa défense. Cette aide + technique et juridique est déterminée au cas par cas entre + le Concédant concerné et le Licencié dans le cadre d'un + protocole d'accord. Le Concédant dégage toute responsabilité + quant à l'utilisation de la dénomination du Logiciel par le + Licencié. Aucune garantie n'est apportée quant à l'existence + de droits antérieurs sur le nom du Logiciel et sur + l'existence d'une marque.

+
+
+
+

Article 10 - + RESILIATION

+
+

10.1 En cas de manquement + par le Licencié aux obligations mises à sa charge par le + Contrat, le Concédant pourra résilier de plein droit le + Contrat trente (30) jours après notification adressée au + Licencié et restée sans effet.

+
+
+

10.2 Le Licencié dont le + Contrat est résilié n'est plus autorisé à utiliser, modifier + ou distribuer le Logiciel. Cependant, toutes les licences + qu'il aura concédées antérieurement à la résiliation du + Contrat resteront valides sous réserve qu'elles aient été + effectuées en conformité avec le Contrat.

+
+
+
+

Article 11 - DISPOSITIONS + DIVERSES

+
+

+ 11.1 CAUSE EXTERIEURE

+

Aucune + des Parties ne sera responsable d'un retard ou d'une + défaillance d'exécution du Contrat qui serait dû + à un cas de force majeure, un cas fortuit ou une cause + extérieure, telle que, notamment, le mauvais fonctionnement + ou les interruptions du réseau électrique ou de + télécommunication, la paralysie du réseau liée + à une attaque informatique, l'intervention des + autorités gouvernementales, les catastrophes naturelles, les + dégâts des eaux, les tremblements de terre, le feu, les + explosions, les grèves et les conflits sociaux, l'état + de guerre...

+
+
+

11.2 Le + fait, par l'une ou l'autre des Parties, d'omettre + en une ou plusieurs occasions de se prévaloir d'une ou + plusieurs dispositions du Contrat, ne pourra en aucun cas impliquer + renonciation par la Partie intéressée à s'en + prévaloir ultérieurement.

+
+
+

11.3 Le Contrat annule et + remplace toute convention antérieure, écrite ou orale, entre + les Parties sur le même objet et constitue l'accord entier + entre les Parties sur cet objet. Aucune addition ou + modification aux termes du Contrat n'aura d'effet à l'égard + des Parties à moins d'être faite par écrit et signée par + leurs représentants dûment habilités.

+
+
+

11.4 Dans l'hypothèse où une + ou plusieurs des dispositions du Contrat s'avèrerait + contraire à une loi ou à un texte applicable, existants ou + futurs, cette loi ou ce texte prévaudrait, et les Parties + feraient les amendements nécessaires pour se conformer à + cette loi ou à ce texte. Toutes les autres dispositions + resteront en vigueur. De même, la nullité, pour quelque + raison que ce soit, d'une des dispositions du Contrat ne + saurait entraîner la nullité de l'ensemble du Contrat.

+
+
+

+ 11.5 LANGUE

+

Le Contrat est rédigé en langue française et en langue + anglaise, ces deux versions faisant également foi. +

+
+
+
+

Article 12 - NOUVELLES + VERSIONS DU CONTRAT

+
+

12.1 Toute personne est + autorisée à copier et distribuer des copies de ce + Contrat.

+
+
+

12.2 Afin d'en préserver la + cohérence, le texte du Contrat est protégé et ne peut être + modifié que par les auteurs de la licence, lesquels se + réservent le droit de publier périodiquement des mises à + jour ou de nouvelles versions du Contrat, qui posséderont + chacune un numéro distinct. Ces versions ultérieures seront + susceptibles de prendre en compte de nouvelles + problématiques rencontrées par les logiciels libres.

+
+
+

12.3 Tout Logiciel diffusé + sous une version donnée du Contrat ne pourra faire l'objet + d'une diffusion ultérieure que sous la même version du + Contrat ou une version postérieure.

+
+
+
+

Article 13 - LOI APPLICABLE + ET COMPETENCE TERRITORIALE

+
+

13.1 Le Contrat est régi + par la loi française. Les Parties conviennent de tenter de + régler à l'amiable les différends ou litiges qui viendraient + à se produire par suite ou à l'occasion du Contrat. +

+
+
+

13.2 A défaut d'accord + amiable dans un délai de deux (2) mois à compter de leur + survenance et sauf situation relevant d'une procédure + d'urgence, les différends ou litiges seront portés par la + Partie la plus diligente devant les Tribunaux compétents de + Paris.

+
+
+
+

1 CeCILL est pour Ce(a) C(nrs) I(nria) L(ogiciel) L(ibre)

+
+
Version 1.0 du 2006-09-05.
+ + diff --git a/src/java/misc/ApplicationManager.java b/src/java/misc/ApplicationManager.java new file mode 100644 index 0000000..4fe4368 --- /dev/null +++ b/src/java/misc/ApplicationManager.java @@ -0,0 +1,12 @@ +package misc; + +import java.awt.Container; +import java.util.Hashtable; +import javax.swing.AbstractButton; +import javax.swing.JMenu; + +public interface ApplicationManager { + public void addMenuItem (JMenu... jMenu); + public void addIconButtons (Container... containers); + public void addActiveButtons (Hashtable buttons); +} diff --git a/src/java/misc/Bundle.java b/src/java/misc/Bundle.java new file mode 100644 index 0000000..c2ba267 --- /dev/null +++ b/src/java/misc/Bundle.java @@ -0,0 +1,532 @@ +package misc; + +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLConnection; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Properties; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.Vector; +import javax.swing.AbstractButton; +import javax.swing.BorderFactory; +import javax.swing.DefaultListCellRenderer; +import javax.swing.ImageIcon; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.border.TitledBorder; + +/** + Managed the internalization by using files properties and ResourceBundle. + This class could be used to managed all texts show in a application (buttons, menus, labels). + The files properties could be embedded in a jar (mix of class and data) or a dedicated directory. + The name file look like : applicationBundle[_langage[_COUNTRY[_VARIANT]]].properties . + Each file contains a "bundle" of key/value association. + The values are string represent message or formated sentences (with numbered holes to put variables values). +*/ +public class Bundle { + + static public final String FS = System.getProperty ("file.separator"); + + // = Constants ============================ + /** Bundle file extension. */ + static public final String bundleExt = ".properties"; + + /** Postfix for title. */ + static public final String titlePostfix = "Title"; + + /** Prefixe for all actioners (button, menus, menuItem) */ + static public final String actionPrefix = "Action"; + + /** XXX commentaire */ + static public final String labelPrefix = "Label"; + static public final String storyPrefix = "Story"; + + /** XXX commentaire */ + static public final String messagePrefix = "Message"; + + /** XXX commentaire */ + static public final String exceptionPrefix = "Exception"; + + /** XXX commentaire */ + static public final String userPrefix = "User_"; + + /** XXX commentaire */ + static public final String enumPrefix = "Enum"; + + /** Message shows when when try use bundle before load it. */ + static private final String bundleUseException = "Can''t find token \"{0}\" in Bundle. Continue?"; + static private final String bundleSaveException = "Bundle {0} can''t be save."; + + // = Attributs ============================ + static class ApplicationInfo { + public ResourceBundle messages; + public Hashtable patch = new Hashtable (); + public boolean modified; + public ApplicationInfo (ResourceBundle messages) { this.messages = messages; } + } + + /** Labels to bundle to find to display.*/ + static private Hashtable labels = new Hashtable (); + /** Applications bundle name to bundle. */ + static private Hashtable applications = new Hashtable (); + static private ArrayList applicationsOrder = new ArrayList (); + /** The current locale used to display messages. */ + static private Locale locale; + + + // = Accessors ============================ + /** + Accessors to locale. + @return current locale. + */ + static public Locale getLocale () { return locale; } + /** + Accesor to locale. + @param locale the new locale value. + */ + static public void setLocale (Locale locale) { + resetLocale (locale); + broadcastBundleReloaded (); + } + + static private final void initLocale () { + if (locale != null) + return; + Locale locale = Locale.getDefault (); + if (Config.getString ("Language") != null) + try { + locale = new Locale (Config.getString ("Language"), + Config.getString ("Country"), + Config.getString ("Variant")); + } catch (Exception e) { + } + resetLocale (locale); + } + + static private void resetLocale (Locale locale) { + if (Bundle.locale == locale || + (Bundle.locale != null && Bundle.locale.equals (locale))) + return; + Locale.setDefault (locale); + Bundle.locale = locale; + for (String application : applicationsOrder) { + ResourceBundle messages = ResourceBundle.getBundle (application, locale, bundleControl); + applications.put (application, new ApplicationInfo (messages)); + } + } + + static ResourceBundle.Control bundleControl = new ResourceBundle.Control () { + private String toResourceName0 (String bundleName, String suffix) { + if (bundleName.contains ("://")) + return null; + else + return toResourceName (bundleName, suffix); + } + public ResourceBundle newBundle (String baseName, Locale locale, String format, ClassLoader loader, boolean reload) + throws IllegalAccessException, InstantiationException, IOException { + String bundleName = toBundleName (baseName, locale); + ResourceBundle bundle = null; + final String resourceName = toResourceName0 (bundleName, "properties"); + if (resourceName == null) + return bundle; + URLConnection connection = Config.getDataUrl (Config.dataDirname, Config.configDirname, resourceName).openConnection (); + connection.setUseCaches (false); + InputStream stream = connection.getInputStream (); + try { + bundle = new PropertyResourceBundle (stream); + } finally { + stream.close (); + } + return bundle; + } + }; + + // = Reading files ======================== + /** + Set the new application and load bundle with parameter saved in configuration or the default system locale. + @param application the application bundle name. + */ + static public final void load (String application) { + initLocale (); + load (application, locale); + } + + /** + Set the new application and load bundle according the locale in parameter. + @param application the application bundle name. + @param locale the localization to used. + */ + static public final void load (String application, Locale locale) { + try { + resetLocale (locale); + if (applicationsOrder.contains (application)) + return; + applicationsOrder.add (application); + ResourceBundle messages = ResourceBundle.getBundle (application, locale, bundleControl); + // XXX anciennes valeurs de patch perdues + applications.put (application, new ApplicationInfo (messages)); + for (String key : messages.keySet ()) + labels.put (key, application); + boolean needSetLanguage = Config.getString ("Language") == null; + broadcastBundleReloaded (); + if (needSetLanguage) + HelpManager.actionLocale (null); + } catch (RuntimeException e) { + if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog + (null, "Would you like to continue?", + " Can't load Bundle "+application, JOptionPane.YES_NO_OPTION)) + throw e; + } + } + + static public final void save (String applicationName) { + File configDir = new File (Config.findDataDir (true), Config.configDirname); + File bundleFile = new File (configDir, applicationName+"_"+locale+bundleExt); + try { + save (applicationName, bundleFile); + } catch (IOException e) { + try { + configDir.mkdirs (); + save (applicationName, bundleFile); + } catch (IOException e2) { + System.err.println (MessageFormat.format (bundleSaveException, applicationName)); + } + } catch (NullPointerException e) { + throw new IllegalArgumentException (bundleUseException); + } + } + static private boolean configurationModified = false; + static public final void save (String applicationName, File file) + throws IOException { + ApplicationInfo applicationInfo = applications.get (applicationName); + if (!applicationInfo.modified && file.exists ()) + return; + if (!file.exists ()) + file.createNewFile (); + Properties properties = new Properties (); + ResourceBundle messages = applicationInfo.messages; + for (String key : messages.keySet ()) + properties.setProperty (key, messages.getString (key)); + Hashtable patch = applicationInfo.patch; + for (String key : patch.keySet ()) + properties.setProperty (key, patch.get (key)); + FileOutputStream fileOutputStream = new FileOutputStream (file); + properties.store (fileOutputStream, "Produit automatiquement par Bundle"); + fileOutputStream.close (); + FileInputStream fileInputStream = new FileInputStream (file); + applicationInfo.messages = new PropertyResourceBundle (fileInputStream); + fileInputStream.close (); + applicationInfo.patch.clear (); + applicationInfo.modified = false; + } + static public void setString (String applicationName, String key, String val) { + ApplicationInfo applicationInfo = applications.get (applicationName); + String oldVal = null; + try { + oldVal = applicationInfo.messages.getString (key); + } catch (Exception e) { + } + if (val == oldVal || val.equals (oldVal)) { + applicationInfo.patch.remove (key); + return; + } + oldVal = applicationInfo.patch.get (key); + if (val == oldVal || val.equals (oldVal)) + return; + applicationInfo.patch.put (key, val); + applicationInfo.modified = true; + labels.put (key, applicationName); + } + + // = Available files ====================== + /** + Give the list of locale available for a dedicated application. + @param application the application bundle name. + @return a array of string denoted locale that could be used for this application. + */ + static public final Locale[] getApplicationsLocales () { + Vector result = new Vector (); + scanLocale: for (Locale locale : getAvailableLocales ()) { + for (String application : applications.keySet ()) + if (ClassLoader.getSystemResource (Config.configSystemDir+application+"_"+locale+bundleExt) == null && + ClassLoader.getSystemResource (Config.configJarDir+application+"_"+locale+bundleExt) == null) + continue scanLocale; + result.add (locale); + } + Locale [] locales = result.toArray (new Locale[0]); + Arrays.sort (locales, new Comparator () { + public int compare(Locale o1, Locale o2) { + return o1.toString ().compareTo (o2.toString ()); + } + }); + return locales; + } + + // ======================================== + + static public Locale[] getAvailableLocales () { + ArrayList all = new ArrayList (Arrays.asList (Locale.getAvailableLocales ())); + //all.add (new Locale.Builder ().setLanguage ("br").setRegion ("BR").setVariant ("gallo").build ()); + //all.add (new Locale.Builder ().setLanguage ("br").setRegion ("BR").setVariant ("breton").build ()); + all.add (new Locale.Builder ().setLanguage ("br").setRegion ("FR").setVariant ("gallo").build ()); + all.add (new Locale.Builder ().setLanguage ("br").setRegion ("FR").setVariant ("breton").build ()); + //all.add (new Locale.Builder ().setLanguage ("es").setRegion ("ES").build ()); + return all.toArray (new Locale[0]); + } + static public final Locale[] getAllLocales () { + Locale[] locales = getAvailableLocales (); + Arrays.sort (locales, new Comparator () { + public int compare(Locale o1, Locale o2) { + return o1.toString ().compareTo (o2.toString ()); + } + }); + return locales; + } + + static public ImageIcon getMixFlag (String language, String country) { + ImageIcon countryFlag = Util.loadImageIcon (Config.dataDirname, Config.iconsDirname, Config.flagsDirname, + country.toLowerCase () + Config.iconsExt); + ImageIcon langFlag = Util.loadImageIcon (Config.dataDirname, Config.iconsDirname, Config.flagsDirname, + language.toLowerCase () + Config.iconsExt); + if (countryFlag == null) + return langFlag; + if (langFlag == null) + return countryFlag; + int width = countryFlag.getIconWidth (); + int height = countryFlag.getIconHeight (); + BufferedImage newFlag = new BufferedImage (width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = newFlag.createGraphics (); + Polygon topLeftCorner = new Polygon (new int[] {0, width, 0}, new int[] {0, 0, height}, 3); + g2.setClip (topLeftCorner); + g2.drawImage (langFlag.getImage (), 0, 0, width, height, null); + Polygon bottomRightCorner = new Polygon (new int[] {0, width, width}, new int[] {height, 0, height}, 3); + g2.setClip (bottomRightCorner); + g2.drawImage (countryFlag.getImage (), 0, 0, width, height, null); + return new ImageIcon (newFlag); + } + + // ======================================== + static public final JComboBox getJComboFlags (final Locale[] locales) { + final ImageIcon [] flags = new ImageIcon [locales.length]; + String [] labels = new String [locales.length]; + final Hashtable labelIndex = new Hashtable (); + for (int i = 0; i < locales.length; i++) { + labelIndex.put (labels [i] = locales[i].toString (), i); + flags [i] = getMixFlag (locales[i].getLanguage (), locales[i].getCountry ()); + if (flags [i] != null) + flags [i].setDescription (locales [i].toString ()); + } + JComboBox localesList = new JComboBox (labels); + localesList.setRenderer (new DefaultListCellRenderer () { + { + setOpaque (true); + setVerticalAlignment (CENTER); + } + static private final long serialVersionUID = 1L; + public Component getListCellRendererComponent (JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + int selectedIndex = labelIndex.get (value); + + if (isSelected) { + setBackground (list.getSelectionBackground ()); + setForeground (list.getSelectionForeground ()); + } else { + setBackground (list.getBackground ()); + setForeground (list.getForeground ()); + } + setIcon (flags[selectedIndex]); + setText (locales[selectedIndex].toString ()); + return this; + } + }); + return localesList; + } + + // = Use localized texts ================== + /** + Give the string to display according to the message identifier. + @param messageId a sting used to tag message. + @return the string to display. + */ + static public boolean askBundleUseException = true; + static public final String getString (String messageId, String defaultValue) { + try { + ApplicationInfo applicationInfo = applications.get (labels.get (messageId)); + String val = applicationInfo.patch.get (messageId); + if (val != null) + return val; + return applicationInfo.messages.getString (messageId); + } catch (Exception e) { + if (defaultValue != null) + return defaultValue; + if (askBundleUseException && JOptionPane.YES_OPTION != + JOptionPane.showConfirmDialog (null, MessageFormat.format (bundleUseException, messageId), + " Bundle corrupted ", JOptionPane.YES_NO_OPTION)) + throw new IllegalArgumentException (MessageFormat.format (bundleUseException, messageId)); + askBundleUseException = false; + System.err.println (messageId+"= ### Bundle ###"); + return messageId; + } + } + static public final String getStory (String messageId) { return getString (storyPrefix+messageId, messageId); } + static public final String getLabel (String messageId) { return messageId == null ? "" : getString (labelPrefix+messageId, null); } + static public final String getMessage (String messageId) { return getString (messagePrefix+messageId, null); } + static public final String getException (String messageId) { return getString (exceptionPrefix+messageId, null); } + static public final String getAction (String messageId) { return getString (actionPrefix+messageId, null); } + static public final String getTitle (String messageId) { return getString (messageId+titlePostfix, null); } + static public final String getUser (String messageId) { return getString (userPrefix+messageId, messageId); } + static public final void setUser (String applicationName, String messageId, String value) { + setString (applicationName, userPrefix+messageId, value); + } + static public final String getEnum (String enumId, String valueId) { return getString (enumPrefix+enumId+valueId, null); } + static public final String getEnum (Enum enumValue) { return getEnum (enumValue.getClass ().getSimpleName (), enumValue.toString ()); } + static public final JComboBox getEnum (Class enumClass, Enum defaultValue) { + String enumName = enumClass.getSimpleName (); + if (!enumClass.isEnum ()) + throw new IllegalArgumentException (enumName+" is not Enum!"); + JComboBox jComboBox = new JComboBox (); + try { + Object[] values = (Object[]) enumClass.getMethod ("values").invoke (null); + for (Object value : values) + jComboBox.addItem (getEnum (enumName, value.toString ())); + jComboBox.setEditable (false); + if (defaultValue != null) + jComboBox.setSelectedIndex (defaultValue.ordinal ()); + } catch (Exception e) { + throw new IllegalArgumentException (enumName+" is not Enum!"); + } + return jComboBox; + } + + // = Automatic update ===================== + + static public void setBorder (JComponent jComponent) { + jComponent.setBorder (BorderFactory.createTitledBorder (Bundle.getTitle (Util.getClassName (jComponent.getClass ())))); + } + static public void updateBorder (JComponent jComponent) { + ((TitledBorder) jComponent.getBorder ()).setTitle (Bundle.getTitle (Util.getClassName (jComponent.getClass ()))); + } + + /** List of actioners (button, ...) to update if the locale is updated. */ + static private Vector allActioners = new Vector (); + + /** List of actioners (menu, ...) to update if the locale is updated. */ + static private Vector allMenus = new Vector (); + + /** List of frame to update if the locale is updated. */ + static private Hashtable allDialogs = new Hashtable (); + + /** List of icon ToolTips to update if the locale is updated. */ + static private Vector allIconToolTips = new Vector (); + + /** List of application module which managed localized texts to update if the locale is updated. */ + static private Hashtable allLabels = new Hashtable (); + + /** XXX commentaire. */ + static private Hashtable, Class> allEnums = new Hashtable, Class> (); + + /** XXX commentaire. */ + static private StateNotifier bundleObservable = new StateNotifier (); + + /** + Say to each actioner and application module that locale change. + */ + static void broadcastBundleReloaded () { + if (Config.isLoaded ()) { + Config.setString ("Country", locale.getCountry ()); + Config.setString ("Language", locale.getLanguage ()); + Config.setString ("Variant", locale.getVariant ()); + } + for (AbstractButton actioner : allActioners) + if (!actioner.getText ().isEmpty ()) + actioner.setText (getAction (actioner.getActionCommand ())); + for (AbstractButton menu : allMenus) + if (!menu.getText ().isEmpty ()) + menu.setText (getTitle (menu.getActionCommand ())); + for (Dialog dialog : allDialogs.keySet ()) + dialog.setTitle (getTitle (allDialogs.get (dialog))); + for (AbstractButton actioner : allIconToolTips) + actioner.setToolTipText (Bundle.getAction (actioner.getActionCommand ())); + for (JLabel jLabel : allLabels.keySet ()) + jLabel.setText (getLabel (allLabels.get (jLabel))); + for (JComboBox jComboBox : allEnums.keySet ()) + try { + Class enumClass = allEnums.get (jComboBox); + String enumName = enumClass.getSimpleName (); + // XXX remove listeners + // XXX sauve selection multiple ? + int idx = jComboBox.getSelectedIndex (); + jComboBox.removeAllItems (); + Object[] values = (Object[]) enumClass.getMethod ("values").invoke (null); + for (Object value : values) + jComboBox.addItem (getEnum (enumName, value.toString ())); + jComboBox.setEditable (false); + jComboBox.setSelectedIndex (idx); + // XXX add listeners + } catch (Exception e) { + } + bundleObservable.broadcastUpdate ("Bundle"); + } + + /** + Declare an actioner to updated if the locale is updated. + @param button the actioner to update. The messageId is find by performed getActionCommand method on it. + */ + static public void addLocalizedActioner (AbstractButton button) { + allActioners.add (button); + } + + static public void addLocalizedMenu (AbstractButton button) { + allMenus.add (button); + } + + static public void addLocalizedDialog (Dialog dialog, String titleId) { + allDialogs.put (dialog, titleId); + } + /** + XXX commentaire + */ + static public void addLocalizedToolTip (AbstractButton button) { + allIconToolTips.add (button); + } + + /** + XXX commentaire + */ + static public void addLocalizedLabel (JLabel jLabel, String messageId) { + allLabels.put (jLabel, messageId); + } + + /** + XXX commentaire + */ + static public void addLocalizedEnum (JComboBox jComboBox, Class enumClass) { + allEnums.put (jComboBox, enumClass); + } + + /** + Declare an application module which managed localized to updated if the locale is updated. + @param object the application module that managed some texts to localized. + */ + static public void addBundleObserver (Object object) { + bundleObservable.addUpdateObserver (object, "Bundle"); + } + + // ======================================== +} diff --git a/src/java/misc/ColorCheckedLine.java b/src/java/misc/ColorCheckedLine.java new file mode 100644 index 0000000..3cd828b --- /dev/null +++ b/src/java/misc/ColorCheckedLine.java @@ -0,0 +1,24 @@ +package misc; + +public class ColorCheckedLine { + + // ============================================================ + static public final int NBV = 8; + static public final int NBT = 60; + + /** voir http://www.termsys.demon.co.uk/vtansi.htm */ + static public final String + MOVE_TO_COL_START = "\033[300C\033[", + MOVE_TO_COL_END = "D", + SETCOLOR_SUCCESS = "\033[1;32m", + SETCOLOR_FAILURE ="\033[1;31m", + SETCOLOR_WARNING ="\033[1;33m", + SETCOLOR_NORMAL ="\033[0;39m"; + + static public final String + MOVE_TO_60 = MOVE_TO_COL_START+(80-60)+MOVE_TO_COL_END, + MSG_OK = MOVE_TO_60+"["+SETCOLOR_SUCCESS+"OK"+SETCOLOR_NORMAL+"]", + MSG_KO = MOVE_TO_60+"["+SETCOLOR_FAILURE+"KO"+SETCOLOR_NORMAL+"]"; + + // ======================================== +} diff --git a/src/java/misc/CommandLineServer.java b/src/java/misc/CommandLineServer.java new file mode 100644 index 0000000..0b9d2b6 --- /dev/null +++ b/src/java/misc/CommandLineServer.java @@ -0,0 +1,154 @@ +package misc; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.ServerSocket; +import java.net.Socket; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Hashtable; +import java.util.TreeSet; +import java.util.Vector; + +public class CommandLineServer { + + // ======================================== + private Model model; + private Class> classWaiter; + private String classWaiterName; + /** List of commands. */ + private TreeSet commands = new TreeSet (); + /** Help for each command. */ + private Hashtable help = new Hashtable (); + private Hashtable actionsMethod = new Hashtable (); + + // ======================================== + public TreeSet getCommands () { return commands; } + public Hashtable getHelp () { return help; } + public Hashtable getActionsMethod () { return actionsMethod; } + public String getClassWaiterName () { return classWaiterName; } + + // ======================================== + public void showMsg (String msg) {} + public void showStatus (String msg) {} + + // ======================================== + public CommandLineServer (Model model, Class> classWaiter, Collection commands) { + this.model = model; + + this.classWaiter = classWaiter; + String[] path = classWaiter.getName ().split ("\\."); + classWaiterName = path [path.length-1]; + + for (String action : commands) + this.commands.add (action.toLowerCase ()); + for (String action : this.commands) { + try { + String methodName = "action"+Util.toCapital (action); + actionsMethod.put (action, classWaiter.getMethod (methodName)); + } catch (NoSuchMethodException e) { + Log.keepLastException ("CommandLineServer", e); + } + } + updateBundle (); + Bundle.addBundleObserver (this); + } + + // ======================================== + public void updateBundle () { + help.clear (); + for (String cmd : commands) + help.put (cmd, Bundle.getString ("Help"+classWaiterName+"_"+cmd, null).replaceAll ("\\\\n", "\n")); + } + + /** In case of multiple started, this is the only thread wich have right to run. */ + private Thread thread; + + // ======================================== + public boolean isAliveServer () { + return thread != null && thread.isAlive (); + } + + // ======================================== + private int lastPort; + /** Interrupt the server after a TIME_OUT. */ + public void stopServer () { + thread = null; + try { + // force le accept pour sortir du run + (new Socket ("localhost", lastPort)).close (); + } catch (Exception e) { + } + stopCalls (); + } + + Vector calls = new Vector (); + public synchronized void addCall (Socket call) { + calls.add (call); + } + public synchronized void removeCall (Socket call) { + calls.remove (call); + } + public synchronized void stopCalls () { + for (Socket call : calls) { + try { + call.close (); + } catch (Exception e) { + } + } + calls.clear (); + } + + // ======================================== + /** + * Start a server on a dedicated port. + * @param port the internet port number. + */ + public void startServer (final int port) { + (new Thread () { + public void run () { + ServerSocket serverSocket = null; + try { + serverSocket = new ServerSocket (port); + CommandLineServer.this.lastPort = port; + } catch (IOException e) { + showMsg (Bundle.getMessage ("CanTCreateSocket")); + Log.keepLastException ("CommandLineServer::startServer", e); + return; + } + showMsg (MessageFormat.format (Bundle.getMessage (classWaiterName+"Starts"), port)); + showStatus (MessageFormat.format (Bundle.getMessage (classWaiterName+"Starts"), port)); + thread = Thread.currentThread (); + try { + while (thread == Thread.currentThread ()) { + try { + Socket call = serverSocket.accept (); + addCall (call); + classWaiter.getConstructor (CommandLineServer.class, model.getClass (), Socket.class). + newInstance (CommandLineServer.this, model, call); + } catch (IOException e) { + showMsg (Bundle.getMessage ("ClientAbort")); + Log.keepLastException ("CommandLineServer::startServer", e); + } + } + } catch (java.lang.reflect.InvocationTargetException e) { + Log.keepLastException ("CommandLineServer::startServer", e); + } catch (NoSuchMethodException e) { + Log.keepLastException ("CommandLineServer::startServer", e); + } catch (IllegalAccessException e) { + Log.keepLastException ("CommandLineServer::startServer", e); + } catch (InstantiationException e) { + Log.keepLastException ("CommandLineServer::startServer", e); + } + try { + showMsg (Bundle.getMessage (classWaiterName+"Stopped")); + showStatus (Bundle.getMessage (classWaiterName+"Stopped")); + serverSocket.close (); + } catch (IOException e) { + } + } + }).start(); + } + + // ======================================== +} diff --git a/src/java/misc/CommandLineWaiter.java b/src/java/misc/CommandLineWaiter.java new file mode 100644 index 0000000..c5aa906 --- /dev/null +++ b/src/java/misc/CommandLineWaiter.java @@ -0,0 +1,165 @@ +package misc; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.net.Socket; +import java.net.SocketException; +import java.text.MessageFormat; +import java.util.Hashtable; +import java.util.StringTokenizer; +import java.util.TreeSet; + +public class CommandLineWaiter { + + // ======================================== + static public final int MAX_COL = 6; + + // ======================================== + protected Model model; + protected CommandLineServer commandLineServer; + + private TreeSet commands; + private Hashtable help; + private Hashtable actionsMethod; + + /** The original call socket to be close at the end of analyse. */ + protected Socket call; + /** Input chanel (of the socket). */ + protected BufferedReader in; + /** Output chanel (of the socket). */ + protected PrintWriter out; + protected String line; + protected StringTokenizer arguments; + + public boolean hasMoreArg () { + return arguments.hasMoreTokens (); + } + public String nextArg () { + return arguments.nextToken ().trim (); + } + + public void startCall () {} + + // ======================================== + public String getCommandLine (BufferedReader in) + throws IOException { + try { + return in.readLine ().trim (); + } catch (SocketException e) { + return null; + } catch (NullPointerException e) { + return null; + } + } + // ======================================== + /** + * Send a answer to the client with a newline and flush the output chanel. + * @param str the answer to send. + */ + public void answerln (String str) { + out.println (str); + out.flush (); + } + + public void syntaxError () { + answerln (Bundle.getMessage ("SyntaxeError")); + } + + // ======================================== + /** + * Give a help to the client. + * @param cmd is null or empty, give the list of commands. Else, give a appropriate help for the command cmd. + */ + public void actionHelp () { + String arg = null; + if (hasMoreArg ()) + arg = nextArg ().toLowerCase (); + if (arg == null || arg.isEmpty ()) + answerln (Bundle.getString ("Help"+commandLineServer.getClassWaiterName ()+"_", null).replaceAll ("\\\\n", "\n")+ + Util.toColumn (Util.set2string (commands), MAX_COL)); + else { + String explanation = help.get (arg); + if (explanation == null) + answerln (MessageFormat.format (Bundle.getMessage ("NotACommand"), arg)); + else + answerln (arg.toLowerCase ()+" "+explanation); + } + } + + // ======================================== + public void actionQuit () { + answerln (Bundle.getMessage ("SeeYouSoon").replaceAll ("\\\\n", "\n")); + try { + call.close (); + } catch (Exception e) { + } + } + + // ======================================== + public void actionExit () { + answerln (Bundle.getMessage ("GoodBye").replaceAll ("\\\\n", "\n")); + System.exit (0); + } + + // ======================================== + /** + * Open a chanel and start a shell interpretor. + * @param call the client connection. + */ + public CommandLineWaiter (CommandLineServer commandLineServer, Model model, Socket call) { + this.commandLineServer = commandLineServer; + this.model = model; + this.call = call; + + commands = commandLineServer.getCommands (); + help = commandLineServer.getHelp (); + actionsMethod = commandLineServer.getActionsMethod (); + try { + in = new BufferedReader (new InputStreamReader (call.getInputStream ())); + out = new PrintWriter (call.getOutputStream ()); + startCall (); + (new Thread () { + public void run() { + analyse (); + } + }).start(); + } catch (IOException e) { + if (!call.isClosed ()) + Log.keepLastException ("CommandLineWaiter", e); + } + } + + // ======================================== + public void analyse () { + String cmd = null; + try { + for (; ! call.isClosed (); ) { + line = getCommandLine (in); + if (line == null) + break; + if (line.isEmpty ()) + continue; + arguments = new StringTokenizer (line); + if (!arguments.hasMoreTokens ()) + continue; + cmd = arguments.nextToken ().trim ().toLowerCase (); + if (commands.contains (cmd)) + actionsMethod.get (cmd).invoke (this); + else + answerln (MessageFormat.format (Bundle.getMessage ("NotACommand"), cmd)); + } + } catch (Exception e) { + Log.keepLastException ("CommandLineWaiter::analyse ("+cmd+")", e); + } finally { + try { + call.close (); + } catch (Exception e) { + } + } + } + + // ======================================== +} diff --git a/src/java/misc/Config.java b/src/java/misc/Config.java new file mode 100644 index 0000000..9b263d8 --- /dev/null +++ b/src/java/misc/Config.java @@ -0,0 +1,523 @@ +package misc; + +import java.awt.Component; +import java.awt.Point; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.text.DecimalFormat; +import java.text.MessageFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Properties; +import java.util.Vector; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.ListModel; + +/** + Managed the persistance key/value strings association. + This class could be used to managed all constants values in a application. + The values commme from a bundle jar (mix of class and data) or a dedicated directeory. +*/ +public class Config { + + static public final String FS = System.getProperty ("file.separator"); + + static public final String + dataDirname = "data", + configDirname = "config", + textsDirname = "texts", + logDirname = "log", + imagesDirname = "images", + iconsDirname = "images", + buttonDirname = "button", + flagsDirname = "flags"; + + /** Path to configuration files in files system. */ + static public final String + configSystemDir = dataDirname+FS+configDirname+FS, + logSystemDir = dataDirname+FS+logDirname+FS, + buttonSystemDir = dataDirname+FS+imagesDirname+FS+buttonDirname+FS; + + /** Path to configuration files in jar. */ + static public final String + configJarDir = dataDirname+"/"+configDirname+"/", + imagesJarDir = dataDirname+"/"+imagesDirname+"/", + textsJarDir = dataDirname+"/"+textsDirname+"/"; + + /** Configuration file extension. */ + static public final String + configExt = ".xml", + iconsExt = ".png", + imagesExt = ".png", + htmlExt = ".html", + logExt = ".log"; + + + /** Enconding charset for saving configuration. */ + static public final String configEncoding = "UTF8"; + + /** Comment used as header configuration file. */ + static public final String configHeaderFile = + "This file is automaticaly generated by {0} application at {1,time,short} on {1,date}."; + + /** Somme postfix for constants name in configuration file. */ + static public final String + checkedPostfix = "Checked", + undockedPostfix = "Undocked", + locationPostfix = "Location"; + + /** Message shows when trying to get string with a null key. */ + static private final String configNullKeyException = "No configuration associates with null key."; + + /** Message shows when trying to use configuration before load it. */ + static private final String configUseException = "No configuration loaded."; + + /** Message shows when load exception appears. */ + static private final String configLoadException = "Configuration {0} can''t be load (corrupted file ?)."; + + /** Message shows when save exception appears. */ + static private final String configSaveException = "Configuration {0} can''t be save."; + + /** Liste of constants (key/value strings association). */ + //static private Properties configuration; + static public Properties configuration; + + /** True if configuration need to be saved. */ + static private boolean configurationModified = false; + + // ======================================== + static private File PWD; + static private String[] dataPath; + static { + setPWD (Util.class); + setDataPath (".:.."); + } + + static public File getJarFileOrClassDir (Class applicationClass) { + try { + return new File (applicationClass.getProtectionDomain ().getCodeSource ().getLocation ().toURI ()); + } catch (Exception e) { + // dosn't work for applet + return null; + } + } + static public File getPWD () { return PWD; } + // XXX static public String getPWD () { return PWD.getAbsolutePath (); } + static public void setPWD (Class applicationClass) { + try { + PWD = getJarFileOrClassDir (applicationClass).getParentFile (); + } catch (Exception e) { + } + } + static public void setDataPath (String path) { + dataPath = path.split (":"); + } + static public File findDataDir () { + return findDataDir (false); + } + static public File findDataDir (boolean writable) { + String name = dataDirname; + try { + // search from excecution dir + File file = (new File (name)); + if (file.exists () && file.isDirectory () && (!writable || file.canWrite ())) + return file; + } catch (Exception e) { + } + for (String path : dataPath) { + try { + // search in relative path from jar + File file = (new File (PWD, path+FS+name)); + if (file.exists () && file.isDirectory () && (!writable || file.canWrite ())) + return file; + } catch (Exception e) { + } + } + return null; + } + + static public URL getDataUrl (String... names) { + if (names.length < 1) + return null; + String name = names[names.length - 1]; + String jarPath = "", systemPath = ""; + for (int i = 0; i < names.length - 1; i++) { + jarPath += names[i]+"/"; + systemPath += names[i]+FS; + } + try { + // search from excecution dir + File file = new File (systemPath+name); + if (file.exists ()) + return file.toURI ().toURL (); + } catch (Exception e) { + } + for (String path : dataPath) { + try { + // search in relative path from jar + File file = new File (PWD, path+FS+systemPath+name); + if (file.exists ()) + return file.getCanonicalFile ().toURI ().toURL (); + } catch (Exception e) { + } + } + try { + // search in jar + URL result = ClassLoader.getSystemResource (jarPath+name); + return result; + } catch (Exception e) { + } + return null; + } + + // ======================================== + /** + Load XML configuration file contains constants (key/value strings association). + @param applicationName the application name use to find file. + */ + static public final void load (String applicationName) { + configuration = new Properties (); + try { + File configDir = new File (findDataDir (), configDirname); + File configFile = new File (configDir, applicationName+configExt); + FileInputStream fileInputStream = new FileInputStream (configFile); + configuration.loadFromXML (fileInputStream); + fileInputStream.close (); + configurationModified = false; + } catch (Exception e) { + try { + configuration.loadFromXML (ClassLoader.getSystemResourceAsStream + (configJarDir+applicationName+configExt)); + configurationModified = false; + } catch (Exception e2) { + try { + configuration.loadFromXML (ClassLoader.getSystemResourceAsStream + (configJarDir+applicationName+configExt)); + configurationModified = false; + } catch (Exception e3) { + if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog + (null, "Would you like to continue?", + " Can't load Configuration ", JOptionPane.YES_NO_OPTION)) + throw new IllegalArgumentException (MessageFormat.format (configLoadException, applicationName)); + } + } + } + } + + // ======================================== + /** + Return the loading state of the configuration. + @return true if the configuration is loaded. + */ + static public boolean isLoaded () { + return configuration != null; + } + + // ======================================== + /** + Save XML configuration file contains key/value strings association. + The file is not writing if no modification occure. + @param applicationName the application name use to save file. + */ + static public final void save (String applicationName) { + File configDir = new File (findDataDir (true), configDirname); + File configFile = new File (configDir, applicationName+configExt); + try { + save (applicationName, configFile); + } catch (IOException e) { + try { + e.printStackTrace (); + configDir.mkdirs (); + save (applicationName,configFile); + } catch (IOException e2) { + System.err.println (MessageFormat.format (configSaveException, applicationName)); + } + } catch (NullPointerException e) { + throw new IllegalArgumentException (configUseException); + } + } + + static public final void save (String applicationName, File file) + throws IOException { + if (!configurationModified && file.exists ()) + return; + if (!file.exists ()) + file.createNewFile (); + FileOutputStream fileOutputStream = new FileOutputStream (file); + configuration.storeToXML (fileOutputStream, + (new MessageFormat (configHeaderFile, Locale.US)).format (new Object[] {applicationName, new Date ()}, + new StringBuffer(), null).toString (), + configEncoding); + fileOutputStream.close (); + configurationModified = false; + } + + // ======================================== + + // XXX defaultValue peut être placé systématiquement (voir à null) + /** + Searches for the value with the specified key in this configuration list. + @param key the name of the configuration parameter. + @return the configuration value associated with the key or null if not found. + */ + static public final String getString (String key) { + if (key == null) + throw new IllegalArgumentException (configNullKeyException); + try { + return configuration.getProperty (key); + } catch (NullPointerException e) { + throw new IllegalArgumentException (configUseException); + } + } + + /** + Searches for the value with the specified key in this configuration list. + @param key the name of the configuration parameter. + @param defaultValue the defaultValue in case the key is not found. + @return the configuration value associated with the key or defaultValue if not found. + */ + static public final String getString (String key, String defaultValue) { + String value = configuration.getProperty (key); + if (value != null) + return value; + configuration.setProperty (key, defaultValue); + return defaultValue; + } + + /** + Change the value with the specified key in this configuration list. + @param key the name of the configuration parameter. + @param value the new configuration value associated with the key. + */ + static public final void setString (String key, String value) { + try { + configuration.setProperty (key, value); + configurationModified = true; + } catch (NullPointerException e) { + throw new IllegalArgumentException (configUseException); + } + } + + // ======================================== + static public final File getFile (String key) { + String fileName = getString (key); + if (fileName == null) + return null; + return new File (fileName); + } + + static public final void setFile (String key, File file) { + setString (key, file.getPath ()); + } + + // ======================================== + static public final boolean getBoolean (String key) { + return Boolean.parseBoolean (getString (key)); + } + + static public final boolean getBoolean (String key, boolean defaultValue) { + return Boolean.parseBoolean (getString (key, ""+defaultValue)); + } + + static public final void setBoolean (String key, boolean value) { + setString (key, ""+value); + } + + // ======================================== + static public final int getInt (String key) { + return Integer.parseInt (getString (key)); + } + + static public final int getInt (String key, int defaultValue) { + try { + return Integer.parseInt (getString (key, ""+defaultValue)); + } catch (Exception e) { + return defaultValue; + } + } + + static public final void setInt (String key, int value) { + setString (key, ""+value); + } + + // ======================================== + static public final float getFloat (String key) { + return Float.parseFloat (getString (key)); + } + + static public final float getFloat (String key, float defaultValue) { + try { + return Float.parseFloat (getString (key, ""+defaultValue)); + } catch (Exception e) { + return defaultValue; + } + } + + static public final void setFloat (String key, float value) { + setString (key, ""+value); + } + + // ======================================== + static public final double getDouble (String key) { + return Double.parseDouble (getString (key)); + } + + static public final double getDouble (String key, double defaultValue) { + try { + return Double.parseDouble (getString (key, ""+defaultValue)); + } catch (Exception e) { + return defaultValue; + } + } + + static public final void setDouble (String key, double value) { + setString (key, ""+value); + } + + // ======================================== + static public final T getEnum (String key, T defaultValue) { + return Util.toEnum (getString (key, ""+defaultValue), defaultValue); + } + + // ======================================== + static public final DecimalFormat indexFormat = new DecimalFormat ("-0000"); + + static public final Vector getList (String key, String defaultValue) { + Vector result = new Vector (); + int size = Integer.parseInt (getString (key+"-size", "1")); + result.add (getString (key, defaultValue)); + for (int i = 1; i < size; i++) + result.add (getString (key+indexFormat.format (i))); + return result; + } + + static public final void setList (String key, Vector value) { + // XXX suppression des anciennes valeurs + int size = value.size (); + int max = Integer.parseInt (getString (key+"-max", "10")); + setString (key+"-max", ""+max); + setString (key+"-size", ""+size); + setString (key, value.elementAt (0)); + size = Math.min (size, max); + for (int i = 1; i < size; i++) + setString (key+indexFormat.format (i), value.elementAt (i)); + } + + // ======================================== + // XXX faire un defaultValue en vector + static public final void loadJList (String key, JList jList, String defaultValue) { + Vector list = getList (key, defaultValue); + jList.setListData (list); + jList.setSelectedIndex (0); + } + + static public final void saveJList (String key, JList jList) { + Vector result = new Vector (); + ListModel model = jList.getModel (); + int size = model.getSize (); + for (int i = 0; i < size; i++) + result.add (model.getElementAt (i)); + int index = jList.getSelectedIndex (); + if (index >= 0) { + result.insertElementAt (result.remove (index), 0); + jList.setListData (result); + jList.setSelectedIndex (0); + } + setList (key, result); + } + + // ======================================== + // XXX faire un defaultValue en vector + static public final void loadJComboBox (String key, JComboBox jComboBox, String defaultValue) { + Vector list = getList (key, defaultValue); + jComboBox.removeAllItems (); + for (int i = 0; i < list.size (); i++) + jComboBox.addItem (list.elementAt (i)); + jComboBox.setSelectedIndex (0); + } + + static public final void loadJComboBoxInteger (String key, JComboBox jComboBox, String defaultValue) { + Vector list = getList (key, defaultValue); + jComboBox.removeAllItems (); + for (int i = 0; i < list.size (); i++) { + try { + jComboBox.addItem (Integer.parseInt (list.elementAt (i))); + } catch (Exception e) { + } + } + jComboBox.setSelectedIndex (0); + } + + static public final void saveJComboBox (String key, JComboBox jComboBox) { + // XX peut être ajouter la valeur éditer dans la liste + Vector result = new Vector (); + int size = jComboBox.getItemCount (); + for (int i = 0; i < size; i++) + result.add (jComboBox.getItemAt (i)); + int index = jComboBox.getSelectedIndex (); + if (index < 0) + result.insertElementAt ((String) jComboBox.getSelectedItem (), 0); + else + result.insertElementAt (result.remove (index), 0); + if (index != 0) { + jComboBox.removeAllItems (); + for (int i = 0; i < result.size (); i++) + jComboBox.addItem (result.elementAt (i)); + jComboBox.setSelectedIndex (0); + } + setList (key, result); + } + + static public final void saveJComboBoxInteger (String key, JComboBox jComboBox) { + // XX peut être ajouter la valeur éditer dans la liste + Vector result = new Vector (); + int size = jComboBox.getItemCount (); + for (int i = 0; i < size; i++) + result.add (""+jComboBox.getItemAt (i)); + int index = jComboBox.getSelectedIndex (); + if (index < 0) + result.insertElementAt ((String) jComboBox.getSelectedItem (), 0); + else + result.insertElementAt (result.remove (index), 0); + setList (key, result); + } + + // ======================================== + /** Text format used to represent coordonates (i.e. [x=123,y=456] ). */ + static public final MessageFormat coordonateFormat = new MessageFormat ("[x={0,number,integer},y={1,number,integer}]"); + + /** + Set component location from a constant coordonates in configuration. + @param key the name used to denoted the contant. + @param component modified by the coordonates retreived in configuration. + @param defaultLocation default coordonates used if non key present. + */ + static public final void loadLocation (String key, Component component, Point defaultLocation) { + try { + Object [] location = coordonateFormat.parse (getString (key+locationPostfix), + new java.text.ParsePosition (0)); + component.setLocation (((Number) location [0]).intValue (), ((Number) location [1]).intValue ()); + } catch (Exception e) { + component.setLocation (defaultLocation); + } + } + + /** + Save constant coordonates to configuration form a component location. + @param key the name used to denoted the contant. + @param component used to set the contant coordonates value. + */ + static public final void saveLocation (String key, Component component) { + Point location = component.getLocation (); + setString (key+locationPostfix, + coordonateFormat.format (new Object [] {location.x, location.y}, + new StringBuffer(), null).toString ()); + } + + // ======================================== +} diff --git a/src/java/misc/Controller.java b/src/java/misc/Controller.java new file mode 100644 index 0000000..15b4c31 --- /dev/null +++ b/src/java/misc/Controller.java @@ -0,0 +1,216 @@ +package misc; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Image; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; + +import javax.swing.AbstractButton; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +public abstract class Controller implements ApplicationManager, OwnFrame, ActionListener { + + // ======================================== + // remplace + // a placer avant la création de socket (y compris celle pour X11 faite par swing) + static { + java.util.Properties props = System.getProperties (); + props.setProperty ("java.net.preferIPv4Stack", ""+true); + System.setProperties (props); + } + + // ======================================== + static public final List actionsNames = Arrays.asList ("Quit"); + @SuppressWarnings("unchecked") + static public final Hashtable actionsMethod = Util.collectMethod (Controller.class, actionsNames); + + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + + // ======================================== + protected JFrame jFrame; + protected Component component; + protected JMenuBar menuBar; + public JFrame getJFrame () { return jFrame; } + + // ======================================== + public Controller (T t) { + createModel (t); + createAndShowFrame (createGUI (), createMenuBar ()); + Plugins.hideSate (); + } + + // ======================================== + // XXX ATTENTION + // createModel est appelé avant la création des attribut pas le constructer de "super" + // les attrubuts ne doivent donc pas être initialisé durant leur déclaration + protected abstract void createModel (T t); + public abstract String getTitle (); + public abstract Image getIcon (); + + public void updateBundle () { + SwingUtilities.invokeLater (new Runnable () { + public void run () { + jFrame.setTitle (getTitle ()); + } + }); + } + + // ======================================== + public void reborn () { + // XXX on pert les boites de dialog attachées à l'ancienne fenêtre + jFrame.dispose (); + jFrame.setJMenuBar (null); + jFrame.getContentPane ().remove (component); + createAndShowFrame (component, menuBar); + } + + // ======================================== + protected void createAndShowFrame (final Component component, final JMenuBar menuBar) { + final JFrame newJFrame = new JFrame (getTitle ()); + newJFrame.setIconImage (getIcon ()); + newJFrame.setJMenuBar (menuBar); + newJFrame.getContentPane ().add (component, BorderLayout.CENTER); + newJFrame.pack (); + newJFrame.addWindowListener (new WindowAdapter () { + public void windowClosing (WindowEvent e) { + Config.saveLocation ("Frame", jFrame); + if (tryClosingWindows ()) + quit (); + else + reborn (); + } + }); + if (jFrame == null) { + newJFrame.setLocationRelativeTo (null); + Config.loadLocation ("Frame", newJFrame, newJFrame.getLocation ()); + } else + newJFrame.setLocation (jFrame.getLocation ()); + jFrame = newJFrame; + this.component = component; + this.menuBar = menuBar; + jFrame.setVisible (true); + newJFrame (); + } + + // ======================================== + protected void newJFrame () { } + + // ======================================== + protected Component createGUI () { + Bundle.addBundleObserver (this); + return new JLabel ("Empty controller"); + } + + // ======================================== + protected JMenuBar createMenuBar () { + return null; + } + + // ======================================== + public void quit () { + System.exit (0); + } + + // ======================================== + public void actionQuit () { + Config.saveLocation ("Frame", jFrame); + if (tryClosingWindows ()) + quit (); + } + + // ======================================== + protected boolean tryClosingWindows () { + switch (JOptionPane.showConfirmDialog (jFrame, "Do you want to quit ?", "Quit", + JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE)) { + case JOptionPane.YES_OPTION: + return true; + case JOptionPane.NO_OPTION: + case JOptionPane.CLOSED_OPTION: + return false; + } + return true; + } + + // ======================================== + public void addMenuItem (JMenu... jMenu) { + Util.addMenuItem (actionsNames, this, jMenu[0]); + } + + // ======================================== + public void addIconButtons (Container... containers) { + Util.addIconButton (actionsNames, this, containers[0]); + } + + // ======================================== + public void addActiveButtons (Hashtable buttons) { + } + + // ======================================== + static public void main (String[] args) { + Config.load ("Misc"); + Bundle.load ("Help"); + Bundle.load ("Controller"); + + @SuppressWarnings ("serial") + class JControllerTestMenu extends JMenuBar { + public JControllerTestMenu (ApplicationManager controllerManager, ApplicationManager helpManager) { + setLayout (new BoxLayout (this, BoxLayout.X_AXIS)); + JMenu fileMenu = Util.addJMenu (this, "File"); + add (Box.createHorizontalGlue ()); + JMenu helpMenu = Util.addJMenu (this, "Help"); + controllerManager.addMenuItem (fileMenu); + helpManager.addMenuItem (helpMenu); + Hashtable buttons = new Hashtable (); + Util.collectButtons (buttons, fileMenu); + Util.collectButtons (buttons, helpMenu); + controllerManager.addActiveButtons (buttons); + helpManager.addActiveButtons (buttons); + } + }; + + class ControllerTest extends Controller { + public ControllerTest () { super (""); } + protected void createModel (String s) {} + public String getTitle () { return null; } + public Image getIcon () { + return Util.loadImageIcon (Config.getString ("MiscIcon", + Config.imagesJarDir+"misc"+Config.imagesExt)).getImage (); + } + protected JMenuBar createMenuBar () { + return new JControllerTestMenu (this, new HelpManager (this, "Misc")); + } + protected Component createGUI () { + return new JLabel ("Affichage de la licence."); + } + protected boolean tryClosingWindows () { + Config.save ("Misc"); + return true; + } + }; + SwingUtilities.invokeLater (new Runnable () { + public void run () { + new ControllerTest (); + } + }); + } + + // ======================================== +} diff --git a/src/java/misc/DatePanel.java b/src/java/misc/DatePanel.java new file mode 100644 index 0000000..f824abc --- /dev/null +++ b/src/java/misc/DatePanel.java @@ -0,0 +1,242 @@ +// ================================================================================ +// Copyright (c) Francois Merciol 2002 +// Project : Classe +// Name : DatePanel.java +// Language : Java +// Type : Source file for DatePanel class +// Author : Francois Merciol +// Creation : 02/09/2002 +// ================================================================================ +// History +// -------------------------------------------------------------------------------- +// Author : +// Date : +// Reason : +// ================================================================================ +// To be improved : +// ================================================================================ +package misc; + +import java.awt.Dimension; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import javax.swing.JTextField; + +/** + Champ de saisie d'une date au format dd/MM/yyyy. + Autres fonctions :
    +
  • (espace) : éfface le champs +
  • '+' ou (fleche haut) : incrémente d'un jour +
  • '-' ou (fleche bas) : décrémente d'un jour +
  • (retoure chariot) ou (perte de "focus") : transforme une date sur deux chiffres en relatif à la date actuelle. +
+*/ +@SuppressWarnings ("serial") public class DatePanel extends JTextField implements KeyListener, FocusListener { + + // ======================================== + /** Format d'une date en francais. */ + static public final SimpleDateFormat dateFormat = new SimpleDateFormat ("dd/MM/yyyy"); + /** Ensemble des caractères ayant une action sur ce champs. + Il faudrais ajouter le retoure chariot et le caractère d'effacement. */ + static public final String acceptChar = "0123456789/+- "+KeyEvent.VK_UP+KeyEvent.VK_DOWN; + + // ======================================== + /** + Création d'une date en reservant une taille maximal puis mis à la date actuelle. + */ + public DatePanel () { + super ("31/12/9999"); + addKeyListener (this); + addFocusListener (this); + set (new Date ()); + } + + // ======================================== + // api + // ======================================== + /** + Interprétation d'une année sur 2 chiffres. + */ + public void enter () { + tune2ky (); + } + + // ======================================== + /** + Fixe une date. + @param date la nouvelle valeur. + */ + public void set (Date date) { + setText (dateFormat.format (date)); + } + + // ======================================== + // Listener + // ======================================== + /** Interface KeyListener. */ + public void keyPressed (KeyEvent e) { + } + + // ======================================== + /** Interface KeyListener. */ + public void keyReleased (KeyEvent e) { + int c = e.getKeyCode (); + switch (c) { + case KeyEvent.VK_UP: + incr (1); + e.consume (); + return; + case KeyEvent.VK_DOWN: + incr (-1); + e.consume (); + return; + } + } + + // ======================================== + /** Interface KeyListener. */ + public void keyTyped (KeyEvent e) { + char c = e.getKeyChar (); + + switch (c) { + case KeyEvent.VK_ENTER: + enter (); + return; + case ';': + // le ; sera s�parateur + e.consume (); + return; + case KeyEvent.VK_BACK_SPACE: + // on laisse java faire + return; + case KeyEvent.VK_SPACE: + setText (""); + e.consume (); + return; + case '+': + incr (1); + e.consume (); + return; + case '-': + incr (-1); + e.consume (); + return; + } + + if (acceptChar.indexOf (c) < 0) { + e.consume (); + return; + } + + if (getSelectedText () == null && getText ().length () >= 12) { + e.consume (); + return; + } + } + + // ======================================== + /** Interface FocusListener. */ + public void focusGained (FocusEvent e) { + } + + // ======================================== + /** Interface FocusListener. */ + public void focusLost (FocusEvent e) { + tune2ky (); + } + + // ======================================== + // Listener spécial + // ======================================== + /** + Interprétation d'une année sur 2 chiffre. + */ + public void tune2ky () { + try { + Date origDate = dateFormat.parse (getText ().trim ()); + GregorianCalendar cal = new GregorianCalendar (); + cal.setTime (origDate); + int y = cal.get (Calendar.YEAR); + if (y < 100) { + cal.add (Calendar.YEAR, cur2ky); + if (y < min2ky) + cal.add (Calendar.YEAR, 100); + else if (y > max2ky) + cal.add (Calendar.YEAR, -100); + } + setText (dateFormat.format (cal.getTime ())); + getSize (tmpSize); + if (tmpSize.width != 0) + size = tmpSize; + } catch (Exception ex) { + } + } + + // ======================================== + /** Taille actuelle du champ. */ + private Dimension tmpSize = new Dimension (); + /** Derniére taille maximum utilisée. */ + private Dimension size; + + // ======================================== + /** + @return la taille maximum déjà utilisé. + */ + public Dimension getPreferredSize () { + if (size != null) + return size; + return super.getPreferredSize (); + } + + // ======================================== + /** + @return la taille maximum déjà utilisé. + */ + public Dimension getMinimumSize () { + if (size != null) + return size; + return super.getPreferredSize (); + } + + // ======================================== + /** + Change la date d'une certain nombre de jours. + @param incr le nombre de jour à avancer (diminuer si négatif). + */ + public void incr (int incr) { + try { + tune2ky (); + Date origDate = dateFormat.parse (getText ().trim ()); + GregorianCalendar cal = new GregorianCalendar (); + cal.setTime (origDate); + cal.add (Calendar.DATE, incr); + setText (dateFormat.format (cal.getTime ())); + } catch (Exception ex) { + } + } + + // ======================================== + // valeurs d'une année sur 2 chiffres + // ======================================== + /** XXX commentaire sur l'optimisation d'une année sur 2 chiffre. */ + static public int min2ky, max2ky, cur2ky; + + /** Initialisation des champs. */ + static { + GregorianCalendar cal = new GregorianCalendar (); + cal.setTime (new Date ()); + int y = cal.get(Calendar.YEAR); + int yMod1ky = y % 100; + min2ky = yMod1ky - 50; + max2ky = yMod1ky + 50; + cur2ky = y - yMod1ky; + } + + // ======================================== +} diff --git a/src/java/misc/DimensionDouble.java b/src/java/misc/DimensionDouble.java new file mode 100644 index 0000000..58379bf --- /dev/null +++ b/src/java/misc/DimensionDouble.java @@ -0,0 +1,62 @@ +// ================================================================================ +// François MERCIOL 2015 +// Name : Dimension2D.java +// Language : Java +// Author : François Merciol +// CopyLeft : Cecil B +// Creation : 2015 +// Version : 0.1 (xx/xx/xx) +// ================================================================================ +package misc; + +import java.awt.geom.Dimension2D; + +@SuppressWarnings ("overrides") +public class DimensionDouble extends Dimension2D { + + // ======================================== + public double width, height; + + public DimensionDouble () { + } + + public DimensionDouble (double width, double height) { + this.width = width; + this.height = height; + } + + public boolean equals (Object obj) { + try { + DimensionDouble dim = (DimensionDouble) obj; + return width == dim.width && height == dim.height; + } catch (Exception e) { + return false; + } + } + + // ======================================== + public double getHeight () { + return height; + } + + public double getWidth () { + return width; + } + + public void setSize (Dimension2D d) { + width = d.getWidth (); + height = d.getHeight (); + } + + public void setSize (double width, double height) { + this.width = width; + this.height = height; + } + + // ======================================== + public String toString () { + return DimensionDouble.class.getSimpleName ()+"[width="+width+",height="+height+"]"; + } + + // ======================================== +} diff --git a/src/java/misc/EasterEgg.java b/src/java/misc/EasterEgg.java new file mode 100644 index 0000000..65aadb6 --- /dev/null +++ b/src/java/misc/EasterEgg.java @@ -0,0 +1,53 @@ +package misc; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +public class EasterEgg { + public static final SimpleDateFormat birthFormat = new SimpleDateFormat("MMdd"); + + String defaultValue; + + class Periode { + String start; + + String stop; + + String value; + + Periode(String start, String stop, String value) { + this.start = start; + this.stop = stop; + this.value = value; + } + } + + ArrayList periodes = new ArrayList<>(); + + public EasterEgg(String defaultValue) { + this.defaultValue = defaultValue; + } + + public void addPeriode(String start, String stop, String value) { + this.periodes.add(new Periode(start, stop, value)); + } + + public String getValue() { + return get(new Date()); + } + + public String get(Date date) { + String day = birthFormat.format(date); + for (Periode periode : this.periodes) { + if (periode.start.compareTo(periode.stop) <= 0) { + if (day.compareTo(periode.start) >= 0 && day.compareTo(periode.stop) <= 0) + return periode.value; + continue; + } + if (day.compareTo(periode.start) <= 0 || day.compareTo(periode.stop) >= 0) + return periode.value; + } + return this.defaultValue; + } +} diff --git a/src/java/misc/Guide.java b/src/java/misc/Guide.java new file mode 100644 index 0000000..70092ac --- /dev/null +++ b/src/java/misc/Guide.java @@ -0,0 +1,208 @@ +package misc; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLFrameHyperlinkEvent; + +@SuppressWarnings ("serial") public class Guide extends HtmlDialog { + + // ======================================== + static public final int H_SPACE = 5; + static public final int V_SPACE = 2; + + // ======================================== + Color standardBackground = Color.gray; + Color highlightColor = Color.orange; + Component [] component; + Color [] componentBackgroundColor; + int next = -1; + boolean [] done; + + // ======================================== + public Guide (Frame frame, String titleName, String fileName, Component [] component, + Color standardBackground, Color highlightColor) { + super (frame, titleName, fileName); + this.component = component; + this.standardBackground = standardBackground; + this.highlightColor = highlightColor; + done = new boolean [component.length]; + componentBackgroundColor = new Color [component.length]; + for (int i = 0; i < component.length; i++) + if (component [i] != null) + componentBackgroundColor [i] = component [i].getBackground (); + reset (); + editorPane.addHyperlinkListener (new Hyperactive ()); + } + + // ======================================== + class Hyperactive implements HyperlinkListener { + + public void hyperlinkUpdate (HyperlinkEvent e) { + if (e.getEventType () == HyperlinkEvent.EventType.ACTIVATED) { + String file = e.getURL ().getPath (); + String command = file.substring (file.lastIndexOf ("/")+1); + int idx = command.indexOf ("?"); + if (idx >= 0) + command = command.substring (0, idx); + if ("Reset".equals (command)) { + reset (); + setNext (); + } else if (command.startsWith ("ActionG")) { + String [] bound = command.substring ("ActionG".length ()).split ("\\-"); + flipGroup (Integer.parseInt (bound[0]), Integer.parseInt (bound[1])); + } else if (command.startsWith ("Action")) + flipStep (Integer.parseInt (command.substring ("Action".length ()))); + else if (e instanceof HTMLFrameHyperlinkEvent) { + HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent) e; + HTMLDocument doc = (HTMLDocument) editorPane.getDocument (); + doc.processHTMLFrameHyperlinkEvent (evt); + } else { + try { + editorPane.setPage (e.getURL ()); + } catch (Throwable t) { + Log.keepLastException ("Guide::hyperlinkUpdate", t); + } + } + } + } + } + + // ======================================== + public void setVisible (boolean visible) { + reset (); + super.setVisible (visible); + if (visible) { + setNext (); + editorPane.scrollToReference ("Top"); + } + } + + // ======================================== + public void changeHtmlClassAction (String actionName, String className) { + String newPage = editorPane.getText (). + replaceAll ("]*name=\"Action("+actionName+")\"[^>]*>", + "
"). + replaceAll ("]*name=\"Action("+actionName+")\"[^>]*>", + ""); + editorPane.setText (newPage); + } + + // ======================================== + public void reset () { + next = -1; + if (done == null) + return; + for (int step = 0; step < done.length; step++) { + done [step] = false; + if (component [step] != null) + component [step].setBackground (componentBackgroundColor [step]); + } + // XXX pb d'affichage + editorPane.getText (); + try { + Thread.sleep (200); + } catch (InterruptedException e) { + } + changeHtmlClassAction ("(G[0-9]+-)?[0-9]+", "Unknown"); + editorPane.scrollToReference ("Top"); + } + + // ======================================== + public void unsetNext () { + if (next < 0) + return; + changeHtmlClassAction (""+next, "Unknown"); + if (component [next] != null) + component [next].setBackground (componentBackgroundColor [next]); + next = -1; + } + + // ======================================== + public void setNext () { + if (next >= 0 && ! done [next]) + return; + next = -1; + for (int step = 0; step < done.length; step++) + if (!done [step]) { + next = step; + changeHtmlClassAction (""+next, "Todo"); + if (component [next] != null) + component [next].setBackground (highlightColor); + editorPane.scrollToReference ("Action"+next); + return; + } + } + + // ======================================== + private void stepIsDone (int step) { + done [step] = true; + changeHtmlClassAction (""+step, "Done"); + if (component [step] != null) + component [step].setBackground (componentBackgroundColor [step]); + } + + // ======================================== + private void stepIsUnknown (int step) { + done [step] = false; + changeHtmlClassAction (""+step, "Unknown"); + } + + // ======================================== + public void flipStep (int step) { + unsetNext (); + if (done [step]) + stepIsUnknown (step); + else + stepIsDone (step); + checkGroup (); + setNext (); + } + + // ======================================== + public void flipGroup (int step1, int step2) { + unsetNext (); + boolean allDone = true; + for (int step = step1; step <= step2; step++) + if (!done [step]) { + allDone = false; + break; + } + if (allDone) { + for (int step = step1; step <= step2; step++) + stepIsUnknown (step); + changeHtmlClassAction ("G"+step1+"-"+step2, "Unknown"); + } else { + for (int step = step1; step <= step2; step++) + stepIsDone (step); + changeHtmlClassAction ("G"+step1+"-"+step2, "Done"); + } + checkGroup (); + setNext (); + } + + // ======================================== + public void checkGroup () { + String newPage = editorPane.getText (); + Pattern pattern = Pattern.compile ("]*name=\"ActionG(([0-9]+)-([0-9]+))\"[^>]*>"); + Matcher matcher = pattern.matcher (newPage); + while (matcher.find()) { + int step1 = Integer.parseInt (matcher.group (2)); + int step2 = Integer.parseInt (matcher.group (3)); + boolean allDone = true; + for (int step = step1; step <= step2; step++) + if (!done [step]) { + allDone = false; + break; + } + changeHtmlClassAction ("G"+step1+"-"+step2, allDone ? "Done" : "Unknown"); + } + } + + // ======================================== +} diff --git a/src/java/misc/HelpManager.java b/src/java/misc/HelpManager.java new file mode 100644 index 0000000..889b8d1 --- /dev/null +++ b/src/java/misc/HelpManager.java @@ -0,0 +1,197 @@ +package misc; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.Locale; +import java.util.Vector; +import javax.swing.AbstractButton; +import javax.swing.ImageIcon; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import misc.Bundle; +import misc.Log; +import misc.Util; + +/** + Les drapeaux peuvent être trouvé sur http://flags.blogpotato.de/ +*/ +@SuppressWarnings ("serial") public class HelpManager implements ApplicationManager, ActionListener { + + // ======================================== + private Frame frame; + private OwnFrame controller; + private String applicationName; + private JConsole jConsole; + + // ======================================== + static public final String + //actionPackBug = "PackBug", + actionForcePack = "ForcePack", + actionBugReport = "BugReport", + actionJConsole = "JConsole"; + + static public final List actionsNames = + Arrays.asList ("About", "Licence", "Locale"); + static public final List actionsBugNames = + Arrays.asList (/*actionPackBug, */actionForcePack, actionBugReport, actionJConsole); + @SuppressWarnings("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (HelpManager.class, actionsNames, actionsBugNames); + + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + + // ======================================== + public HelpManager (OwnFrame controller, String applicationName) { + this.controller = controller; + this.applicationName = applicationName; + frame = new Frame (); + frame.setIconImage (controller.getIcon ()); + aboutDialog = new HtmlDialog (frame, "About", Config.textsJarDir+"About"+applicationName+Config.htmlExt); + licenceDialog = new HtmlDialog (frame, "Licence", Config.textsJarDir+applicationName+"Licence"+Config.htmlExt); + jConsole = new JConsole (frame); + //Util.packBug = Config.getBoolean (actionPackBug+Config.checkedPostfix, false); + } + + // ======================================== + private HtmlDialog aboutDialog; + private HtmlDialog licenceDialog; + + // ======================================== + public void actionAbout () { + aboutDialog.restart (); + aboutDialog.setVisible (true); + } + + // ======================================== + public void actionLicence () { + licenceDialog.restart (); + licenceDialog.setVisible (true); + } + + // ======================================== + public void actionLocale () { + actionLocale (controller); + } + + static public void actionLocale (OwnFrame controller) { + try { + Locale [] locales = Bundle.getApplicationsLocales (); + JComboBox localesList = Bundle.getJComboFlags (locales); + Locale currentLocale = Bundle.getLocale (); + int currentIndex = 0; + if (currentLocale != null) + for (int i = 0; i < locales.length; i++) + if (currentLocale.equals (locales[i])) { + currentIndex = i; + break; + } + localesList.setSelectedIndex (currentIndex); + JPanel jPanel = new JPanel (new BorderLayout ()); + jPanel.add (new JLabel (Bundle.getMessage ("ChooseLocale")), BorderLayout.PAGE_START); + jPanel.add (localesList, BorderLayout.CENTER); + + Frame frame = controller != null ? controller.getJFrame () : null; + Frame frmOpt = null; + if (frame == null) { + frmOpt = new JFrame (); + frmOpt.setVisible (true); + frmOpt.setLocationRelativeTo (null); + frmOpt.setAlwaysOnTop (true); + frame = frmOpt; + } + try { + if (JOptionPane.OK_OPTION != + JOptionPane.showConfirmDialog (frame, jPanel, Bundle.getTitle ("ChangeLocale"), + JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE)) + return; + } finally { + if (frmOpt != null) + frmOpt.dispose(); + } + Bundle.setLocale (locales[localesList.getSelectedIndex ()]); + Util.packWindow (frame); + } catch (Exception e) { + Log.keepLastException ("HelpManager::actionLocale", e); + } + } + + // // ======================================== + // public void actionPackBug () { + // Util.packBug = ! Util.packBug; + // Config.setBoolean (actionPackBug+Config.checkedPostfix, Util.packBug); + // ImageIcon icon = Util.loadActionIcon (actionPackBug+(Util.packBug ? "On" : "Off")); + // for (AbstractButton packBugCommand : packBugCommands) { + // packBugCommand<.setSelected (Util.packBug); + // // XXX bug Java + // packBugCommand.setIcon (icon); + // } + // } + + // ======================================== + public void actionForcePack () { + controller.reborn (); + } + + // ======================================== + public void actionBugReport () { + Log.dumpSaveDialog (controller.getJFrame ()); + } + + // ======================================== + public void actionJConsole (boolean checked) { + jConsole.setVisible (checked); + Util.updateCheckBox (actionJConsole, checked); + } + + // ======================================== + // static private Vector packBugCommands = new Vector (); + // static public void addPackBugCommand (AbstractButton packBugCommand) { + // if (packBugCommand == null) + // return; + // packBugCommands.add (packBugCommand); + // packBugCommand.setSelected (Util.packBug); + // // XXX bug Java + // packBugCommand.setIcon (Util.loadActionIcon (actionPackBug+(Util.packBug ? "On" : "Off"))); + // } + // static public void removePackBugCommand (AbstractButton packBugCommand) { + // packBugCommands.remove (packBugCommand); + // } + + // ======================================== + public void addMenuItem (JMenu... jMenu) { + Util.addMenuItem (actionsNames, this, jMenu[0]); + //Util.addCheckMenuItem (actionPackBug, this, Util.packBug, jMenu[0]); + Util.addMenuItem (actionForcePack, this, jMenu[0]); + Util.addMenuItem (actionBugReport, this, jMenu[0]); + Util.addCheckMenuItem (actionJConsole, this, jMenu[0]); + } + + // ======================================== + public void addIconButtons (Container... containers) { + Util.addIconButton (actionsNames, this, containers[0]); + //Util.addIconButton (actionPackBug, this, containers[0]); + Util.addIconButton (actionForcePack, this, containers[0]); + Util.addIconButton (actionBugReport, this, containers[0]); + } + + // ======================================== + public void addActiveButtons (Hashtable buttons) { + //addPackBugCommand (buttons.get (actionPackBug)); + Log.addDumpCommand (buttons.get (actionBugReport)); + } + // ======================================== +} diff --git a/src/java/misc/HourPanel.java b/src/java/misc/HourPanel.java new file mode 100644 index 0000000..8d37920 --- /dev/null +++ b/src/java/misc/HourPanel.java @@ -0,0 +1,253 @@ +// ================================================================================ +// Copyright (c) Francois Merciol 2002 +// Project : Perso +// Name : HourPanel.java +// Language : Java +// Type : Source file for HourPanel class +// Author : Francois Merciol +// Creation : 02/09/2002 +// ================================================================================ +// History +// -------------------------------------------------------------------------------- +// Author : +// Date : +// Reason : +// ================================================================================ +// To be improved : +// ================================================================================ +package misc; + +import java.awt.Dimension; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.text.SimpleDateFormat; +import java.util.Date; +import javax.swing.JTextField; + +/** + Champ de saisie d'une heure au format HH:mm. + Autres fonctions :
    +
  • (espace) : éfface le champs +
  • (retoure chariot) ou (perte de "focus") : remet le champ au format. +
  • Le premier caractère frappé éfface le champs. Des caractères peuvent être concervé si le prmier caractère + concerne un déplacement ou un éffacement. +
+*/ +@SuppressWarnings ("serial") public class HourPanel extends JTextField implements KeyListener, FocusListener { + + // ======================================== + /** Format d'une heure. */ + static public final SimpleDateFormat hourFormat = new SimpleDateFormat ("HH:mm"); + /** Ensemble des caractères ayant une action sur ce champs. + Il faudrais ajouter le retoure chariot et le caractère d'effacement. */ + static public final String acceptChar = "0123456789: "; + + // ======================================== + /** mémorise si le caractère frappé et le premier dans le champs. */ + private boolean justFocus = false; + + // ======================================== + /** + Création d'une date en reservant une taille maximal puis mis à la date actuelle. + */ + public HourPanel () { + super ("23:59"); + addKeyListener (this); + addFocusListener (this); + set (new Date ()); + } + + // ======================================== + // api + // ======================================== + /** + Remet en forme le champs. + */ + public void enter () { + tuneHour (); + } + + // ======================================== + /** + Fixe une heure. + @param date la nouvelle valeur. + */ + public void set (Date date) { + setText (hourFormat.format (date)); + } + + // ======================================== + // Listener + // ======================================== + /** Dernière possition du curseur. */ + int lastJTextPos = -1; + + // ======================================== + /** Interface KeyListener. */ + public void keyPressed (KeyEvent e) { + lastJTextPos = getCaretPosition (); + } + + // ======================================== + /** Interface KeyListener. */ + public void keyReleased (KeyEvent e) { + } + + // ======================================== + /** Interface KeyListener. */ + public void keyTyped (KeyEvent e) { + char c = e.getKeyChar (); + + switch (c) { + case KeyEvent.VK_ENTER: + enter (); + return; + case ';': + // le ; sera s�parateur + e.consume (); + return; + case KeyEvent.VK_BACK_SPACE: + // on laisse java faire + justFocus = false; + return; + case KeyEvent.VK_SPACE: + setText (""); + e.consume (); + return; + } + + if (acceptChar.indexOf (c) < 0) { + e.consume (); + return; + } + + if (getSelectedText () == null) { + if (justFocus) + setText (""); + if (c != ':' && getText ().length () >= 5) { + e.consume (); + return; + } + } else { + String text = getText (); + lastJTextPos = getSelectionStart (); + setText (text.substring (0, getSelectionStart ())+ text.substring (getSelectionEnd ())); + } + justFocus = false; + processHour (e); + } + + // ======================================== + /** Interface FocusListener. */ + public void focusGained (FocusEvent e) { + justFocus = true; + } + + // ======================================== + /** Interface FocusListener. */ + public void focusLost (FocusEvent e) { + justFocus = false; + tuneHour (); + } + + // ======================================== + // Listener spécial + // ======================================== + /** + Traite l'insertion d'un caractère dur champ (chiffre ou ':'). + @param e touche frappé au clavier. + */ + public void processHour (KeyEvent e) { + char c = e.getKeyChar (); + String text = getText (); + int dpPos = text.indexOf (":"); + if (dpPos >= 0) { + if (c == ':') { + if (lastJTextPos <= dpPos) + setText (((lastJTextPos == 0) ? "0" : text.substring (0, lastJTextPos))+":"+ + text.substring (dpPos+1)); + else + setText (text.substring (0, dpPos)+":"+text.substring (lastJTextPos)); + setCaretPosition (text.indexOf (":")+1); + e.consume (); + return; + } + } else { + if (c == ':') + return; + // traiter l'apparition du : + if (text.length () < 1) + return; + if (text.charAt (0) > '2') { + // XXX pb si lastJTextPos = 0 + setText (text.charAt (0)+":"+text.substring (1)); + setCaretPosition (lastJTextPos+1); + return; + } + if (text.length () > 1) { + // XXX pb si lastJTextPos < 2 + setText (text.substring (0, 2)+":"+text.substring (2)); + setCaretPosition (lastJTextPos+1); + return; + } + } + } + + // ======================================== + /** + Remise en forme de l'heure. + */ + public void tuneHour () { + try { + String text = getText ().trim (); + if (text.isEmpty ()) { + setText ("00:00"); + return; + } + switch (text.indexOf (":")) { + case -1: + text += ":0"; + break; + case 0: + text = "0"+text; + } + if (text.indexOf (":") == (text.length ()-1)) + text += "0"; + setText (hourFormat.format (hourFormat.parse (text))); + getSize (tmpSize); + if (tmpSize.width != 0) + size = tmpSize; + } catch (Exception ex) { + } + } + + // ======================================== + /** Taille actuelle du champ. */ + private Dimension tmpSize = new Dimension (); + /** Derni�re taille maximum utilisée. */ + private Dimension size; + + // ======================================== + /** + @return la taille maximum déjà utilisé. + */ + public Dimension getPreferredSize () { + if (size != null) + return size; + return super.getPreferredSize (); + } + + // ======================================== + /** + @return la taille maximum déjà utilisé. + */ + public Dimension getMinimumSize () { + if (size != null) + return size; + return super.getPreferredSize (); + } + + // ======================================== +} diff --git a/src/java/misc/HtmlDialog.java b/src/java/misc/HtmlDialog.java new file mode 100644 index 0000000..f9fe778 --- /dev/null +++ b/src/java/misc/HtmlDialog.java @@ -0,0 +1,85 @@ +package misc; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Frame; +import java.io.IOException; +import java.net.URL; +import java.text.MessageFormat; +import javax.swing.BorderFactory; +import javax.swing.JEditorPane; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLFrameHyperlinkEvent; + +// XXX rechercher le fichier à afficher en fonction de la langue +// XXX mise à jour dynamique en cas de changement de langue + +@SuppressWarnings ("serial") public class HtmlDialog extends TitledDialog { + + // ======================================== + public JEditorPane editorPane; + + protected URL startedURL; + + // ======================================== + public HtmlDialog (Frame frame, String titleName, String fileName) { + super (frame, titleName); + editorPane = new JEditorPane (); + changeFileName (fileName); + editorPane.setBackground (getBackground ()); + editorPane.setBorder (BorderFactory.createCompoundBorder (BorderFactory.createRaisedBevelBorder (), + BorderFactory.createEmptyBorder (2, 5, 2, 5))); + editorPane.setEditable (false); + editorPane.setCaretPosition (0); + editorPane.scrollToReference ("Top"); + JScrollPane editorScrollPane = new JScrollPane (editorPane); + editorScrollPane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + editorScrollPane.setPreferredSize (new Dimension (600, 300)); + getContentPane ().add (editorScrollPane, BorderLayout.CENTER); + } + + // ======================================== + public void changeFileName (String fileName) { + try { + URL newURL = Config.getDataUrl (fileName); + if (newURL.equals (startedURL)) + return; + startedURL = newURL; + restart (); + } catch (Exception e) { + } + } + + // ======================================== + HyperlinkListener hyperlinkListener = new HyperlinkListener () { + public void hyperlinkUpdate (HyperlinkEvent e) { + if (e.getEventType () == HyperlinkEvent.EventType.ACTIVATED) { + if (e instanceof HTMLFrameHyperlinkEvent) { + ((HTMLDocument) editorPane.getDocument ()).processHTMLFrameHyperlinkEvent ((HTMLFrameHyperlinkEvent) e); + } else { + try { + editorPane.setPage (e.getURL ()); + } catch (Exception ioe) { + System.err.println ("IOE: " + ioe); + } + } + } + } + }; + + public void restart () { + try { + editorPane.setPage (startedURL); + editorPane.setDragEnabled (true); + editorPane.removeHyperlinkListener (hyperlinkListener); + editorPane.addHyperlinkListener (hyperlinkListener); + } catch (Exception e) { + } + } + + // ======================================== +} diff --git a/src/java/misc/ImagePreview.java b/src/java/misc/ImagePreview.java new file mode 100644 index 0000000..0d06473 --- /dev/null +++ b/src/java/misc/ImagePreview.java @@ -0,0 +1,89 @@ +package misc; + +import javax.swing.*; +import java.beans.*; +import java.awt.*; +import java.io.File; + +/* ImagePreview.java by FileChooserDemo2.java. */ +@SuppressWarnings("serial") +public class ImagePreview extends JComponent + implements PropertyChangeListener { + + static public int maxInSide = 90; + static public int border = 5; + static public int maxOutSide = maxInSide+2*border; + + protected ImageIcon thumbnail = null; + protected File file = null; + protected long maxSize; + + public ImagePreview (JFileChooser fc, long maxSize) { + this.maxSize = maxSize; + setPreferredSize (new Dimension (maxOutSide, maxOutSide)); + fc.addPropertyChangeListener (this); + } + + public void loadImage () { + if (file == null || (maxSize > 0 && file.length () > maxSize)) { + thumbnail = null; + return; + } + //Don't use createImageIcon (which is a wrapper for getResource) + //because the image we're trying to load is probably not one + //of this program's own resources. + ImageIcon tmpIcon = new ImageIcon (file.getPath ()); + if (tmpIcon != null) { + thumbnail = tmpIcon; + if (tmpIcon.getIconWidth () > tmpIcon.getIconHeight ()) { + if (tmpIcon.getIconWidth () > maxInSide) + thumbnail = + new ImageIcon (tmpIcon.getImage ().getScaledInstance (maxInSide, -1, Image.SCALE_DEFAULT)); + } else { + if (tmpIcon.getIconHeight () > maxInSide) + thumbnail = + new ImageIcon (tmpIcon.getImage ().getScaledInstance (-1, maxInSide, Image.SCALE_DEFAULT)); + + } + } + } + + public void propertyChange (PropertyChangeEvent e) { + boolean update = false; + String prop = e.getPropertyName (); + + //If the directory changed, don't show an image. + if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals (prop)) { + file = null; + update = true; + + //If a file became selected, find out which one. + } else if (JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals (prop)) { + file = (File) e.getNewValue (); + update = true; + } + + //Update the preview accordingly. + if (update) { + thumbnail = null; + if (isShowing ()) { + loadImage (); + repaint (); + } + } + } + + protected void paintComponent (Graphics g) { + if (thumbnail == null) + loadImage (); + if (thumbnail != null) { + int x = getWidth ()/2 - thumbnail.getIconWidth ()/2; + int y = getHeight ()/2 - thumbnail.getIconHeight ()/2; + if (y < border) + y = border; + if (x < border) + x = border; + thumbnail.paintIcon (this, g, x, y); + } + } +} diff --git a/src/java/misc/JConsole.java b/src/java/misc/JConsole.java new file mode 100644 index 0000000..006aa6e --- /dev/null +++ b/src/java/misc/JConsole.java @@ -0,0 +1,77 @@ +package misc; + +import java.io.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; + +@SuppressWarnings ("serial") public class JConsole extends TitledDialog { + + // ======================================== + private JTextArea textArea = new JTextArea (5, 20); + public static boolean copy = true; + + // ======================================== + public JConsole (Frame frame) { + super (frame, "JConsole"); + textArea.setEditable (false); + setPreferredSize (new Dimension (550, 300)); + getContentPane ().add (new JScrollPane (textArea), BorderLayout.CENTER); + getContentPane ().add (Util.newButton ("Clear", new ActionListener () { + public void actionPerformed (ActionEvent evt) { + synchronized (textArea) { + textArea.setText (""); + } + } + }), BorderLayout.SOUTH); + if (copy) { + copyOut (true); + copyOut (false); + } + pack (); + } + + + // ======================================== + public void copyOut (boolean err) { + Thread thread = new Thread () { + public void run () { + for (;;) { + try { + PipedInputStream pin = new PipedInputStream (); + PrintStream out = new PrintStream (new PipedOutputStream (pin), true, "UTF-8"); + if (err) + System.setErr (out); + else + System.setOut (out); + BufferedReader in = new BufferedReader (new InputStreamReader (pin)); + for (;;) { + String line = in.readLine (); + synchronized (textArea) { + textArea.append (line+"\n"); + } + } + } catch (Exception e) { + textArea.append (e.toString()); + ByteArrayOutputStream buf = new ByteArrayOutputStream (); + e.printStackTrace (new PrintStream (buf)); + textArea.append (buf.toString ()); + try { + Thread.sleep (1000); + } catch (InterruptedException e2) { + } + } + } + } + }; + thread.setDaemon (true); + thread.start (); + } + + // ======================================== + public static void main (String[] arg) { + new JConsole (null); + } + + // ======================================== +} diff --git a/src/java/misc/JRemoteUpdate.java b/src/java/misc/JRemoteUpdate.java new file mode 100644 index 0000000..bad2a8f --- /dev/null +++ b/src/java/misc/JRemoteUpdate.java @@ -0,0 +1,237 @@ +package misc; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.TreeSet; +import java.util.Vector; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingConstants; +import javax.swing.table.DefaultTableModel; + +import static misc.Util.GBC; +import static misc.Util.GBCNL; + +import static misc.RemoteUpdate.CheckPeriod; +import static misc.RemoteUpdate.FileDescriptor; +import static misc.RemoteUpdate.FileInfo; +import static misc.RemoteUpdate.TodoFile; + +public class JRemoteUpdate implements SwingConstants { + + static public String + actionDetails = "Details", + labelFileName = "FileName", labelLocal= "Local", labelRemote = "Remote"; + + + // ======================================== + private OwnFrame controller; + RemoteUpdate remoteUpdate; + Runnable actionAfterDownLoad; + + JPanel jPanel = Util.getGridBagPanel (); + ArrayList allMirrorInfo; + JFrame jFrame = new JFrame (); + JButton startAction; + + // ======================================== + public JRemoteUpdate (OwnFrame controller, RemoteUpdate remoteUpdate, Runnable actionAfterDownLoad) { + this.controller = controller; + this.remoteUpdate = remoteUpdate; + this.actionAfterDownLoad = actionAfterDownLoad; + jFrame.setIconImage (controller.getIcon ()); + } + + public class MirrorInfo { + RemoteUpdate.Mirror mirror; + StateNotifier notifier = new StateNotifier (); + public ProgressState progressState = new ProgressState (notifier, "Progress"); + TodoFile transfertAction, cleanAction; + JCheckBox transfertBoutton, cleanBoutton; + JLabel info; + ProgressStatePanel progressStatePanel = new ProgressStatePanel (progressState); + + MirrorInfo (RemoteUpdate.Mirror mirror, TodoFile transfertAction) { + this.mirror = mirror; + // XXX check transfertAction in Download|Upload + this.transfertAction = transfertAction; + cleanAction = transfertAction == TodoFile.Download ? TodoFile.LocalRemove : TodoFile.RemoteRemove; + transfertBoutton = Util.addCheckIcon (""+transfertAction, null, + Config.getBoolean (transfertAction+mirror.token+Config.checkedPostfix, false), + jPanel, GBC); + cleanBoutton = Util.addCheckIcon (""+cleanAction, null, + Config.getBoolean (cleanAction+mirror.token+Config.checkedPostfix, false), + jPanel, GBC); + notifier.addUpdateObserver (progressStatePanel, "Progress"); + info = new JLabel (mirror.getInfo (mirror.check (transfertAction), transfertAction)); + Util.addComponent (info, jPanel, GBC); + Util.addIconButton (actionDetails, new ActionListener () { + public void actionPerformed (ActionEvent e) { + JOptionPane.showMessageDialog (jFrame, getDetails (), Bundle.getTitle (""+actionDetails), JOptionPane.INFORMATION_MESSAGE); + } + }, jPanel); + Util.unBoxButton (jPanel); + Util.addComponent (progressStatePanel, jPanel, GBCNL); + } + public void setConfig () { + Config.setBoolean (transfertAction+mirror.token+Config.checkedPostfix, transfertBoutton.isSelected ()); + Config.setBoolean (cleanAction+mirror.token+Config.checkedPostfix, cleanBoutton.isSelected ()); + } + public void update () { + (new Thread () { + public void run () { + mirror.update (); + info.setText (mirror.getInfo (mirror.check (transfertAction), transfertAction)); + //Util.packWindow (jPanel); + } + }).start (); + } + public JScrollPane getDetails () { + @SuppressWarnings ("serial") + DefaultTableModel dataObjectModel = new DefaultTableModel () { + public Class getColumnClass (int column) { + if (column != 1) + return String.class; + return javax.swing.ImageIcon.class; + } + }; + JTable jTableObject = new JTable (dataObjectModel); + jTableObject.getTableHeader ().setReorderingAllowed (true); + jTableObject.setShowVerticalLines (false); + JScrollPane scrollPane = Util.getJScrollPane (jTableObject, true, false); + jTableObject.setFillsViewportHeight (true); + dataObjectModel.setColumnCount (6); + Util.setColumnLabels (jTableObject, new String[] {"FileName", null, "LocalDate", "LocalSize", "RemoteDate", "RemoteSize"}); + + for (TodoFile action : new TodoFile []{transfertAction, cleanAction}) + for (String fileName : mirror.check (action)) { + FileDescriptor fd = mirror.getInfo (fileName); + FileInfo lfi = fd.local, rfi = fd.remote; + dataObjectModel.addRow (new Object [] {fileName, Util.loadActionIcon (""+fd.todo+Util.ON), + FileInfo.getDate (lfi), FileInfo.getSize (lfi), + FileInfo.getDate (rfi), FileInfo.getSize (rfi)}); + } + scrollPane.setPreferredSize (new java.awt.Dimension (800, 400)); + return scrollPane; + } + public void startAction () { + (new Thread () { + public void run () { + try { + String messagePrefix = transfertAction == TodoFile.Download ? "Download" : "Upload"; + if (!(cleanBoutton.isSelected () || transfertBoutton.isSelected ())) + return; + mirror.update (); + TreeSet removed = new TreeSet (); + if (cleanBoutton.isSelected ()) + removed = mirror.performe (cleanAction, null); + TreeSet transfered = new TreeSet (); + if (transfertBoutton.isSelected ()) { + transfered = mirror.performe (transfertAction, progressState); + mirror.update (); + info.setText (mirror.getInfo (mirror.check (transfertAction), transfertAction)); + } + if (removed.size () == 0 && transfered.size () == 0) + return; + JPanel msgPanel = Util.getGridBagPanel (); + Util.addComponent (new JLabel (MessageFormat.format (Bundle.getMessage (messagePrefix+"Completed"), + transfered.size ())), msgPanel, GBCNL); + if (transfered.size () > 0) { + Util.addLabel (""+transfertAction, LEFT, msgPanel, GBCNL); + Util.addComponent (Util.getJScrollPane (new JList (new Vector (transfered))), + msgPanel, GBCNL); + } + if (removed.size () > 0) { + Util.addLabel ("Remove", LEFT, msgPanel, GBCNL); + Util.addComponent (Util.getJScrollPane (new JList (new Vector (removed))), + msgPanel, GBCNL); + } + JOptionPane.showMessageDialog (jFrame, + msgPanel, + Bundle.getTitle (""+transfertAction), JOptionPane.INFORMATION_MESSAGE); + } finally { + decrThreads (); + } + } + }).start (); + } + }; + private int nbThreads; + private boolean isDownload; + private synchronized void resetThreads (int nbThreads, boolean isDownload) { + if (nbThreads < 1) + return; + while (this.nbThreads != 0) + try { + wait (); + } catch (Exception InterruptedException) { + } + this.isDownload = isDownload; + startAction.setEnabled (false); + this.nbThreads = nbThreads; + } + private synchronized void decrThreads () { + nbThreads--; + if (nbThreads != 0) + return; + notifyAll (); + startAction.setEnabled (true); + if (isDownload && actionAfterDownLoad != null) + actionAfterDownLoad.run (); + } + + // ======================================== + public void dialog (TodoFile action, boolean checkPeriod) { + jPanel.removeAll (); + allMirrorInfo = new ArrayList (); + for (RemoteUpdate.Mirror mirror : remoteUpdate.mirrors) + allMirrorInfo.add (new MirrorInfo (mirror, action)); + for (MirrorInfo mirrorInfo : allMirrorInfo) + mirrorInfo.update (); + startAction = Util.addButton (""+action, null, jPanel, GBCNL); + startAction.addActionListener (new ActionListener () { + public void actionPerformed (ActionEvent e) { + (new Thread () { + public void run () { + resetThreads (allMirrorInfo.size (), action == TodoFile.Download); + for (MirrorInfo mirrorInfo : allMirrorInfo) + mirrorInfo.startAction (); + } + }).start (); + } + }); + if (checkPeriod) { + JPanel line = Util.getGridBagPanel (); + Util.addLabel ("CheckingPeriod", LEFT, line, GBC); + JComboBox periodChooser = Util.addEnum (CheckPeriod.class, remoteUpdate.currentPeriod (), line, GBCNL); + Util.addComponent (line, jPanel, GBCNL); + if (JOptionPane.showConfirmDialog (jFrame, jPanel, Bundle.getTitle (""+action), + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) + Config.setString ("CheckPeriod", ""+CheckPeriod.values () [periodChooser.getSelectedIndex ()]); + } else + JOptionPane.showMessageDialog (jFrame, jPanel, Bundle.getTitle (""+action), JOptionPane. QUESTION_MESSAGE); + for (MirrorInfo mirrorInfo : allMirrorInfo) + mirrorInfo.progressState.abort (); + for (MirrorInfo mirrorInfo : allMirrorInfo) + mirrorInfo.setConfig (); + } + public void downloadDialog () { + dialog (TodoFile.Download, true); + } + public void uploadDialog () { + dialog (TodoFile.Upload, false); + } + + // ======================================== +} diff --git a/src/java/misc/LocalizedUserLabel.java b/src/java/misc/LocalizedUserLabel.java new file mode 100644 index 0000000..2ff802d --- /dev/null +++ b/src/java/misc/LocalizedUserLabel.java @@ -0,0 +1,79 @@ +// ================================================================================ +// François MERCIOL 2015 +// Name : LocalizedUserLabel.java +// Language : Java +// Author : François Merciol +// CopyLeft : Cecil B +// Creation : 2015 +// Version : 0.1 (xx/xx/xx) +// ================================================================================ +package misc; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Locale; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; + +import static misc.Util.GBC; +import static misc.Util.GBCNL; + +public class LocalizedUserLabel { + + // ======================================== + protected boolean isCustomized; + protected String labelName; + protected String currentLabel; + protected String newLabel; + protected JLabel jLabel; + + public String getLabel () { return labelName; } + public String getNewLabel () { return isCustomized ? newLabel : null; } + public String getCurrentLabel () { return currentLabel; } + public JLabel getJLabel () { return jLabel; } + + // ======================================== + public LocalizedUserLabel (String labelName, boolean isLocalized, boolean admin) { + isCustomized = isLocalized && admin; + this.labelName = labelName; + currentLabel = isLocalized ? Bundle.getUser (labelName) : labelName; + jLabel = new JLabel (currentLabel, SwingConstants.RIGHT); + if (!isCustomized) + return; + jLabel.addMouseListener (new MouseAdapter () { + public void mousePressed (MouseEvent e) { + LocalizedUserLabel.this.mousePressed (e); + } + }); + } + + // ======================================== + public void mousePressed (MouseEvent e) { + if (!isCustomized || !SwingUtilities.isRightMouseButton (e)) + return; + Locale locale = Bundle.getLocale (); + ImageIcon flag = Util.loadImageIcon (Config.dataDirname, Config.iconsDirname, Config.flagsDirname, + locale.getCountry ().toLowerCase () + Config.iconsExt); + if (flag != null) + flag.setDescription (locale.toString ()); + JLabel jLocal = new JLabel (locale.toString (), flag, SwingConstants.LEFT); + JTextField jValue = new JTextField (newLabel == null ? Bundle.getUser (labelName) : newLabel, 9); + JPanel content = Util.getGridBagPanel (); + Util.addComponent (new JLabel (), content, GBC); + Util.addComponent (jLocal, content, GBCNL); + Util.addComponent (new JLabel (labelName+" : ", SwingConstants.RIGHT), content, GBC); + Util.addComponent (jValue, content, GBCNL); + if (JOptionPane.showConfirmDialog (e.getComponent (), content, Bundle.getTitle ("Localized"), + JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) + return; + currentLabel = newLabel = jValue.getText (); + jLabel.setText (currentLabel); + } + + // ======================================== +} diff --git a/src/java/misc/Log.java b/src/java/misc/Log.java new file mode 100644 index 0000000..a29a17c --- /dev/null +++ b/src/java/misc/Log.java @@ -0,0 +1,102 @@ +package misc; + +import java.awt.Component; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Hashtable; +import java.util.Vector; +import javax.swing.AbstractButton; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileNameExtensionFilter; + +public class Log { + + // ======================================== + static private final SimpleDateFormat dateFormat = new SimpleDateFormat ("[yyyy/MM/dd HH:mm:ss] "); + // static private final String separator = System.getProperty ("file.separator"); + // static public final String dumpExtension = "log"; + static public final boolean debug = true; + + // ======================================== + static public void writeLog (String serviceName, String message) { + try { + BufferedWriter out = new BufferedWriter + (new FileWriter (Config.getString ("logPath", Config.logSystemDir)+Config.FS+serviceName+Config.logExt, true)); + out.write (dateFormat.format (new Date ())+message+"\n"); + out.flush (); + out.close (); + } catch (IOException e) { + } + } + + // ======================================== + static public Hashtable lastExceptions = new Hashtable (); + static public boolean keepLastException; + + // ======================================== + static public void keepLastException (String where, Throwable t) { + try { + if (debug) + t.printStackTrace (); + ByteArrayOutputStream baos = new ByteArrayOutputStream (); + PrintWriter printWriter = new PrintWriter (baos); + printWriter.println ("Exception:\nDate: "+dateFormat.format (new Date ())+"\n"); + t.printStackTrace (printWriter); + printWriter.flush (); + printWriter.close (); + lastExceptions.put (where, baos.toString ("ISO-8859-1")); + System.err.println ("Exception "+t+" find in "+where+"\nSee log file."); + keepLastException = true; + for (AbstractButton dumpCommand : dumpCommands) + dumpCommand.setEnabled (keepLastException); + } catch (Exception e2) { + } + } + + // ======================================== + static public void dumpLastException (File file) { + PrintWriter printWriter = null; + try { + printWriter = new PrintWriter (new FileWriter (file)); + for (String where : lastExceptions.keySet ()) + printWriter.println ("\n\nContext: "+where+"\n"+lastExceptions.get (where)); + printWriter.flush (); + } catch (Exception e) { + } finally { + try { + printWriter.close (); + } catch (Exception e) { + } + } + } + + // ======================================== + static public void dumpSaveDialog (Component component) { + JFileChooser dumpChooser = + new JFileChooser (Config.getString ("dumpDir", Config.logSystemDir)); + dumpChooser.setFileFilter (new FileNameExtensionFilter (Bundle.getLabel ("DumpFilter"), Config.logExt)); + dumpChooser.setDialogTitle (Bundle.getTitle ("Dump")); + if (dumpChooser.showSaveDialog (component) != JFileChooser.APPROVE_OPTION) + return; + dumpLastException (dumpChooser.getSelectedFile ()); + } + + // ======================================== + static private Vector dumpCommands = new Vector (); + static public void addDumpCommand (AbstractButton dumpCommand) { + if (dumpCommand == null) + return; + dumpCommands.add (dumpCommand); + dumpCommand.setEnabled (keepLastException); + } + static public void removeDumpCommand (AbstractButton dumpCommand) { + dumpCommands.remove (dumpCommand); + } + // ======================================== +} diff --git a/src/java/misc/MultiToolBarBorderLayout.java b/src/java/misc/MultiToolBarBorderLayout.java new file mode 100644 index 0000000..1958fd9 --- /dev/null +++ b/src/java/misc/MultiToolBarBorderLayout.java @@ -0,0 +1,318 @@ +package misc; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.LayoutManager2; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.TreeSet; +import javax.swing.SwingConstants; + +public class MultiToolBarBorderLayout implements LayoutManager2, SwingConstants { + static public final Hashtable positionStringToInt; + static public final Hashtable positionIntToString; + + static { + positionIntToString = new Hashtable (); + positionIntToString.put (CENTER, BorderLayout.CENTER); + positionIntToString.put (NORTH, BorderLayout.NORTH); + positionIntToString.put (EAST, BorderLayout.EAST); + positionIntToString.put (SOUTH, BorderLayout.SOUTH); + positionIntToString.put (WEST, BorderLayout.WEST); + + positionStringToInt = new Hashtable (); + for (int key : positionIntToString.keySet ()) + positionStringToInt.put (positionIntToString.get (key), key); + } + + // ======================================== + int hgap; + int vgap; + Container menu; + Hashtable positions = new Hashtable (); + Component center; + + public MultiToolBarBorderLayout () { + this (0, 0); + } + public MultiToolBarBorderLayout (int hgap, int vgap) { + this.hgap = hgap; + this.vgap = vgap; + } + public int getHgap () { + return hgap; + } + public void setHgap (int hgap) { + this.hgap = hgap; + } + public int getVgap () { + return vgap; + } + public void setVgap (int vgap) { + this.vgap = vgap; + } + public void setMenu (Container menu) { + this.menu = menu; + } + private ArrayList orderedComponents = new ArrayList (); + public void setLayoutOrderedComponents (ArrayList orderedComponents) { + if (orderedComponents== null) + return; + this.orderedComponents = orderedComponents; + } + // ======================================== + public void addLayoutComponent (Component comp, Object constraints) { + synchronized (comp.getTreeLock ()) { + if ((constraints == null) || (constraints instanceof String)) + addLayoutComponent ((String)constraints, comp); + else + throw new IllegalArgumentException ("cannot add to layout: constraint must be a string (or null)"); + } + } + @Deprecated + public void addLayoutComponent (String name, Component comp) { + synchronized (comp.getTreeLock ()) { + if (name == null) + name = BorderLayout.CENTER; + Integer position = positionStringToInt.get (name); + if (position == CENTER) { + if (center != null) + // XXX ou erreur + positions.remove (comp); + center = comp; + } + positions.put (comp, position); + } + } + public void removeLayoutComponent (Component comp) { + synchronized (comp.getTreeLock ()) { + positions.remove (comp); + if (center == comp) + center = null; + } + } + public Object getConstraints (Component comp) { + try { + return positionIntToString.get (positions.get (comp)); + } catch (Exception e) { + return null; + } + } + // ======================================== + public Dimension minimumLayoutSize (Container target) { + return layoutSize (target, true); + } + public Dimension preferredLayoutSize (Container target) { + return layoutSize (target, false); + } + public Dimension maximumLayoutSize (Container target) { + return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE); + } + Hashtable> getSortedComponents (Container target) { + ArrayList allComponents = new ArrayList (orderedComponents); + for (Component c : target.getComponents ()) + if (!allComponents.contains (c)) + allComponents.add (c); + Hashtable> sortedComponents = new Hashtable> (); + for (int pos : positionIntToString.keySet ()) + sortedComponents.put (pos, new ArrayList ()); + for (Component c : allComponents) { + Integer position = positions.get (c); + if (!c.isVisible () || position == null) + continue; + sortedComponents.get (position).add (c); + } + return sortedComponents; + } + int maxSize (ArrayList components, boolean isMinimum, boolean isHorizontal) { + int max = 0; + int componentCount = components.size (); + for (int i = 0; i < componentCount; i++) { + Component c = components.get (i); + Dimension d = isMinimum ? c.getMinimumSize () : c.getPreferredSize (); + max = isHorizontal ? Math.max (max, d.width) : Math.max (max, d.height); + } + return max; + } + TreeSet getIntermediate (ArrayList components, boolean isMinimum, boolean isHorizontal, int minSize) { + ArrayList sizes = new ArrayList (); + int componentCount = components.size (); + for (int i = 0; i < componentCount; i++) { + Component c = components.get (i); + Dimension d = isMinimum ? c.getMinimumSize () : c.getPreferredSize (); + sizes.add (isHorizontal ? d.width : d.height); + } + TreeSet result = new TreeSet (); + int maxComp = componentCount-1; + for (int i = 0; i < componentCount; i++) { + int size = 0; + for (int j = i; j < maxComp; j++) { + size += sizes.get (j); + if (size > minSize) + result.add (size); + } + maxComp = componentCount; + } + return result; + } + void swapAxes (Dimension d) { + int tmp = d.width; + d.width = d.height; + d.height = tmp; + } + Dimension fill (ArrayList components, boolean isMinimum, boolean isHorizontal, int maxWidth) { + int componentCount = components.size (); + int x = 0, y = 0, maxX = 0, rowHeight = 0; + for (int i = 0; i < componentCount; i++) { + Component c = components.get (i); + Dimension d = isMinimum ? c.getMinimumSize () : c.getPreferredSize (); + if (!isHorizontal) + swapAxes (d); + if (x == 0 || x + d.width <= maxWidth) { + if (x > 0) + x += isHorizontal ? hgap : vgap; + x += d.width; + rowHeight = Math.max (rowHeight, d.height); + continue; + } + maxX = Math.max (maxX, x); + x = d.width; + y += (isHorizontal ? vgap : hgap) + rowHeight; + rowHeight = d.height; + } + Dimension result = new Dimension (Math.max (maxX, x), y+rowHeight); + // !!! if !isHorizontal : width => main axes + return result; + } + Dimension getExtra (Hashtable> sortedComponents, + int northPos, int southPos, boolean isMinimum, boolean isHorizontal, + int menuWidth, int extraCenter) { + int minWidth = Util.max (maxSize (sortedComponents.get (CENTER), isMinimum, isHorizontal)+extraCenter, + menuWidth, + maxSize (sortedComponents.get (northPos), isMinimum, isHorizontal), + maxSize (sortedComponents.get (southPos), isMinimum, isHorizontal)); + TreeSet sizes = new TreeSet (); + sizes.add (minWidth); + sizes.addAll (getIntermediate (sortedComponents.get (northPos), isMinimum, isHorizontal, minWidth)); + sizes.addAll (getIntermediate (sortedComponents.get (southPos), isMinimum, isHorizontal, minWidth)); + int minSurface = Integer.MAX_VALUE; + int extraHeight = 0; + for (int size : sizes) { + Dimension northDim = fill (sortedComponents.get (northPos), isMinimum, isHorizontal, size); + Dimension southDim = fill (sortedComponents.get (southPos), isMinimum, isHorizontal, size); + int width = Util.max (size, northDim.width, southDim.width); + int height = northDim.height+southDim.height; + int surface = height*width; + if (surface < minSurface) { + minSurface = surface; + minWidth = width; + extraHeight = height; + } + } + Dimension result = new Dimension (minWidth, extraHeight); + if (!isHorizontal) + swapAxes (result); + return result; + } + Dimension layoutSize (Container target, boolean isMinimum) { + synchronized (target.getTreeLock ()) { + Hashtable> sortedComponents = getSortedComponents (target); + Dimension extraSide = getExtra (sortedComponents, WEST, EAST, isMinimum, false, 0, 0); + int menuWidth = menu == null ? 0 : (isMinimum ? menu.getMinimumSize () : menu.getPreferredSize ()).width; + Dimension extra = getExtra (sortedComponents, NORTH, SOUTH, isMinimum, true, menuWidth, extraSide.width); + Insets insets = target.getInsets (); + return new Dimension (extra.width+insets.left+insets.right, + extraSide.height+extra.height+insets.top+insets.bottom); + } + } + public float getLayoutAlignmentX (Container parent) { + return 0.5f; + } + public float getLayoutAlignmentY (Container parent) { + return 0.5f; + } + // ======================================== + public void invalidateLayout (Container target) { + } + public void layoutContainer (Container target) { + synchronized (target.getTreeLock ()) { + Hashtable> sortedComponents = getSortedComponents (target); + Insets insets = target.getInsets (); + int top = insets.top; + int bottom = target.getHeight () - insets.bottom; + int left = insets.left; + int right = target.getWidth () - insets.right; + + int maxWidth = right-left; + top += setComponents (target, sortedComponents.get (NORTH), insets.top, insets.left, true, maxWidth); + bottom -= setComponents (target, sortedComponents.get (SOUTH), insets.top, insets.left, true, maxWidth); + moveComponents (sortedComponents.get (SOUTH), 0, bottom-insets.top); + + int maxHeight = bottom-top; + left += setComponents (target, sortedComponents.get (WEST), top, insets.left, false, maxHeight); + right -= setComponents (target, sortedComponents.get (EAST), top, insets.left, false, maxHeight); + moveComponents (sortedComponents.get (EAST), right-insets.left, 0); + + if (center != null && center.isVisible ()) + center.setBounds (left, top, right - left, bottom - top); + target.repaint (); + } + } + private void moveComponents (ArrayList components, int dx, int dy) { + int componentCount = components.size (); + for (int i = 0; i < componentCount; i++) { + Component c = components.get (i); + c.setLocation (c.getX ()+dx, c.getY ()+dy); + } + } + private int setComponents (Container target, ArrayList components, int top, int left, boolean isHorizontal, int maxWidth) { + int x = 0, y = 0, rowHeight = 0, rowStart = 0; + int componentCount = components.size (); + for (int i = 0; i < componentCount; i++) { + Component c = components.get (i); + Dimension d = c.getPreferredSize (); + c.setSize (d.width, d.height); + if (!isHorizontal) + swapAxes (d); + if (x == 0 || x + d.width <= maxWidth) { + if (x > 0) + x += isHorizontal ? hgap : vgap; + x += d.width; + rowHeight = Math.max (rowHeight, d.height); + continue; + } + setComponents (target, left, top, components, rowStart, i, isHorizontal, y, rowHeight); + x = d.width; + y += (isHorizontal ? vgap : hgap) + rowHeight; + rowHeight = d.height; + rowStart = i; + } + setComponents (target, left, top, components, rowStart, componentCount, isHorizontal, y, rowHeight); + return y+rowHeight; + } + private void setComponents (Container target, int x, int y, + ArrayList components, int rowStart, int rowEnd, + boolean isHorizontal, int delta, int maxSize) { + for (int i = rowStart; i < rowEnd; i++) { + Component c = components.get (i); + if (isHorizontal) { + int cy = y + delta + (maxSize - c.getHeight ())/2; + c.setLocation (x, cy); + x += c.getWidth () + hgap; + } else { + int cx = x + delta + (maxSize - c.getWidth ())/2; + c.setLocation (cx, y); + y += c.getHeight () + vgap; + } + } + } + // ======================================== + public String toString () { + return getClass ().getName () + "[hgap=" + hgap + ",vgap=" + vgap + "]"; + } +} diff --git a/src/java/misc/OwnFrame.java b/src/java/misc/OwnFrame.java new file mode 100644 index 0000000..b07ae2b --- /dev/null +++ b/src/java/misc/OwnFrame.java @@ -0,0 +1,11 @@ +package misc; + +import java.awt.Image; +import javax.swing.JFrame; + +public interface OwnFrame { + public JFrame getJFrame (); + public String getTitle (); + public Image getIcon (); + public void reborn (); +} diff --git a/src/java/misc/Plugins.java b/src/java/misc/Plugins.java new file mode 100644 index 0000000..09d1d93 --- /dev/null +++ b/src/java/misc/Plugins.java @@ -0,0 +1,165 @@ +package misc; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Vector; +import javax.swing.ImageIcon; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; + +public class Plugins { + + // ======================================== + static public URL getDataUrl (String name) { + try { + File file = (new File (name)); + if (file.exists ()) { + URL result = file.toURI ().toURL (); + return result; + } + } catch (Exception e) { + } + try { + URL result = ClassLoader.getSystemResource (name); + return result; + } catch (Exception e) { + } + return null; + } + // ======================================== + static public ImageIcon loadImageIcon (String name) { + URL url = getDataUrl ("data/images/button/"+name+".png"); + return (url != null) ? new ImageIcon (url) : null; + } + // ======================================== + // Change Util.version too ! + static public final Long version = 20171101L; + static public final String[] stateLabel = {"not found", "old version", "updated"}; + static public final ImageIcon[] stateIcon = { loadImageIcon ("Bad"), loadImageIcon ("Warning"), loadImageIcon ("Good")}; + + static public class PluginInfo { + public String name; + public String url; + public boolean mandatory, loaded, update; + public PluginInfo (String name, String url, boolean mandatory) { + this.name = name; + this.url = url; + this.mandatory = mandatory; + } + }; + + static ArrayList pluginsInfo = new ArrayList (); + + // ======================================== + @SuppressWarnings ("rawtypes") + static public void check (String name, String url, String className, boolean mandatory, Long dateNeed) { + PluginInfo pluginInfo = new PluginInfo (name, url, mandatory); + pluginsInfo.add (pluginInfo); + try { + Class plugin = ClassLoader.getSystemClassLoader ().loadClass (className); + pluginInfo.loaded = true; + pluginInfo.update = + dateNeed == null || + dateNeed <= (Long) plugin.getDeclaredField ("version").get (null); + } catch (Exception e) { + } + } + + // ======================================== + @SuppressWarnings ("serial") + static public class ImageCellRenderer extends JLabel implements TableCellRenderer { + public Component getTableCellRendererComponent (JTable jTable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + if (value instanceof ImageIcon) { + setIcon ((ImageIcon) value); + setText (null); + + } else { + setIcon (null); + setText ((String) value); + } + return this; + } + } + + // ======================================== + static public JDialog jDialog; + static public boolean warning = false; + + static public void showSate () { + Vector> data = new Vector> (); + boolean ready = true; + for (PluginInfo pluginInfo : pluginsInfo) { + ready &= !pluginInfo.mandatory || pluginInfo.loaded; + int state = (pluginInfo.loaded ? (pluginInfo.update ? 2 : 1) : 0); + if (state != 2) + warning = true; + data.add + (new Vector + (Arrays.asList (pluginInfo.name, stateIcon[state], stateLabel[state], (pluginInfo.update ? "" : pluginInfo.url) ))); + } + Vector columnNames = new Vector (Arrays.asList ("Plugin name", "", "State", "URL")); + @SuppressWarnings ("serial") + JTable table = new JTable (data, columnNames) { + public Component prepareRenderer (final TableCellRenderer renderer, final int row, final int column) { + final Component prepareRenderer = super.prepareRenderer (renderer, row, column); + final TableColumn tableColumn = getColumnModel ().getColumn (column); + int width = Math.max (prepareRenderer.getPreferredSize ().width, tableColumn.getPreferredWidth ()); + tableColumn.setPreferredWidth (width); + return prepareRenderer; + } + }; + table.setDefaultRenderer (Object.class, new ImageCellRenderer ()); + + final TableCellRenderer renderer = table.getTableHeader ().getDefaultRenderer (); + for (int i = 0; i < table.getColumnCount (); ++i) { + int width = renderer.getTableCellRendererComponent (table, table.getModel ().getColumnName (i), false, false, 0, i).getPreferredSize ().width; + TableColumn tableColumn = table.getColumnModel ().getColumn (i); + tableColumn.setPreferredWidth (width); + } + + JPanel container = new JPanel (); + container.setLayout (new BorderLayout()); + container.add (table.getTableHeader(), BorderLayout.PAGE_START); + container.add (table, BorderLayout.CENTER); + + if (!ready) { + Dimension containerSize = container.getSize (); + containerSize.width = Math.max (containerSize.width, 600); + containerSize.height = Math.max (containerSize.height, 100); + container.setPreferredSize (containerSize); + + container.add (new JLabel ("Would you like to continue?"), BorderLayout.PAGE_END); + if (JOptionPane.YES_OPTION != + JOptionPane.showConfirmDialog (null, container, " Can't load plugins ", JOptionPane.YES_NO_OPTION)) + System.exit (1); + return; + } + jDialog = new JDialog ((java.awt.Frame)null, " Plugins state ", false); + jDialog.getContentPane ().add (container, BorderLayout.CENTER); + jDialog.setVisible (true); + jDialog.pack (); + jDialog.setLocationRelativeTo (null); + jDialog.toFront (); + } + + static public void hideSate () { + if (jDialog == null) + return; + jDialog.pack (); + //jDialog.toFront (); + if (!warning) + jDialog.setVisible (false); + } + + // ======================================== +} diff --git a/src/java/misc/ProgressState.java b/src/java/misc/ProgressState.java new file mode 100644 index 0000000..1680f5a --- /dev/null +++ b/src/java/misc/ProgressState.java @@ -0,0 +1,67 @@ +package misc; + +import java.util.HashSet; + +public class ProgressState { + + // ======================================== + HashSet workingThreads = new HashSet (); + StateNotifier observable; + String valueName; + + static public enum State {Init, Value, End}; + public State state; + public String domain; + public int value; + public int maxValue; + + // ======================================== + public ProgressState (StateNotifier observable, String valueName) { + this.observable = observable; + this.valueName = valueName; + } + // ======================================== + public synchronized void init (String domain, int maxValue) { + this.domain = domain; + this.value = 0; + this.maxValue = maxValue; + state = State.Init; + workingThreads = new HashSet (); + workingThreads.add (Thread.currentThread ()); + observable.broadcastUpdate (valueName); + } + public synchronized void addThread (Thread thread) { + // XXX test ! State.end + workingThreads.add (thread); + } + // ======================================== + public synchronized void abort () { + workingThreads.clear (); + } + // ======================================== + public synchronized boolean isInterrupted () { + return !workingThreads.contains (Thread.currentThread ()); + } + // ======================================== + public synchronized boolean setValue (int value) { + this.value = value; + state = State.Value; + observable.broadcastUpdate (valueName); + return workingThreads.contains (Thread.currentThread ()); + } + // ======================================== + public synchronized boolean addValue (int delta) { + if (delta > 0) { + value += delta; + state = State.Value; + observable.broadcastUpdate (valueName); + } + return workingThreads.contains (Thread.currentThread ()); + } + // ======================================== + public synchronized void end () { + state = State.End; + observable.broadcastUpdate (valueName); + } + // ======================================== +} diff --git a/src/java/misc/ProgressStatePanel.java b/src/java/misc/ProgressStatePanel.java new file mode 100644 index 0000000..a6313ab --- /dev/null +++ b/src/java/misc/ProgressStatePanel.java @@ -0,0 +1,78 @@ +// ================================================================================ +// Copyright (c) Francois Merciol 2002 +// Project : Classe +// Name : DatePanel.java +// Language : Java +// Type : Source file for DatePanel class +// Author : Francois Merciol +// Creation : 02/09/2002 +// ================================================================================ +// History +// -------------------------------------------------------------------------------- +// Author : +// Date : +// Reason : +// ================================================================================ +// To be improved : +// ================================================================================ +package misc; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JProgressBar; + +@SuppressWarnings ("serial") public class ProgressStatePanel extends JPanel implements ActionListener { + + // ======================================== + JProgressBar jProgressBar = new JProgressBar (); + JButton stopButton = new JButton (Util.loadActionIcon ("Abort")); + ProgressState progressState; + + // ======================================== + public ProgressStatePanel (ProgressState progressState) { + this.progressState = progressState; + jProgressBar.setMaximum (1); + jProgressBar.setString (""); + jProgressBar.setStringPainted (true); + jProgressBar.setAlignmentX (Component.LEFT_ALIGNMENT); + + setLayout (new BorderLayout ()); + add (jProgressBar, BorderLayout.CENTER); + stopButton.setActionCommand ("Abort"); + stopButton.addActionListener (this); + stopButton.setEnabled (false); + add (stopButton, BorderLayout.LINE_END); + } + + public void actionPerformed (ActionEvent e) { + if (e.getSource () != stopButton) + return; + progressState.abort (); + } + + // ======================================== + public void updateProgress () { + switch (progressState.state) { + case Value: + jProgressBar.setValue (progressState.value); + break; + case Init: + jProgressBar.setString (progressState.domain); + jProgressBar.setValue (progressState.value); + jProgressBar.setMaximum (progressState.maxValue); + stopButton.setEnabled (true); + break; + case End: + jProgressBar.setString (""); + jProgressBar.setValue (0); + stopButton.setEnabled (false); + break; + } + } + + // ======================================== +} diff --git a/src/java/misc/ProxyManager.java b/src/java/misc/ProxyManager.java new file mode 100644 index 0000000..09cb21e --- /dev/null +++ b/src/java/misc/ProxyManager.java @@ -0,0 +1,110 @@ +package misc; + +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import static misc.Util.GBCNL; + +@SuppressWarnings ("serial") +public class ProxyManager implements ApplicationManager, ActionListener { + static public final String + actionSetProxy = "SetProxy", + actionNoProxy = "NoProxy", + actionSystemConfigProxy = "SystemConfigProxy", + actionManualConfigProxy = "ManualConfigProxy"; + + static public final List radioButtonNames = Arrays.asList (actionNoProxy, actionSystemConfigProxy, actionManualConfigProxy); + static public final List actionsNames = Arrays.asList (actionSetProxy); + @SuppressWarnings("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (ProxyManager.class, actionsNames, radioButtonNames); + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + // ======================================== + public void addMenuItem (JMenu... jMenu) { + Util.addMenuItem (actionsNames, this, jMenu[0]); + } + public void addIconButtons (Container... containers) { + Util.addIconButton (actionsNames, this, containers[0]); + } + public void addActiveButtons (Hashtable buttons) { + // disable if no network ? + } + JPanel manualPanel; + private String getConfig () { + if ("true".equals (System.getProperty ("java.net.useSystemProxies"))) + return actionSystemConfigProxy; + String host = System.getProperty ("http.proxyHost"); + String port = System.getProperty ("http.proxyPort"); + if (host == null || port == null || host.isEmpty () || port.isEmpty ()) + return actionNoProxy; + return actionManualConfigProxy; + } + public void actionSetProxy () { + JFrame jFrame = new JFrame (); + jFrame.setIconImage (controller.getIcon ()); + JPanel msgPanel = Util.getGridBagPanel (); + String proxyConfig = getConfig (); + ButtonGroup group = new ButtonGroup (); + Util.addRadioButton (actionNoProxy, this, group, proxyConfig, msgPanel, GBCNL); + Util.addRadioButton (actionSystemConfigProxy, this, group, proxyConfig, msgPanel, GBCNL); + Util.addRadioButton (actionManualConfigProxy, this, group, proxyConfig, msgPanel, GBCNL); + manualPanel = Util.getGridBagPanel (); + JTextField hostTF = new JTextField (Config.getString ("ProxyHostName", "squid"), 18); + JTextField portTF = new JTextField (Config.getString ("ProxyPort", "3128"), 6); + // System.setProperty("http.proxyUser", "user"); + // System.setProperty("http.proxyPassword", "password"); + Util.addLabelFields (manualPanel, "Host", hostTF); + Util.addLabelFields (manualPanel, "Port", portTF); + Util.addComponent (manualPanel, msgPanel, GBCNL); + Util.setEnabled (manualPanel, proxyConfig.equals (actionManualConfigProxy)); + if (JOptionPane.showConfirmDialog (jFrame, msgPanel, misc.Bundle.getTitle ("Proxy"), JOptionPane.YES_NO_OPTION) + != JOptionPane.YES_OPTION) + return; + Config.setString ("ProxyHostName", hostTF.getText ()); + Config.setString ("ProxyPort", portTF.getText ()); + proxyConfig = group.getSelection ().getActionCommand (); + if (actionSystemConfigProxy.equals (proxyConfig)) { + System.setProperty ("java.net.useSystemProxies", "true"); + System.setProperty ("http.proxyHost", ""); + } else { + System.setProperty ("java.net.useSystemProxies", "false"); + if (actionNoProxy.equals (proxyConfig)) + System.setProperty ("http.proxyHost", ""); + else if (actionManualConfigProxy.equals (proxyConfig)) { + System.setProperty ("http.proxyHost", hostTF.getText ()); + System.setProperty ("http.proxyPort", portTF.getText ()); + } + } + java.net.ProxySelector.setDefault (java.net.ProxySelector.getDefault ()); + } + public void actionNoProxy () { + Util.setEnabled (manualPanel, false); + } + public void actionSystemConfigProxy () { + Util.setEnabled (manualPanel, false); + } + public void actionManualConfigProxy () { + Util.setEnabled (manualPanel, true); + } + + // ======================================== + private OwnFrame controller; + public ProxyManager (OwnFrame controller) { + this.controller = controller; + } + // ======================================== +} diff --git a/src/java/misc/RealTime.java b/src/java/misc/RealTime.java new file mode 100644 index 0000000..050b92c --- /dev/null +++ b/src/java/misc/RealTime.java @@ -0,0 +1,73 @@ +package misc; + +@SuppressWarnings ("serial") +class NewChalenger extends RuntimeException { + public NewChalenger () { + super ("NewChalenger"); + } +} + +public class RealTime { + // ======================================== + private int nbChalengers; + private Thread leadership; + + public void start (Runnable task) { + (new Thread () { + public void run () { + if (!getLeadership ()) + return; + try { + task.run (); + } catch (NewChalenger e) { + } finally { + loseLeaderShip (); + } + } + }).start (); + } + + // ======================================== + public synchronized void waitTerminaison () { + if (leadership == Thread.currentThread ()) + return; + while (leadership != null && nbChalengers > 0) + try { + wait (); + } catch (InterruptedException e) { + } + } + + // ======================================== + public synchronized boolean getLeadership () { + if (leadership != null) { + nbChalengers++; + try { + wait (); + } catch (InterruptedException e) { + } + nbChalengers--; + if (nbChalengers != 0) { + notifyAll (); + return false; + } + } + leadership = Thread.currentThread (); + return true; + } + public synchronized void loseLeaderShip () { + leadership = null; + notifyAll (); + } + public synchronized void checkChalengers () { + if (nbChalengers != 0) + throw new NewChalenger (); + } + + // ======================================== + static public void checkChalengers (RealTime realTime) { + if (realTime != null) + realTime.checkChalengers (); + } + // ======================================== +} diff --git a/src/java/misc/RemoteUpdate.java b/src/java/misc/RemoteUpdate.java new file mode 100644 index 0000000..3647309 --- /dev/null +++ b/src/java/misc/RemoteUpdate.java @@ -0,0 +1,748 @@ +package misc; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.CookiePolicy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Random; +import java.util.TimeZone; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +public class RemoteUpdate { + + // ======================================== + static long delay = 2000; + static CookieManager cookieManager = new CookieManager(); + static { + try { + ((CookieManager) CookieHandler.getDefault ()).setCookiePolicy (CookiePolicy.ACCEPT_ALL); + } catch (Exception e) { + cookieManager.setCookiePolicy (CookiePolicy.ACCEPT_ALL); + CookieHandler.setDefault (cookieManager); + } + } + + // ======================================== + static public final String backExtention = "back"; + static public final int bufSize = 1024*1024; + static public final SimpleDateFormat dateFormat = new SimpleDateFormat ("yyyyMMdd"); + static public final SimpleDateFormat timeFormat = new SimpleDateFormat ("yyyyMMddHHmmss"); + static public final SimpleDateFormat displayFormat = new SimpleDateFormat ("dd/MM/yyyy HH:mm:ss"); + static public final NumberFormat numberFormat = NumberFormat.getInstance (); + + static public enum CheckPeriod { NoCheck, Day, Week, Month, Year; }; + static public final List excludeFileName = Arrays.asList ("timestamp"); + + static public enum TodoFile {LocalRemove, Download, NoChange, Upload, RemoteRemove}; + static public class FileInfo { + long date, size; + public FileInfo (long date, long size) { this.date = date; this.size = size; } + public String toString () { return "{"+displayFormat.format (new Date (date))+" "+size+"}"; } + static public String getDate (FileInfo fileInfo) { return fileInfo == null ? "" : displayFormat.format (fileInfo.date); } + static public String getSize (FileInfo fileInfo) { return fileInfo == null ? "" : ""+fileInfo.size; } + }; + + static public FilenameFilter mirrorFilter = new FilenameFilter () { + public boolean accept (File dir, String name) { + return ! (excludeFileName.contains (name) || name.endsWith ("."+backExtention) || name.endsWith (".new")); + } + }; + + // ======================================== + static public class FileDescriptor { + // ---------------------------------------- + public TodoFile todo = TodoFile.NoChange; + public FileInfo local, remote; + public String toString () { return ""+todo+" "+local+" "+remote; } + // ---------------------------------------- + public void updateTodo (TodoFile action) { + todo = TodoFile.NoChange; + if (remote == null) { + switch (action) { + case LocalRemove: + case Upload: + todo = action; + case Download: + break; + case NoChange: + case RemoteRemove: + break; + } + return; + } + if (local == null) { + switch (action) { + case RemoteRemove: + case Download: + todo = action; + case LocalRemove: + break; + case NoChange: + case Upload: + break; + } + return; + } + if (local.size != remote.size) { + switch (action) { + case LocalRemove: + todo = TodoFile.Download; + break; + case RemoteRemove: + todo = TodoFile.Upload; + break; + default: + todo = action; + } + return; + } + if (local.date+delay < remote.date) { + if (action == TodoFile.Download || action == TodoFile.LocalRemove) + todo = TodoFile.Download; + return; + } + if (local.date > remote.date+delay) { + if (action == TodoFile.Upload || action == TodoFile.RemoteRemove) + todo = TodoFile.Upload; + //System.err.print (" "+local.date+" "+remote.date+" "+action); + return; + } + } + // ---------------------------------------- + }; + + // ======================================== + public class Mirror { + String token; + File localRepository; + FilenameFilter mirrorFilter = RemoteUpdate.mirrorFilter; + TreeSet excludeFiles; + TreeMap localFiles, remoteFiles; + TreeMap allFiles; + + // ---------------------------------------- + public Mirror (File root, String... params) { + token = params[0]; + if (params.length > 1) { + localRepository = root; + for (int i = 1; i < params.length; i++) + localRepository = new File (localRepository, params[i]); + } else + localRepository = new File (root, token); + } + public String toString () { return token+": "+localFiles+" "+remoteFiles; } + public synchronized void update () { + updateRemoteFiles (); + updateLocalFiles (); + if (remoteFiles == null) + return; + TreeSet allFilesName = new TreeSet (); + allFilesName.addAll (localFiles.keySet ()); + allFilesName.addAll (remoteFiles.keySet ()); + allFiles = new TreeMap (); + for (String fileName : allFilesName) + allFiles.put (fileName, new FileDescriptor ()); + for (String fileName : localFiles.keySet ()) + allFiles.get (fileName).local = localFiles.get (fileName); + for (String fileName : remoteFiles.keySet ()) + allFiles.get (fileName).remote = remoteFiles.get (fileName); + } + public void updateTodo (TodoFile action) { + if (allFiles == null) + return; + for (String fileName : allFiles.keySet ()) { + //System.err.print ("coucou : "+fileName+" "); + allFiles.get (fileName).updateTodo (action); + //System.err.println (); + } + } + public long getSize (TreeSet filesName, TodoFile action) { + long result = 0; + if (allFiles == null || action == TodoFile.NoChange) + return result; + boolean remote = false; + switch (action) { + case LocalRemove: + case Download: + remote = true; + case NoChange: + break; + case RemoteRemove: + case Upload: + break; + } + for (String fileName : filesName) { + FileDescriptor file = allFiles.get (fileName); + if (file == null) + continue; + try { + if (file.todo == action) + result += remote ? file.remote.size : file.local.size; + } catch (Exception e) { + } + } + return result; + } + public TreeSet getFileAction (TodoFile action) { + TreeSet result = new TreeSet (); + if (allFiles == null) + return result; + for (String fileName : allFiles.keySet ()) + if (allFiles.get (fileName).todo == action) + result.add (fileName); + return result; + } + public synchronized boolean hasNewVersion () { + update (); + return check (TodoFile.Download).size () > 0; + } + public synchronized TreeSet check (TodoFile action) { + updateTodo (action); + return getFileAction (action); + } + public TreeSet performe (TodoFile action, ProgressState progressState) { + try { + TreeSet files = check (action); + if (progressState != null) + progressState.init (Bundle.getMessage (""+action), (int) getSize (files, action)); + switch (action) { + case LocalRemove: + return localRemove (this, files, progressState); + case RemoteRemove: + return remoteRemove (this, files, progressState); + case Download: + return getRemoteFiles (this, files, progressState); + case Upload: + return putRemoteFiles (this, files, progressState); + case NoChange: + break; + } + } finally { + if (progressState != null) + progressState.end (); + } + return new TreeSet (); + } + public String getInfo (TreeSet files, TodoFile action) { + return MessageFormat.format (Bundle.getMessage ("DownloadInfo"), + Bundle.getLabel (Util.toCapital (token)), + allFiles == null ? 0 : 1, files.size (), Util.toNumIn2Units (getSize (files, action))); + } + public FileDescriptor getInfo (String fileName) { + return allFiles.get (fileName); + } + + // ---------------------------------------- + private void updateLocalFiles () { + excludeFiles = new TreeSet (); + localFiles = new TreeMap (); + updateLocalFiles (localRepository, token); + } + private void updateLocalFiles (File localRepository, String name) { + if (localRepository.isFile ()) { + if (excludeFileName.contains (name) || name.endsWith ("."+backExtention)) { + excludeFiles.add (name); + return; + } + localFiles.put (name, new FileInfo (localRepository.lastModified (), localRepository.length ())); + return; + } + if (!localRepository.isDirectory ()) + return; + for (String child : localRepository.list ()) { + updateLocalFiles (new File (localRepository, child), name+"/"+child); + } + } + private void getExcludeFiles (File localRepository, String name) { + if (localRepository.isFile ()) { + localFiles.put (name, new FileInfo (localRepository.lastModified (), localRepository.length ())); + return; + } + if (!localRepository.isDirectory ()) + return; + for (String child : localRepository.list (mirrorFilter)) { + if (excludeFileName.contains (child)) + continue; + updateLocalFiles (new File (localRepository, child), name+"/"+child); + } + } + // ---------------------------------------- + private void updateRemoteFiles () { + try { + allFiles = null; + remoteFiles = null; + URLConnection urlConnection = getUrlConnection ("zipList", token); + urlConnection.connect (); + remoteFiles = new TreeMap (); + receiveZip (urlConnection, new ZipLineReader () { + public void readLine (String line) { + ParsePosition pos = new ParsePosition (0); + try { + long date = timeFormat.parse (line, pos).getTime (); + pos.setIndex (pos.getIndex ()+1); + long size = numberFormat.parse (line, pos).longValue (); + String fileName = line.substring (pos.getIndex ()+1); + remoteFiles.put (fileName, new FileInfo (date, size)); + } catch (Exception e) { + } + }}, token); + } catch (Exception e) { + System.err.println (e); + //e.printStackTrace (); + } + } + // ---------------------------------------- + }; + + // ======================================== + public String protocol; + public String serverName; + public String versionName; + public String requestModel; + public Mirror[] mirrors; + public RemoteUpdate (String protocol, String serverName, String versionName, String requestModel, String []... mirrorsParams) { + this.protocol = protocol; + this.serverName = serverName; + this.versionName = versionName; + this.requestModel = requestModel; + File root = Config.getPWD ().getParentFile (); + this.mirrors = new Mirror[mirrorsParams.length]; + int idx = 0; + for (String [] params : mirrorsParams) + mirrors[idx++] = new Mirror (root, params); + } + public CheckPeriod currentPeriod () { + return CheckPeriod.valueOf (CheckPeriod.class, Config.getString ("CheckPeriod", ""+CheckPeriod.Month)); + } + public boolean hasNewVersion () { + CheckPeriod period = currentPeriod (); + Date lastCheck = new Date (); + try { + lastCheck = dateFormat.parse (Config.getString ("LastCheck", "20150401")); + } catch (Exception e) { + } + Date today = new Date (); + long age = (today.getTime ()-lastCheck.getTime ())/(24*60*60*1000); + switch (period) { + case NoCheck: + return false; + case Day: + if (age < 1) + return false; + break; + case Week: + if (age < 7) + return false; + break; + case Month: + if (age < 31) + return false; + break; + case Year: + if (age < 366) + return false; + break; + } + Config.setString ("LastCheck", dateFormat.format (today)); + for (Mirror mirror : mirrors) + if (mirror.hasNewVersion ()) + return true; + return false; + } + + // ======================================== + private URLConnection getUrlConnection (String cmd, String arg) + throws IOException { + return getUrlConnection (MessageFormat.format (requestModel, versionName, cmd, arg)); + } + private URLConnection getUrlConnection (String uri) + throws IOException { + URL url = new URL (protocol+"://"+serverName+uri); + URLConnection urlConnection = url.openConnection (); + urlConnection.setRequestProperty ("User-Agent", "Adecwatt Agent"); + urlConnection.setUseCaches (true); + return urlConnection; + } + + static private Random random = new Random (); + static private final String charset = "UTF-8"; + static private final String LINE_FEED = "\r\n"; + static public String getBoundary () { + // XXX we must check not apears in message :-( + return "=-="+Long.toString (random.nextLong (), 36).toUpperCase ()+"=-="; + } + + // ======================================== + public abstract class ZipBuilder { + public abstract void writeEntry (String token, ZipOutputStream zipOut) throws IOException; + }; + public class TextZipBuilder extends ZipBuilder { + private TreeSet filesName; + public TextZipBuilder (TreeSet filesName) { this.filesName = filesName; } + public void writeEntry (String token, ZipOutputStream zipOut) throws IOException { + ZipEntry zipOutEntry = new ZipEntry (token); + zipOut.putNextEntry (zipOutEntry); + PrintWriter entryWriter = new PrintWriter (new OutputStreamWriter (zipOut)); + for (String fileName : filesName) + entryWriter.print (fileName+"\n"); + entryWriter.flush (); + zipOut.closeEntry (); + } + }; + public abstract class ZipFileReader { + public abstract boolean readEntry (ZipInputStream zipIn, String entryName, String token) throws IOException; + }; + public abstract class ZipLineReader extends ZipFileReader { + public boolean readEntry (ZipInputStream zipIn, String entryName, String token) throws IOException { + if (! entryName.equals (token)) + return true; + BufferedReader in = new BufferedReader (new InputStreamReader (zipIn)); + for (;;) { + String line = in.readLine (); + if (line == null) + break; + if ("".equals (line)) + continue; + readLine (line); + } + return false; + } + + public abstract void readLine (String line); + }; + + // ======================================== + public void receiveZip (URLConnection urlConnection, ZipFileReader zipFileReader, String token) { + try { + String contentType = urlConnection.getContentType (); + if (contentType == null || !contentType.startsWith ("application/zip")) + return; + ZipInputStream zipIn = new ZipInputStream (urlConnection.getInputStream ()); + for (;;) { + ZipEntry zipInEntry = zipIn.getNextEntry (); + if (zipInEntry == null) + break; + String entryName = zipInEntry.getName (); + if (!entryName.startsWith (token)) + continue; + if (!zipFileReader.readEntry (zipIn, entryName, token)) + break; + } + } catch (Exception e) { + e.printStackTrace (); + } + } + + // ======================================== + public URLConnection sendZip (String cmd, String token, ZipBuilder zipBuilder) { + try { + URLConnection urlConnection = getUrlConnection (cmd, token); + String boundary = getBoundary (); + urlConnection.setRequestProperty ("Content-Type", "multipart/form-data; boundary="+boundary); + urlConnection.setDoOutput (true); + urlConnection.setDoInput (true); + OutputStream httpStream = urlConnection.getOutputStream (); + PrintWriter httpWriter = new PrintWriter (new OutputStreamWriter (httpStream, charset), true); + httpWriter.append ("--"+boundary).append (LINE_FEED); + httpWriter.append ("Content-Disposition: form-data; name=\""+cmd+"\"; filename=\""+cmd+"\"").append (LINE_FEED); + httpWriter.append ("Content-Type: application/zip").append (LINE_FEED); + httpWriter.append ("Content-Transfer-Encoding: binary").append (LINE_FEED); + httpWriter.append (LINE_FEED).flush (); + ByteArrayOutputStream memStream = new ByteArrayOutputStream (); + ZipOutputStream zipOut = new ZipOutputStream (memStream); + + zipBuilder.writeEntry (token, zipOut); + + zipOut.flush (); + zipOut.close (); + httpWriter.flush (); + httpStream.write (memStream.toByteArray ()); + httpWriter.append (LINE_FEED).flush (); + httpWriter.append ("--"+boundary+"--").append(LINE_FEED); + httpWriter.close (); + + urlConnection.connect (); + return urlConnection; + } catch (Exception e) { + e.printStackTrace (); + } + return null; + } + + // ======================================== + public TreeSet localRemove (Mirror mirror, TreeSet filesName, ProgressState progressState) { + if (filesName == null) + filesName = new TreeSet (); + if (mirror.excludeFiles != null) + filesName.addAll (mirror.excludeFiles); + TreeSet removed = new TreeSet (); + for (String fileName : filesName) { + if (! fileName.startsWith (mirror.token)) + continue; + fileName = fileName.substring (mirror.token.length ()); + File oldFile = new File (mirror.localRepository.getAbsolutePath ()+fileName); + if (!oldFile.delete ()) + System.err.println ("coucou pb delete: "+oldFile); + removed.add (fileName); + } + return removed; + } + + // ======================================== + public TreeSet remoteRemove (Mirror mirror, TreeSet filesName, ProgressState progressState) { + if (filesName == null) + filesName = new TreeSet (); + if (mirror.excludeFiles != null) + filesName.addAll (mirror.excludeFiles); + TreeSet removed = new TreeSet (); + if (filesName.size () < 1) + return removed; + URLConnection urlConnection = sendZip ("zipRemove", mirror.token, new TextZipBuilder (filesName)); + receiveZip (urlConnection, new ZipLineReader () { + public void readLine (String line) { + removed.add (line); + }}, mirror.token); + return removed; + } + + // ======================================== + public TreeSet getRemoteFiles (Mirror mirror, TreeSet filesName, ProgressState progressState) { + TreeSet downloaded = new TreeSet (); + try { + if (filesName.size () < 1) + return downloaded; + URLConnection urlConnection = sendZip ("zipGets", mirror.token, new TextZipBuilder (filesName)); + byte[] tmp = new byte[bufSize]; + receiveZip (urlConnection, new ZipFileReader () { + public boolean readEntry (ZipInputStream zipIn, String entryName, String token) throws IOException { + File newFile = mirror.localRepository; + for (String item : entryName.substring (mirror.token.length ()).split ("/")) { + if (item == null || item.isEmpty ()) + continue; + newFile = new File (newFile, item); + } + File dirFile = newFile.getParentFile (); + dirFile.mkdirs (); + File tmpFile = File.createTempFile ("download", "tmp", dirFile); + tmpFile.deleteOnExit (); + Util.copy (zipIn, new FileOutputStream (tmpFile), tmp, progressState, false, true); + // XXX tmpFile.setLastModified (zipInEntry.getLastModifiedTime ().toMillis ()); + tmpFile.setLastModified (mirror.allFiles.get (entryName).remote.date); + if (progressState != null && progressState.isInterrupted ()) + return false; + Util.backup (newFile, Util.getExtention (newFile), backExtention); + try { + Files.move (tmpFile.toPath (), newFile.toPath (), StandardCopyOption.REPLACE_EXISTING); + } catch (Exception e) { + } + if (tmpFile.exists ()) { + System.err.println ("can't change open file!"); + tmpFile.renameTo (new File (""+newFile+".new")); + } + downloaded.add (entryName); + return true; + }}, mirror.token); + } catch (Exception e) { + e.printStackTrace (); + } + return downloaded; + } + + // ======================================== + public static boolean newFileExists (File file) { + if (file.isFile () && file.exists () && "new".equals (Util.getExtention (file))) + return true; + if (!file.isDirectory ()) + return false; + for (File subFile : file.listFiles (new FileFilter () { + public boolean accept (File file2) { + return file2.isDirectory () || (file2.isFile () && "new".equals (Util.getExtention (file2))); + } + })) + if (newFileExists (subFile)) + return true; + return false; + } + public static void renameNewFile (File file) { + if (file.isFile () && file.exists () && "new".equals (Util.getExtention (file))) { + try { + File newFile = new File (file.getParentFile (), Util.getBase (file)); + Files.move (file.toPath (), newFile.toPath (), StandardCopyOption.REPLACE_EXISTING); + } catch (Exception e) { + } + } + if (!file.isDirectory ()) + return; + for (File subFile : file.listFiles (new FileFilter () { + public boolean accept (File file2) { + return file2.isDirectory () || (file2.isFile () && "new".equals (Util.getExtention (file2))); + } + })) + renameNewFile (subFile); + } + + // ======================================== + public static void launch (File jarFile) { + try { + Runtime.getRuntime ().exec ("java -jar "+jarFile.getName (), null, jarFile.getParentFile ()); + System.exit (0); + } catch (Exception e) { + e.printStackTrace (); + } + } + + // ======================================== + public TreeSet putRemoteFiles (Mirror mirror, TreeSet filesName, ProgressState progressState) { + TreeSet uploaded = new TreeSet (); + if (filesName == null || filesName.size () < 1) + return uploaded; + try { + byte[] tmp = new byte[bufSize]; + TimeZone timeZone = TimeZone.getDefault (); + URLConnection urlConnection = sendZip ("zipPuts", mirror.token, new ZipBuilder () { + public void writeEntry (String token, ZipOutputStream zipOut) throws IOException { + for (String fileName : filesName) { + if (! fileName.startsWith (token)) + continue; + ZipEntry zipOutEntry = new ZipEntry (fileName); + File file = new File (mirror.localRepository, fileName.substring (token.length ())); + long dateMs = file.lastModified (); + dateMs -= timeZone.getOffset (dateMs); + zipOutEntry.setTime (dateMs); + zipOut.putNextEntry (zipOutEntry); + PrintWriter entryWriter = new PrintWriter (new OutputStreamWriter (zipOut)); + Util.copy (new FileInputStream (file), zipOut, tmp, progressState, true, false); + entryWriter.flush (); + zipOut.closeEntry (); + } + }}); + receiveZip (urlConnection, new ZipLineReader () { + public void readLine (String line) { + uploaded.add (line); + }}, mirror.token); + } catch (Exception e) { + e.printStackTrace (); + } + return uploaded; + } + + // ======================================== + public String getRoles (String login) { + try { + URLConnection urlConnection = getUrlConnection ("getRoles", login); + urlConnection.connect (); + BufferedReader br = new BufferedReader (new InputStreamReader (urlConnection.getInputStream ())); + String roles = br.readLine (); + for (;;) { + String line = br.readLine (); + if (line == null) + break; + } + if (roles.indexOf ("|") < 0) + return null; + return roles; + } catch (Exception e) { + return null; + } + } + public void logoutDokuwiki () { + try { + URLConnection urlConnection = getUrlConnection ("?do=logout"); + urlConnection.connect (); + forceOpenConnection (urlConnection); + } catch (Exception e) { + } + } + public void loginDokuwiki (String login, String password) { + try { + // page de connexion + URLConnection urlConnection = getUrlConnection ("?do=login"); + urlConnection.connect (); + String sectok = null; + BufferedReader br = new BufferedReader (new InputStreamReader (urlConnection.getInputStream ())); + for (;;) { + String line = br.readLine (); + if (line == null) + break; + if (line.indexOf ("name=\"sectok\"") < 0) + continue; + Pattern p = Pattern.compile (".*<([^<]*name=\"sectok\"[^>]*)>.*"); + Matcher m = p.matcher (line); + if (!m.matches ()) + break; + line = m.group (1); + p = Pattern.compile (".*value=\"([^\"]*)\".*"); + m = p.matcher (line); + if (!m.matches ()) + break; + sectok = m.group (1); + break; + } + if (sectok == null) + return; + // envoie de mot de passe + urlConnection = getUrlConnection ("?do=login"); + urlConnection.setDoOutput (true); + urlConnection.setDoInput (true); + urlConnection.setRequestProperty ("Content-Type", "application/x-www-form-urlencoded"); + String post = + "sectok="+sectok+"&"+ + "id=debut&"+ + "do=login&"+ + "u="+URLEncoder.encode (login, charset)+"&"+ + "p="+URLEncoder.encode (password, charset); + urlConnection.setRequestProperty ("Content-Length", ""+post.getBytes ().length); + OutputStream httpStream = urlConnection.getOutputStream (); + PrintWriter httpWriter = new PrintWriter (new OutputStreamWriter (httpStream, charset), true); + httpWriter.append (post).append (LINE_FEED).flush (); + urlConnection.connect (); + forceOpenConnection (urlConnection); + } catch (Exception e) { + e.printStackTrace (); + } + } + + // ======================================== + static public void forceOpenConnection (URLConnection urlConnection) { + try { + BufferedReader br = new BufferedReader (new InputStreamReader (urlConnection.getInputStream ())); + for (;;) { + String line = br.readLine (); + if (line == null) + break; + } + } catch (Exception e) { + } + } + + // ======================================== +} diff --git a/src/java/misc/RemoteUpdateManager.java b/src/java/misc/RemoteUpdateManager.java new file mode 100644 index 0000000..28b4cac --- /dev/null +++ b/src/java/misc/RemoteUpdateManager.java @@ -0,0 +1,74 @@ +package misc; + +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import javax.swing.AbstractButton; +import javax.swing.JMenu; + +@SuppressWarnings ("serial") +public class RemoteUpdateManager implements ApplicationManager, ActionListener { + + // ======================================== + static public final String + actionUpdate = "Update", + actionOnline = "Online"; + static public final List actionsNames = Arrays.asList (actionUpdate, actionOnline); + @SuppressWarnings("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (RemoteUpdateManager.class, actionsNames); + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + + // ======================================== + public void addMenuItem (JMenu... jMenu) { + Util.addMenuItem (actionsNames, this, jMenu[0]); + } + public void addIconButtons (Container... containers) { + Util.addIconButton (actionsNames, this, containers[0]); + } + private ArrayList enabledConnectedButtons = new ArrayList (); + public void addActiveButtons (Hashtable buttons) { + enabledConnectedButtons.add (buttons.get (actionOnline)); + updateActiveButtons (); + } + public void updateActiveButtons () { + for (AbstractButton button : enabledConnectedButtons) + button.setEnabled (connected); + } + public void actionUpdate () { + jRemoteUpdate.downloadDialog (); + } + public void actionOnline () { + jRemoteUpdate.uploadDialog (); + } + + // ======================================== + private boolean connected = false; + private RemoteUpdate remoteUpdate; + private JRemoteUpdate jRemoteUpdate; + + public void setConnected (boolean connected) { this.connected = connected; updateActiveButtons (); } + public RemoteUpdate getRemoteUpdate () { return remoteUpdate; } + public JRemoteUpdate getJRemoteUpdate () { return jRemoteUpdate; } + + public RemoteUpdateManager (OwnFrame controller, RemoteUpdate remoteUpdate, Runnable actionAfterUpdate) { + this.remoteUpdate = remoteUpdate; + jRemoteUpdate = new JRemoteUpdate (controller, remoteUpdate, actionAfterUpdate); + } + + public void check () { + (new Thread () { public void run () { + if (remoteUpdate.hasNewVersion ()) + jRemoteUpdate.downloadDialog (); + } }).start (); + } + + // ======================================== +} diff --git a/src/java/misc/ScaledImage.java b/src/java/misc/ScaledImage.java new file mode 100644 index 0000000..eedd252 --- /dev/null +++ b/src/java/misc/ScaledImage.java @@ -0,0 +1,238 @@ +// ================================================================================ +// François MERCIOL 2015 +// Name : ScaledImage.java +// Language : Java +// Author : François Merciol +// CopyLeft : Cecil B +// Creation : 2015 +// Version : 0.1 (xx/xx/xx) +// ================================================================================ +package misc; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; +import java.awt.geom.Point2D; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.util.Hashtable; +import javax.swing.ImageIcon; + +public class ScaledImage { + + static public class ImageInfo { + public AffineTransform at; + public double [] bounds; + public double x, y; + Hashtable tiles = new Hashtable (); + public ImageInfo (AffineTransform at, double [] bounds, double x, double y) { + this.at = at; + this.bounds = bounds; + this.x = x; + this.y = y; + } + } + // ======================================== + static public final RenderingHints renderingHints; + static { + renderingHints = new RenderingHints (RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + // renderingHints.put (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + // renderingHints.put (RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + // renderingHints.put (RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); + // renderingHints.put (RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + } + + static public final int defaultAdjust = 8; + public BufferedImage reference; + public boolean horizontalSpin, verticalSpin; + + Hashtable> scalesRotations = new Hashtable> (); + + public ScaledImage (BufferedImage image) { + reference = image; + } + public ScaledImage (BufferedImage image, boolean horizontalSpin, boolean verticalSpin) { + reference = image; + this.horizontalSpin = horizontalSpin; + this.verticalSpin = verticalSpin; + } + + public ScaledImage getNewSpin (boolean horizontalSpin, boolean verticalSpin) { + if (this.horizontalSpin == horizontalSpin && this.verticalSpin == verticalSpin) + return this; + return new ScaledImage (reference, this.horizontalSpin ^ horizontalSpin, this.verticalSpin ^ verticalSpin); + } + + public void changeReference (BufferedImage image) { + reference = image; + clearCache (); + } + + public void clearCache () { + scalesRotations.clear (); + } + + // ======================================== + public AffineTransform getAffineTransform (Dimension2D size, double theta) { + int width = reference.getWidth (); + int height = reference.getHeight (); + double scaleX = size.getWidth () / width; + double scaleY = size.getHeight () / height; + if (scaleX == 0 || scaleY == 0) + return null; + if (horizontalSpin) + scaleX = -scaleX; + if (verticalSpin) + scaleY = -scaleY; + AffineTransform at = AffineTransform.getRotateInstance (Math.toRadians ((int) Math.toDegrees (theta))); + at.scale (scaleX, scaleY); + at.translate (-width/2., -height/2.); + return at; + } + + static public final Point2D ORIGINE = new Point2D.Double (0, 0); + static public final Dimension ONE_TILE = new Dimension (1, 1); + public ImageIcon get (Dimension size, double theta) { + return get (size, theta, ONE_TILE); + } + public ImageIcon get (Dimension size, double theta, Dimension tile) { + if (tile.width <= 0 || tile.height <= 0) + return null; + ImageInfo imageInfo = getInfo (size, theta); + if (imageInfo == null) + return null; + ImageIcon imageIcon = imageInfo.tiles.get (tile); + if (imageIcon != null) + return imageIcon; + AffineTransformOp op = new AffineTransformOp (imageInfo.at, renderingHints); + BufferedImage image = op.createCompatibleDestImage (reference, null); + Graphics2D printGraphics = (Graphics2D) image.getGraphics (); + printGraphics.setRenderingHint (RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + print (printGraphics, null, imageInfo, tile); + imageIcon = new ImageIcon (image); + imageInfo.tiles.put (tile, imageIcon); + return imageIcon; + } + public void print (Graphics2D printGraphics, Point2D pos, ImageInfo imageInfo, Dimension tile) { + if (pos != null) + printGraphics.translate (pos.getX ()+imageInfo.x, pos.getY ()+imageInfo.y); + if (ONE_TILE.equals (tile)) + printGraphics.drawImage (reference, imageInfo.at, null); + else { + double width = reference.getWidth (), height = reference.getHeight (); + double scaleX = 1.0/tile.width, scaleY = 1.0/tile.height; + double stepX = width/tile.width, stepY = height/tile.height; + for (int i = 0; i < tile.width; i++) + for (int j = 0; j < tile.height; j++) { + AffineTransform at = new AffineTransform (imageInfo.at); + at.translate (i*stepX, j*stepY); + at.scale (scaleX, scaleY); + printGraphics.drawImage (reference, at, null); + } + } + if (pos != null) + printGraphics.translate (-pos.getX ()-imageInfo.x, -pos.getY ()-imageInfo.y); + } + public void print (Graphics2D printGraphics, Point2D pos, DimensionDouble size, double theta, Dimension tile) { + print (printGraphics, pos, newInfo (size, theta), tile); + } + + public double[] getBounds (Dimension size, double theta) { + ImageInfo imageInfo = getInfo (size, theta); + if (imageInfo == null) + return null; + return imageInfo.bounds; + } + public ImageInfo getInfo (Dimension size, double theta) { + int angle = (int) Math.toDegrees (theta); + Hashtable rotated = scalesRotations.get (angle); + if (rotated == null) { + rotated = new Hashtable (); + scalesRotations.put (angle, rotated); + } + ImageInfo imageInfo = rotated.get (size); + if (imageInfo != null) + return imageInfo; + imageInfo = newInfo (new DimensionDouble (size.width, size.height), theta); + if (imageInfo == null) + return null; + rotated.put (size, imageInfo); + return imageInfo; + } + public ImageInfo newInfo (DimensionDouble size, double theta) { + int width = reference.getWidth (); + int height = reference.getHeight (); + double scaleX = size.getWidth () / width; + double scaleY = size.getHeight () / height; + if (scaleX == 0 || scaleY == 0) + return null; + if (horizontalSpin) + scaleX = -scaleX; + if (verticalSpin) + scaleY = -scaleY; + AffineTransform at = getAffineTransform (size, theta); + double [] bounds = new double [] {0, 0, 0, height, width, height, width, 0}; + at.transform (bounds, 0, bounds, 0, 4); + double dx = bounds[0]; + double dy = bounds[1]; + for (int i = 1; i < 4; i++) { + dx = Math.min (dx, bounds [i*2]); + dy = Math.min (dy, bounds [i*2+1]); + } + at.preConcatenate (AffineTransform.getTranslateInstance (-dx, -dy)); + return new ImageInfo (at, bounds, dx, dy); + } + + public ImageInfo newInfo2 (DimensionDouble size, double theta) { + int width = reference.getWidth (); + int height = reference.getHeight (); + double scaleX = size.getWidth () / width; + double scaleY = size.getHeight () / height; + if (scaleX == 0 || scaleY == 0) + return null; + if (horizontalSpin) + scaleX = -scaleX; + if (verticalSpin) + scaleY = -scaleY; + AffineTransform at = AffineTransform.getRotateInstance (theta); + at.scale (scaleX, scaleY); + at.translate (-width/2., -height/2.); + double [] bounds = new double [] {0, 0, 0, height, width, height, width, 0}; + at.transform (bounds, 0, bounds, 0, 4); + double dx = bounds[0]; + double dy = bounds[1]; + for (int i = 1; i < 4; i++) { + dx = Math.min (dx, bounds [i*2]); + dy = Math.min (dy, bounds [i*2+1]); + } + at.preConcatenate (AffineTransform.getTranslateInstance (-dx, -dy)); + return new ImageInfo (at, bounds, dx, dy); + } + + public ImageIcon getSide (int max) { + int width = reference.getWidth (); + int height = reference.getHeight (); + return get (width>height ? new Dimension (max, (height*max)/width) : new Dimension ((width*max)/height, max), 0); + } + + public ImageIcon getInsideFit (int maxWidth, int maxHeight, int adjust) { + return getInside (Math.min (reference.getWidth (), (maxWidth/adjust)*adjust), + Math.min (reference.getHeight (), (maxHeight/adjust)*adjust)); + } + + public ImageIcon getInside (int maxWidth, int maxHeight) { + int width = reference.getWidth (); + int height = reference.getHeight (); + return get ((width*maxHeight)/height > maxWidth ? + new Dimension (maxWidth, (height*maxWidth)/width) : + new Dimension ((width*maxHeight)/height, maxHeight), 0); + } + + public ImageIcon get (DimensionDouble real, double scale, double theta) { + return get (new Dimension ((int)(real.getWidth ()*scale), (int)(real.getHeight ()*scale)), 0); + } + + // ======================================== +} diff --git a/src/java/misc/SpinnerSlider.java b/src/java/misc/SpinnerSlider.java new file mode 100644 index 0000000..013129d --- /dev/null +++ b/src/java/misc/SpinnerSlider.java @@ -0,0 +1,125 @@ +package misc; + +import java.awt.Dimension; +import java.awt.GridBagLayout; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.ChangeListener; + +import misc.Util; +import static misc.Util.GBC; +import static misc.Util.GBCNL; + +/** + Spinner and Slider +*/ +@SuppressWarnings ("serial") +public class SpinnerSlider extends JPanel implements ChangeListener { + + static public int MaxPrecision = 10000; + static public NumberFormat numberFormat; + static { + numberFormat = NumberFormat.getInstance (Locale.FRENCH); + //numberFormat = new DecimalFormat ("#,##0.###,#"); + numberFormat.setGroupingUsed (true); + numberFormat.setMinimumFractionDigits (0); + numberFormat.setMaximumFractionDigits (12); + } + + // ======================================== + public interface Accessor { + /** current */ + public void set (double value); + /** minValue, maxValue, current */ + public double [] get (); + }; + Accessor accessor; + + // ======================================== + public double minValue, maxValue; + public SpinnerNumberModel snm = new SpinnerNumberModel (0., 0., 0., 1.) { + public Object getPreviousValue () { + Number value = (Number) super.getPreviousValue (); + return SpinnerSlider.this.getPreviousValue (value); + } + public Object getNextValue () { + Number value = (Number) super.getNextValue (); + return SpinnerSlider.this.getNextValue (value); + } + }; + public JSpinner spinner = new JSpinner (snm); + public JLabel label = new JLabel ("/0"); + private JSlider slider = new JSlider (0, MaxPrecision, 1); + + // ======================================== + public SpinnerSlider (Accessor accessor, int textLength, float slideWidthCoef) { + super (null); + this.accessor = accessor; + + snm.addChangeListener (this); + slider.addChangeListener (this); + ((JSpinner.DefaultEditor) spinner.getEditor ()).getTextField ().setColumns (textLength); + + JSpinner.NumberEditor editor = (JSpinner.NumberEditor) spinner.getEditor (); + DecimalFormat format = editor.getFormat (); + format.setDecimalSeparatorAlwaysShown (true); + format.setMinimumFractionDigits (0); + format.setMaximumFractionDigits (12); + + setLayout (new GridBagLayout ()); + Util.addComponent (spinner, this, GBC); + Util.addComponent (label, this, GBC); + Util.addComponent (slider, this, GBCNL); + + Dimension d = slider.getPreferredSize (); + d.width = (int)(slideWidthCoef*d.width); + slider.setPreferredSize (d); + slider.setMinimumSize (d); + } + + // ======================================== + public void stateChanged (ChangeEvent e) { + try { + if (e.getSource () == slider) + accessor.set (((double)slider.getValue ()/MaxPrecision)*(maxValue - minValue)+minValue); + else if (e.getSource () == snm) + accessor.set (((Number)snm.getValue ()).doubleValue ()); + } catch (Exception e2) { + e2.printStackTrace (); + } + } + + // ======================================== + public void update () { + double [] doubleValues = accessor.get (); + minValue = doubleValues [0]; + maxValue = doubleValues [1]; + slider.removeChangeListener (this); + snm.removeChangeListener (this); + snm.setMinimum (doubleValues [0]); + snm.setMaximum (doubleValues [1]); // XXX ? + snm.setValue (doubleValues [2]); + //snm.setStepSize ((maxValue-minValue)/MaxPrecision); + slider.setValue ((int)((doubleValues [2]-minValue)/(maxValue-minValue)*MaxPrecision)); + label.setText ("/"+numberFormat.format ((float)doubleValues [1])); + snm.addChangeListener (this); + slider.addChangeListener (this); + } + + // ======================================== + public Number getPreviousValue (Number value) { + return value; + } + public Number getNextValue (Number value) { + return value; + } + // ======================================== +} diff --git a/src/java/misc/StateNotifier.java b/src/java/misc/StateNotifier.java new file mode 100644 index 0000000..665d879 --- /dev/null +++ b/src/java/misc/StateNotifier.java @@ -0,0 +1,109 @@ +// ================================================================================ +// François MERCIOL 2013 +// Name : StateNotifier.java +// Language : Java +// Author : François Merciol +// CopyLeft : Cecil B +// Creation : 2012 +// Version : 0.1 (xx/xx/xx) +// ================================================================================ + +package misc; + +import java.lang.reflect.Method; +import java.util.Hashtable; +import java.util.List; +import java.util.TreeSet; + +public class StateNotifier { + + // ======================================== + private Hashtable> observers = new Hashtable> (); + private Hashtable> observableNames= new Hashtable> (); + + // ======================================== + private void broadcast (String observable) { + Hashtable objectMethod = observableNames.get (observable); + if (objectMethod == null) + return; + try { + for (Object object : objectMethod.keySet ()) + objectMethod.get (object).invoke (object); + } catch (Exception e) { + Log.keepLastException ("ModelInfoSender::broadcast <"+observable+">", e); + } + } + + // ======================================== + private void broadcast (String observable, Object... args) { + Hashtable objectMethod = observableNames.get (observable); + if (objectMethod == null) + return; + try { + for (Object object : objectMethod.keySet ()) + objectMethod.get (object).invoke (object, new Object[]{args}); + } catch (Exception e) { + Log.keepLastException ("ModelInfoSender::broadcast <"+observable+">", e); + } + } + + // ======================================== + public void broadcastUpdate (String observable) { + broadcast ("update"+observable); + } + public void broadcastDisplay (String observable, Object... args) { + broadcast ("display"+observable, args); + } + + // ======================================== + private void addObserver (Object object, boolean arg, String... observable) { + TreeSet newNames = new TreeSet (); + for (String obs : observable) { + String name = (arg ? "display" : "update")+obs; + try { + Method method = arg ? + object.getClass ().getMethod (name, Object[].class): + object.getClass ().getMethod (name); + Hashtable objectMethod = observableNames.get (name); + if (objectMethod == null) + observableNames.put (name, objectMethod = new Hashtable ()); + objectMethod.put (object, method); + newNames.add (name); + } catch (NoSuchMethodException e) { + Log.keepLastException ("StateNotifier::addObserver no method <"+name+"> in class <"+object.getClass ()+">", e); + } + } + if (newNames.size () == 0) + return; + try { + TreeSet oldNames = observers.get (object); + oldNames.addAll (newNames); + } catch (Exception e) { + observers.put (object, newNames); + } + } + + // ======================================== + public void addUpdateObserver (Object object, String... observable) { + addObserver (object, false, observable); + } + public void addMsgObserver (Object object, String... observable) { + addObserver (object, true, observable); + } + + // ======================================== + public void removeObserver (Object object) { + TreeSet names = observers.get (object); + if (names == null) + return; + for (String name : names) { + Hashtable objectMethod = observableNames.get (name); + objectMethod.remove (object); + if (objectMethod.size () == 0) + observableNames.remove (name); + } + observers.remove (object); + } + + // ======================================== +} diff --git a/src/java/misc/Story.java b/src/java/misc/Story.java new file mode 100644 index 0000000..13e920e --- /dev/null +++ b/src/java/misc/Story.java @@ -0,0 +1,156 @@ +package misc; + +import java.util.ArrayList; +import java.util.Stack; +import java.util.Vector; + +public class Story { + + // ======================================== + static public final String + BroadcastStory = "Story"; + + public abstract class Command { + public final String name; + public Command (String name) { this.name = name; } + public abstract void exec (); + public abstract void undo (); + public void displayExec () { display (); } + public void displayUndo () { display (); } + public void display () {} + } + + public abstract class Commands extends Command { + ArrayList commands = new ArrayList (); + public Commands (String name) { super (name); } + public Story getStory () { return Story.this; } + public int size () { return commands.size (); } + public void add (Command command) { + commands.add (command); + } + public void exec () { + // XXX si erreur undo jusqu'a l'etape + for (Command command : commands) { + command.exec (); + command.displayExec (); + } + } + public void undo () { + for (int i = commands.size ()-1; i >= 0; i--) { + Command command = commands.get (i); + command.undo (); + command.displayUndo (); + } + } + public String toString () { + String result = ""; + for (Command command : commands) + result += " "+command.name; + return result; + } + } + + // ======================================== + StateNotifier observable; + Stack undo = new Stack (); + Stack redo = new Stack (); + + public Vector getUndoCmd () { + return getCmd (undo); + } + public Vector getRedoCmd () { + return getCmd (redo); + } + public Vector getCmd (Stack commands) { + Vector result = new Vector (commands.size ()); + for (int i = commands.size () - 1; i >= 0; i--) + result.add (commands.get (i).name); + return result; + } + + public Story (StateNotifier observable) { + this.observable = observable; + } + public void addUpdateObserver (Object object) { + observable.addUpdateObserver (object, BroadcastStory); + } + public void removeObserver (Object object) { + observable.removeObserver (object); + } + + public void add (Command command) { + try { + if (command instanceof Commands && ((Commands) command).commands.size () < 1) + return; + command.exec (); + command.displayExec (); + undo.push (command); + } finally { + redo.clear (); + observable.broadcastUpdate (BroadcastStory); + } + }; + + // ======================================== + Command lastSaved; + Command lastCommand () { + return undo.empty () ? null : undo.peek (); + } + + public void markNotSaved () { + lastSaved = new Command ("") { public void exec () {} public void undo () {} }; + observable.broadcastUpdate (BroadcastStory); + } + public void markSaved () { + lastSaved = lastCommand (); + observable.broadcastUpdate (BroadcastStory); + } + public boolean isModified () { + return lastSaved != lastCommand (); + } + + // ======================================== + public boolean undoAvailable () { + return !undo.empty (); + } + public boolean redoAvailable () { + return !redo.empty (); + } + public ArrayList getUndoCommande () { return getString (undo); } + public ArrayList getRedoCommande () { return getString (redo); } + ArrayList getString (Stack stack) { + ArrayList result = new ArrayList (); + for (Command command : stack) + result.add (command.name); + return result; + } + + // ======================================== + public void undo (int level) { + try { + for (int i = 0; i < level; i++) { + Command command = undo.pop (); + command.undo (); + command.displayUndo (); + redo.push (command); + } + } catch (Exception e) { + } + observable.broadcastUpdate (BroadcastStory); + } + + public void redo (int level) { + try { + for (int i = 0; i < level; i++) { + Command command = redo.pop (); + command.exec (); + command.displayExec (); + undo.push (command); + } + } catch (Exception e) { + } + observable.broadcastUpdate (BroadcastStory); + } + + // ======================================== +} diff --git a/src/java/misc/StoryManager.java b/src/java/misc/StoryManager.java new file mode 100644 index 0000000..61391d1 --- /dev/null +++ b/src/java/misc/StoryManager.java @@ -0,0 +1,142 @@ +package misc; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.Vector; +import javax.swing.AbstractButton; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; + +public class StoryManager implements ApplicationManager, ActionListener { + + // ======================================== + static public final String + actionUndo = "Undo", + actionRedo = "Redo"; + static public final List + actionsNames = Arrays.asList (actionUndo, actionRedo); + @SuppressWarnings("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (StoryManager.class, actionsNames); + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + private ArrayList enabledUndoButtons = new ArrayList (); + private ArrayList enabledRedoButtons = new ArrayList (); + + // ======================================== + public void addMenuItem (JMenu... jMenu) { + Util.addMenuItem (actionsNames, this, jMenu[0]); + } + public void addIconButtons (Container... containers) { + addListButton (Util.addIconButton (actionUndo, this, containers[0]), true); + addListButton (Util.addIconButton (actionRedo, this, containers[0]), false); + } + public void addListButton (JButton button, boolean undo) { + Dimension currentSize = button.getPreferredSize (); + button.setLayout (new BorderLayout ()); + JLabel develop = new JLabel (Util.loadActionIcon ("develop")); + Dimension addedSize = develop.getPreferredSize (); + button.add (develop, BorderLayout.EAST); + currentSize.width += addedSize.width; + button.setPreferredSize (currentSize); + + develop.addMouseListener (new MouseAdapter () { + public void mousePressed (MouseEvent e) { + button.setSelected (false); + if (!button.isEnabled () || story == null) + return; + new JStoryPopup (button, undo, SwingUtilities.convertMouseEvent (develop, e, button).getPoint ()); + } + }); + } + + @SuppressWarnings ("serial") + class JStoryPopup extends JPopupMenu { + public JStoryPopup (JButton button, boolean undo, Point pos) { + Vector cmds = undo ? story.getUndoCmd () : story.getRedoCmd (); + for (int i = 0; i < cmds.size (); i++) { + if (i >= 10) { + add (new JLabel ("...")); + break; + } + JMenuItem jMenu = new JMenuItem (Bundle.getStory (cmds.get (i))); + final int count = i+1; + jMenu.addActionListener (new ActionListener () { + public void actionPerformed (ActionEvent e) { + if (undo) + story.undo (count); + else + story.redo (count); + } + }); + add (jMenu); + } + show (button, pos.x, pos.y); + } + } + + public void addActiveButtons (Hashtable buttons) { + enabledUndoButtons.add (buttons.get (actionUndo)); + enabledRedoButtons.add (buttons.get (actionRedo)); + updateActiveButtons (); + } + public void updateActiveButtons () { + updateStory (); + } + + public void updateStory () { + boolean undo = false, redo = false; + if (story != null) { + undo = story.undoAvailable (); + redo = story.redoAvailable (); + } + for (AbstractButton button : enabledUndoButtons) + button.setEnabled (undo); + for (AbstractButton button : enabledRedoButtons) + button.setEnabled (redo); + } + + public void actionUndo () { + if (story == null) + return; + story.undo (1); + } + + public void actionRedo () { + if (story == null) + return; + story.redo (1); + } + + // ======================================== + Story story; + public void setStory (Story story) { + if (this.story != null) + this.story.removeObserver (this); + this.story = story; + if (story != null) + story.addUpdateObserver (this); + updateStory (); + } + + // public StoryManager () { + // } + + // ======================================== +} diff --git a/src/java/misc/Timer.java b/src/java/misc/Timer.java new file mode 100644 index 0000000..36654d4 --- /dev/null +++ b/src/java/misc/Timer.java @@ -0,0 +1,58 @@ +package misc; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.TimeZone; + +public class Timer { + // ======================================== + static public SimpleDateFormat sdf = new SimpleDateFormat ("H:mm:ss.S"); + static { + sdf.setTimeZone (TimeZone.getTimeZone ("GMT")); + } + class Milestone { + String name; + long end; + long last; + Milestone (String name, long end, long last) { + this.name = name; + this.end = end; + this.last = last; + } + } + // ======================================== + long start = System.currentTimeMillis (); + long lastEnd = start; + + ArrayList milestones = new ArrayList (); + + public String getLap (String milestoneName) { + long now = System.currentTimeMillis (); + long end = now-start; + long last = now - lastEnd; + lastEnd = now; + milestones.add (new Milestone (milestoneName, end, last)); + return milestoneName+": "+sdf.format (end)+" ("+end+")"+": "+sdf.format (last)+" ("+last+")"; + } + + public String getNames () { + String result = ""; + for (Milestone milestone : milestones) + result += ":"+milestone.name; + return result; + } + public String getEnds () { + String result = ""; + for (Milestone milestone : milestones) + result += ":"+sdf.format (milestone.end)+" ("+milestone.end+")"; + return result; + } + public String getLasts () { + String result = ""; + for (Milestone milestone : milestones) + result += ":"+sdf.format (milestone.last)+" ("+milestone.last+")"; + return result; + } + + // ======================================== +} diff --git a/src/java/misc/Timestamp.java b/src/java/misc/Timestamp.java new file mode 100644 index 0000000..2afd164 --- /dev/null +++ b/src/java/misc/Timestamp.java @@ -0,0 +1,34 @@ +package misc; + +public class Timestamp { + // ======================================== + private long changeTimestamp, updateTimestamp; + public long[] getValues () { return new long[]{changeTimestamp, updateTimestamp}; } + + // ======================================== + public void reset () { + changeTimestamp = updateTimestamp = System.currentTimeMillis (); + } + + // ======================================== + public void change () { + changeTimestamp = System.currentTimeMillis (); + } + + // ======================================== + public void update () { + updateTimestamp = System.currentTimeMillis (); + } + + // ======================================== + public boolean isUpdated (Timestamp... others) { + if (changeTimestamp > updateTimestamp) + return false; + for (Timestamp other : others) + if (other.updateTimestamp > updateTimestamp) + return false; + return true; + } + + // ======================================== +} diff --git a/src/java/misc/TitledDialog.java b/src/java/misc/TitledDialog.java new file mode 100644 index 0000000..277ae63 --- /dev/null +++ b/src/java/misc/TitledDialog.java @@ -0,0 +1,57 @@ +package misc; + +import java.awt.Frame; +import java.awt.Point; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.SwingConstants; + +@SuppressWarnings ("serial") public class TitledDialog extends JDialog implements SwingConstants { + // ======================================== + protected String titleId; + + // ======================================== + public TitledDialog (Frame frame, final String titleId) { + super (frame, Bundle.getTitle (titleId), false); + this.titleId = titleId; + Bundle.addLocalizedDialog (this, titleId); + addWindowListener (new WindowAdapter () { + public void windowClosing (WindowEvent e) { + Util.updateCheckBox (titleId, false); + } + }); + boolean visible = Config.getBoolean (titleId+Config.checkedPostfix, false); + setVisible (visible); + //Util.updateCheckBox (titleId, visible); + } + + // ======================================== + public void setVisible (boolean visible) { + // if (visible == isVisible ()) + // return; + super.setVisible (visible); + Util.updateCheckBox (titleId, visible); + if (visible) { + pack (); + setLocationRelativeTo (null); + Config.loadLocation (titleId, this, getLocation ()); + toFront (); + } else { + saveLocation (); + } + } + + // ======================================== + public void setJFrame (JFrame jFrame) { + setIconImage (jFrame.getIconImage ()); + } + + // ======================================== + public void saveLocation () { + Config.saveLocation (titleId, this); + } + + // ======================================== +} diff --git a/src/java/misc/ToolBarManager.java b/src/java/misc/ToolBarManager.java new file mode 100644 index 0000000..39e7dd6 --- /dev/null +++ b/src/java/misc/ToolBarManager.java @@ -0,0 +1,462 @@ +package misc; + +import java.util.Comparator; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Image; +import java.awt.Point; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.TreeSet; +import java.util.Vector; +import javax.swing.AbstractButton; +import javax.swing.BorderFactory; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; +import javax.swing.border.Border; +import javax.swing.plaf.basic.BasicButtonUI; +import javax.swing.table.AbstractTableModel; + +import misc.TitledDialog; +import static misc.Util.GBCNL; + +/** + ... +*/ + +public class ToolBarManager implements ApplicationManager, ActionListener, ContainerListener, SwingConstants { + + // ======================================== + static public BasicButtonUI buttonUI = new BasicButtonUI (); + static public Border buttonBorder = BorderFactory.createEmptyBorder (2, 2, 2, 2); + + static public final String + actionUp = "Up", + actionDown = "Down", + actionToolBarProfil = "ToolBarProfil"; + static public final List actionsToolBar = + Arrays.asList (actionToolBarProfil); + @SuppressWarnings("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (ToolBarManager.class, actionsToolBar); + public void actionPerformed (ActionEvent e) { + try { + AbstractButton button = ((AbstractButton) e.getSource ()); + Component component = toolBarComponent.get (button.getActionCommand ()); + if (component != null) { + if (button.isSelected ()) + show (component); + else + hide (component); + return; + } + } catch (Exception e2) { + Log.keepLastException ("ToolBarManager::actionPerformed", e2); + } + Util.actionPerformed (actionsMethod, e, this); + } + public void addMenuItem (JMenu... jMenu) { + Util.addMenuItem (actionsToolBar, this, jMenu[0]); + ((MultiToolBarBorderLayout) container.getLayout ()).setMenu (jMenu[0].getParent ()); + } + public void addIconButtons (Container... containers) { + Util.addIconButton (actionsToolBar, this, containers[0]); + } + public void addActiveButtons (Hashtable buttons) { + // disable if no toolbar to manage (orderedComponents.size () < 1 => container listener) + } + // ======================================== + @SuppressWarnings ("serial") public class TableObjectModel extends AbstractTableModel { + public int getColumnCount () { return 2; } + public int getRowCount () { return orderedComponents.size (); } + public Object getValueAt (int row, int column) { + if (row >= orderedComponents.size ()) + return null; + return column == 0 ? + orderedComponents.get (row).getParent () != null : + Bundle.getTitle (toolBarName.get (orderedComponents.get (row))); + } + public String getColumnName (int column) { + return column == 0 ? Bundle.getLabel ("ToolBarShowed") : Bundle.getLabel ("ToolBarNamed"); + } + public Class getColumnClass (int column) { + return column == 0 ? Boolean.class : String.class; + } + public boolean isCellEditable (int row, int column) { + return column == 0; + } + public void setValueAt (Object aValue, int row, int column) { + if (column > 0 || row >= orderedComponents.size ()) + return; + Component component = orderedComponents.get (row); + if ((Boolean) aValue) + show (component); + else + hide (component); + } + public void move (int delta) { + int idx = table.getSelectedRow (); + if (idx < 0) + return; + int mod = orderedComponents.size (); + Component old = orderedComponents.remove (idx); + idx = (idx+delta+mod) % mod; + if (idx == orderedComponents.size ()) + orderedComponents.add (old); + else + orderedComponents.add (idx, old); + table.revalidate (); + table.getSelectionModel ().setLeadSelectionIndex (idx); + container.getLayout ().layoutContainer (container); + } + }; + TableObjectModel dataObjectModel = new TableObjectModel (); + JTable table = new JTable (dataObjectModel); + public void actionToolBarProfil () { + JPanel panel = new JPanel (new BorderLayout ()); + panel.add (Util.getJScrollPane (table), BorderLayout.CENTER); + JPanel leftPropCmdPanel = new JPanel (); + panel.add (leftPropCmdPanel, BorderLayout.SOUTH); + leftPropCmdPanel.add (Util.newIconButton (actionUp, new ActionListener () { + public void actionPerformed (ActionEvent e) { dataObjectModel.move (-1); } + })); + leftPropCmdPanel.add (Util.newIconButton (actionDown, new ActionListener () { + public void actionPerformed (ActionEvent e) { dataObjectModel.move (+1); } + })); + Hashtable buttons = Util.collectButtons (null, leftPropCmdPanel); + Util.collectButtons (buttons, leftPropCmdPanel); + for (AbstractButton button : buttons.values ()) { + button.setUI (buttonUI); + button.setBorder (buttonBorder); + } + //table.getTableHeader ().setReorderingAllowed (true); + table.setShowHorizontalLines (false); + table.setShowVerticalLines (false); + table.setRowSelectionAllowed (true); + table.setColumnSelectionAllowed (false); + table.setSelectionMode (0); + Dimension preferredSize = new Dimension (table.getPreferredSize ()); + preferredSize.height += 40; + table.getParent ().getParent ().setPreferredSize (preferredSize); + for (int row = 0; row < table.getRowCount (); row++) + // check bundle before grab dialog + table.getValueAt (row, 1); + JOptionPane.showMessageDialog (frame, panel); + int i = 0; + for (Component component : orderedComponents) + Config.setInt (toolBarName.get (component)+"Order", i++); + } + + // ======================================== + /** Les points cardinaux sur lesquels l'orientation des boîtes doit être horizontal. */ + static final List horizontalCardinal = Arrays.asList (NORTH, SOUTH); + + // ======================================== + /** receptacle qui accueille les boîtes (doit avoir une mise en forme de type bordure "BorderLayout"). */ + private Container container; + /** + * Nom donné à chaque boîte. Ce nom est utilisé comme racine pour trouver des valeur dans la configuration ou l'internationnalisation. + * Exemple si le nom est "Horloge" on trouvera dans la configuration : + * "HorlogeChecked" (true indiquant que la boîte est visible) et + * "HorlogePlace" (East indiquant que la boîte s'attachera à droite) + * On trouvera dans le fichier des + * "ActionHorloge" associant le texte à la case à cocher ("Montre l'horloge" pour le texte en français) + * Enfin un évenement de type "Horloge" pourra être traité par "actionPerformed". + */ + private Hashtable toolBarName = new Hashtable(); + private Hashtable toolBarOrder = new Hashtable(); + private ArrayList orderedComponents = new ArrayList (); + /** Assoication permettant de trouver un composant d'après son nom. */ + private Hashtable toolBarComponent = new Hashtable(); + /** mémorise la position relative (points cardinaux) de la boîte lorsqu'elle est attachée. */ + private Hashtable toolBarPlace = new Hashtable(); + /** Liste des cases à cocher associé à la visibilité d'une boîte. */ + private Hashtable> nameCheckBox = new Hashtable>(); + + JFrame frame = new JFrame (); + // ======================================== + /** + * créer un gestionnaire de boîte à outil détachable gravitant autour d'un receptacle. + * Le receptacle doit avoir une mise en forme de type bordure "BorderLayout". + * Le gestionnaire se mets automatiquement à l'écoute de l'ajout et de la suppression de composant qui peuvent résulter de l'attachement ou du détachment de boîte. + */ + public ToolBarManager (Image icon, Container container) { + this.container = container; + ((MultiToolBarBorderLayout) container.getLayout ()).setLayoutOrderedComponents (orderedComponents); + frame.setIconImage (icon); + container.addContainerListener (this); + } + + // ======================================== + public JToolBar newJToolBar (String toolBarId, String defaultCardinalPoint) { + JToolBar toolBar = new JToolBar (toolBarId); + // XXX + //Bundle.addLocalizedActioner (toolBar); + add (toolBar, defaultCardinalPoint); + return toolBar; + } + + /** + * Enregistre un composant détachable. + * Il ne devrait pas y avoir plus de 4 boîtes visible en même temps (au maximum un par point cardinal). + */ + public void add (Component component, String defaultCardinalPoint) { + String name = component.getName (); //+toolBarPostfix; + //Util.getClassName (component.getClass ()); + if (toolBarName.contains (component)) + throw new IllegalArgumentException (MessageFormat.format (Bundle.getException ("AlreadyRegistredTool"), + name, defaultCardinalPoint)); + toolBarName.put (component, name); + toolBarComponent.put (name, component); + nameCheckBox.put (name, new Vector()); + toolBarOrder.put (component, Config.getInt (name+"Order", toolBarOrder.size ())); + orderedComponents.add (component); + orderedComponents.sort (componentsComparator); + String place = Config.getString (name+"Place", defaultCardinalPoint); + Integer cardinal = MultiToolBarBorderLayout.positionStringToInt.get (place); + if (cardinal == null || cardinal == CENTER) + throw new IllegalArgumentException (MessageFormat.format (Bundle.getException ("NotCardinalPoint"), place)); + put (component, place); + showIfVisible (component); + } + + // ======================================== + /** + * Ajoute un case à cocher concernée par la visibilité d'une boîte détachable. + * La case sera mise à jour suivant le changement de visibilité du composant. + * Le gestionaire de boîte se mets automatiquement à l'écoute du changement d'état de la case (que cela soit un JButton ou un JCheckBoxMenuItem). + */ + public void addCheckBox (AbstractButton checkbox) { + String name = checkbox.getActionCommand (); + Vector buttons = nameCheckBox.get (name); + if (buttons == null) + return; + buttons.add (checkbox); + checkbox.addActionListener (this); + } + + // ======================================== + /** + * les seuls évènement pris en charge provienne du changement d'état de case à cocher. + * L'action doit avoir le même nom que celui utilisé pour désigner la boîte détachable. + */ + + // ======================================== + /** Mets à jour les boîtes à cocher refletant la visibilité d'une boîte à outils. */ + public void checkBoxComponent (Component component) { + boolean showed = component.getParent () != null; + for (AbstractButton checkbox : nameCheckBox.get (toolBarName.get (component))) { + try { + checkbox.getClass ().getMethod ("setState", boolean.class).invoke (checkbox, showed); + } catch (NoSuchMethodException e) { + Log.keepLastException ("ToolBarManager::checkBoxComponent", e); + } catch (InvocationTargetException e) { + Log.keepLastException ("ToolBarManager::checkBoxComponent", e); + } catch (IllegalAccessException e) { + Log.keepLastException ("ToolBarManager::checkBoxComponent", e); + } + } + } + + // ======================================== + /** Creer une boîte de dialogue pour une boîte detache au lancement de l'application. */ + private void newUndocked (final Component component) { + final String name = toolBarName.get (component); + final TitledDialog jdialog = new TitledDialog (frame, name); + jdialog.add (component); + ((JToolBar) component).setFloatable (false); + String place = get (component); + try { + int cardinal = MultiToolBarBorderLayout.positionStringToInt.get (place); + ((JToolBar) component).setOrientation (horizontalCardinal.contains (cardinal) ? + SwingConstants.HORIZONTAL : SwingConstants.VERTICAL); + } catch (Exception e) { + } + ((JToolBar) component).addContainerListener (new ContainerListener () { + public void componentAdded (ContainerEvent e) { Util.packWindow (component); } + public void componentRemoved (ContainerEvent e) { Util.packWindow (component); } + }); + + jdialog.addWindowListener (new WindowAdapter () { + public void windowClosing (WindowEvent e) { + jdialog.remove (component); + ((JToolBar) component).setFloatable (true); + show (component); + } + }); + jdialog.setLocationRelativeTo (null); + Config.loadLocation (name, jdialog, jdialog.getLocation ()); + jdialog.setVisible (true); + jdialog.pack (); + } + + public void saveLocation () { + for (Component component : toolBarName.keySet ()) { + if (component.getParent () == null || container.isAncestorOf (component)) + continue; + Config.saveLocation (toolBarName.get (component), getUndocked (component)); + } + } + + // ======================================== + /** Montre le composant au lancement de l'application s'il est marqué comme visible dans le fichier de configuration. */ + public void showIfVisible (final Component component) { + if (Config.getBoolean (toolBarName.get (component)+Config.checkedPostfix, true)) + if (Config.getBoolean (toolBarName.get (component)+Config.undockedPostfix, false)) + newUndocked (component); + else { + + show (component); + } + } + + // ======================================== + /** Montre le composant, adapte l'orientation (horizontal ou non), mémorise l'état dans le fichier de configuration et mets à jour les cases à cocher. */ + public void show (Component component) { + String place = get (component); + try { + int cardinal = MultiToolBarBorderLayout.positionStringToInt.get (place); + ((JToolBar) component).setOrientation (horizontalCardinal.contains (cardinal) ? + SwingConstants.HORIZONTAL : SwingConstants.VERTICAL); + } catch (Exception e){ + } + Container parent = component.getParent (); + if (parent!= null) + return; + ((JToolBar) component).setFloatable (true); + container.invalidate (); + container.add (component, place); + container.validate (); + Config.setBoolean (toolBarName.get (component)+Config.checkedPostfix, true); + checkBoxComponent (component); + } + + // ======================================== + private Container getUndocked (Component component) { + Container frame = component.getParent (); + for (;;) { + if (frame instanceof Window) + return frame; + frame = frame.getParent (); + } + } + + // ======================================== + /** Cache le composant, mémorise l'état dans le fichier de configuration et mets à jour les cases à cocher. */ + public void hide (Component component) { + Container parent = component.getParent (); + if (parent == null) + return; + if (container.isAncestorOf (component)) { + container.invalidate (); + container.remove (component); + container.validate (); + } else { + try { + getUndocked (component).setVisible (false); + } catch (Exception e) { + } + parent.remove (component); + } + Config.setBoolean (toolBarName.get (component)+Config.checkedPostfix, false); + checkBoxComponent (component); + } + + // ======================================== + /** Change l'état de visibilité de la boîte détachable. */ + public void showHide (Component component) { + if (component.getParent () == null) + show (component); + else + hide (component); + } + + // ======================================== + /** Recherche le point cardinal réel d'une boîte detachable dans le receptacle (exemple : cas d'une boîte vient d'être réttachée). */ + private String find (Component component) { + return (String) ((MultiToolBarBorderLayout) container.getLayout ()).getConstraints (component); + } + + // ======================================== + /** Recherche un point cardinal pour une boîte. */ + private String get (Component component) { + String place = toolBarPlace.get (component); + if (place != null) + return place; + place = BorderLayout.NORTH; + put (component, place); + return place; + } + + // ======================================== + /** Mémorise, sans controle, le point cardinal pour une boîte (y compris dans le fichier de configuration). */ + private void put (Component component, String place) { + toolBarPlace.put (component, place); + Config.setString (toolBarName.get (component)+"Place", place); + } + + // ======================================== + /** + * Traite l'ajout d'une boîte détachable par insertion (coche d'une case ou fermeture de sa boîte de dialogue quand elle est détachée). + * Vérifie si elle attérie sur une place déjà prise. + */ + public void componentAdded (ContainerEvent e) { + Component component = e.getChild (); + if (! toolBarName.keySet ().contains (component)) + // boîte non géré par ce gestionnaire + return; + String place = find (component); + put (component, place); + Util.packWindow (container); + Config.setBoolean (toolBarName.get (component)+Config.undockedPostfix, false); + } + + // ======================================== + /** + * Traite le retrait d'une boîte détachable par suppression (décoche d'une case ou détachement vers une doite de dialogue). + */ + public void componentRemoved (ContainerEvent e) { + Util.packWindow (container); + Component component = e.getChild (); + if (! toolBarName.keySet ().contains (component)) + // boîte non géré par ce gestionnaire + return; + Config.setBoolean (toolBarName.get (component)+Config.undockedPostfix, true); + } + + // ======================================== + public Comparator componentsComparator = + new Comparator () { + public int compare (Component o1, Component o2) { + Integer i1 = toolBarOrder.get (o1); + Integer i2 = toolBarOrder.get (o2); + if (i1 == i2) + return o1.hashCode ()-o2.hashCode (); + if (i1 == null || i2 == null) + return i2 == null ? 1 : -1; + return i1-i2; + } + }; + + // ======================================== +} diff --git a/src/java/misc/Util.java b/src/java/misc/Util.java new file mode 100644 index 0000000..30953fc --- /dev/null +++ b/src/java/misc/Util.java @@ -0,0 +1,964 @@ +// ================================================================================ +// François MERCIOL 2012 +// Name : Util.java +// Language : Java +// Author : François Merciol +// CopyLeft : Cecil B +// Creation : 2012 +// Version : 0.1 (xx/xx/xx) +// ================================================================================ +package misc; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.Normalizer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Hashtable; +import java.util.List; +import java.util.Set; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.LineEvent; +import javax.sound.sampled.LineListener; +import javax.sound.sampled.LineUnavailableException; +import javax.swing.AbstractButton; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.DefaultCellEditor; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.JSplitPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.plaf.basic.BasicButtonUI; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; + +public class Util implements SwingConstants { + + // ======================================== + // Change Plugins.version too ! + static public final Long version = 20171101L; + + static public final int bufSize = 1024*1024; + static public final String NL = System.getProperty ("line.separator"); + static public final String ON = "On"; + static public final String OFF = "Off"; + + static public BasicButtonUI buttonNoUI = new BasicButtonUI (); + static public Border buttonNoBorder = BorderFactory.createEmptyBorder (2, 2, 2, 2); + + // ======================================== + static public final String toNumIn2Units (long bytes) { + if (bytes == 0) + return "0 "; + int u = 0; + for (; bytes > 1024*1024; bytes >>= 10) + u++; + + if (bytes < 1024) + return String.format ("%d %c", bytes, " kMGTPEZY".charAt (u)).replace (" ", ""); + u++; + return String.format ("%.1f %c", bytes/1024f, " kMGTPEZY".charAt (u)).replace (",0 ", " ").replace (" ", ""); + } + static public final String toNumIn10Units (long bytes) { + if (bytes == 0) + return "0 "; + int u = 0; + for (; bytes > 1000*1000; bytes >>= 10) + u++; + + if (bytes < 1000) + return String.format ("%d %c", bytes, " kMGTPEZY".charAt (u)).replace (" ", ""); + u++; + return String.format ("%.1f %c", bytes/1000f, " kMGTPEZY".charAt (u)).replace (",0 ", " ").replace (" ", ""); + } + @SuppressWarnings ({"unchecked", "rawtypes"}) + static public final T toEnum (String value, T defaultValue) { + try { + return (T) Enum.valueOf ((Class)defaultValue.getClass (), value); + } catch (Exception e) { + } + return defaultValue; + } + + // ======================================== + static public final String toColumn (String [] lines, int nbColumn) { + int size = lines.length; + if (size < 1) + return ""; + if (size < 2) + return lines [0]+"\n"; + String result = ""; + int nbLignes = size/nbColumn; + int nbLongColumn = size % nbColumn; + for (int i = 0, k = 0; k < size; i++) { + int idx = i; + String sep = ""; + for (int j = 0; j < nbColumn && k < size; j++, k++) { + result += sep + lines [idx]; + sep = "\t"; + idx += nbLignes; + if (j < nbLongColumn) + idx++; + } + result += "\n"; + } + return result; + } + + // ======================================== + static public final String[] set2string (Set set) { + String [] result = new String [set.size ()]; + int idx = 0; + for (String key : set) + result [idx++] = key; + return result; + } + + // ====================================================================== + static public JScrollPane getJScrollPane (Component view) { + return getJScrollPane (view, true, true); + } + static public JScrollPane getJScrollPane (Component view, boolean vNeed, boolean hNeed) { + return new JScrollPane (view, + vNeed ? JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED : JScrollPane.VERTICAL_SCROLLBAR_NEVER, + hNeed ? JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED : JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + } + + // ======================================== + static public JSplitPane getJSplitPane (int newOrientation, Component newLeftComponent, Component newRightComponent) { + JSplitPane result = new JSplitPane (newOrientation, newLeftComponent, newRightComponent); + result.setResizeWeight (0); + result.setOneTouchExpandable (true); + result.setContinuousLayout (true); + return result; + } + + // ======================================== + static public final void setEnabled (Component component, boolean enabled) { + component.setEnabled (enabled); + try { + for (Component component2 : ((Container) component).getComponents ()) + setEnabled (component2, enabled); + } catch (Exception e) { + } + } + + // ======================================== + static public final GridBagConstraints + GBC, GBCNL, GBCLNL, GBCBNL; + static { + GBC = new GridBagConstraints (); + GBC.insets = new Insets (1, 2, 1, 2); + GBC.weightx = GBC.weighty = 1.; + GBC.fill = GridBagConstraints.HORIZONTAL; + + GBCLNL = (GridBagConstraints) GBC.clone (); + GBCLNL.anchor = GridBagConstraints.WEST; + GBCLNL.fill = GridBagConstraints.NONE; + GBCLNL.gridwidth = GridBagConstraints.REMAINDER; + + GBCNL = (GridBagConstraints) GBC.clone (); + GBCNL.gridwidth = GridBagConstraints.REMAINDER; + + + GBCBNL = (GridBagConstraints) GBCNL.clone (); + GBCBNL.fill = GridBagConstraints.BOTH; + } + static public JPanel getGridBagPanel () { + return new JPanel (new GridBagLayout ()); + } + static public void addLabelFields (Container container, String label, Component... components) { + addLabel (label, RIGHT, container, GBC); + for (int i = 0; i < components.length; i++) + addComponent (components[i], container, i == components.length -1 ? GBCNL : GBC); + } + + // ======================================== + static public AbstractButton activeButton (AbstractButton button, String action, ActionListener actionListener) { + button.setActionCommand (action); + Bundle.addLocalizedActioner (button); + button.setToolTipText (Bundle.getAction (action)); + Bundle.addLocalizedToolTip (button); + if (actionListener != null) + button.addActionListener (actionListener); + return button; + } + + // ======================================== + static public final JCheckBox newCheckIcon (String action, ActionListener actionListener) { + JCheckBox button = new JCheckBox (loadActionIcon (action+OFF)); + button.setSelectedIcon (loadActionIcon (action+ON)); + activeButton (button, action, actionListener); + return button; + } + static public final JCheckBox newCheckIcon (String action, ActionListener actionListener, boolean defaultValue) { + JCheckBox button = newCheckIcon (action, actionListener); + button.setSelected (defaultValue); + return button; + } + static public final JCheckBox addCheckIcon (String action, ActionListener actionListener, Container container) { + JCheckBox button = newCheckIcon (action, actionListener); + container.add (button); + return button; + } + static public final JCheckBox addCheckIcon (String action, ActionListener actionListener, boolean defaultValue, Container container) { + JCheckBox button = newCheckIcon (action, actionListener); + container.add (button); + button.setSelected (defaultValue); + return button; + } + static public final void addCheckIcon (List actions, ActionListener actionListener, Container container) { + for (String action : actions) + addCheckIcon (action, actionListener, container); + } + static public final JCheckBox addCheckIcon (String action, ActionListener actionListener, boolean defaultValue, + Container container, GridBagConstraints constraint) { + JCheckBox button = newCheckIcon (action, actionListener, defaultValue); + addComponent (button, container, constraint); + return button; + } + + static public void setCheckIconTableCellEditorRenderer (String action, TableColumn tableColumn) { + tableColumn.setCellRenderer (new CheckIconTableCellEditorRenderer (action)); + tableColumn.setCellEditor (new DefaultCellEditor (newCheckIcon (action, null))); + } + + static public class CheckIconTableCellEditorRenderer implements TableCellRenderer { + JCheckBox jCheckBox; + public CheckIconTableCellEditorRenderer (String action) { + jCheckBox = Util.newCheckIcon (action, null); + } + public Component getTableCellRendererComponent (JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + jCheckBox.setBackground (isSelected ? table.getSelectionBackground () : table.getBackground ()); + jCheckBox.setForeground (isSelected ? table.getSelectionForeground () : table.getForeground ()); + if (value instanceof Boolean) + jCheckBox.setSelected (((Boolean) value).booleanValue ()); + return jCheckBox; + } + } + + // ======================================== + static public final JCheckBox newCheckButton (String action, ActionListener actionListener) { + JCheckBox button = new JCheckBox (Bundle.getAction (action), loadActionIcon (action+OFF)); + button.setSelectedIcon (loadActionIcon (action+ON)); + activeButton (button, action, actionListener); + return button; + } + static public final JCheckBox addCheckButton (String action, ActionListener actionListener, Container container) { + JCheckBox button = newCheckButton (action, actionListener); + container.add (button); + return button; + } + static public final void addCheckButton (List actions, ActionListener actionListener, Container container) { + for (String action : actions) + addCheckButton (action, actionListener, container); + } + static public final JCheckBox addCheckButton (String action, ActionListener actionListener, + Container container, GridBagConstraints constraint) { + JCheckBox button = newCheckButton (action, actionListener); + addComponent (button, container, constraint); + return button; + } + // ======================================== + static public final JCheckBox newCheckButtonConfig (String action, ActionListener actionListener, boolean defaultValue) { + boolean startValue = Config.getBoolean (action+Config.checkedPostfix, defaultValue); + JCheckBox button = newCheckButton (action, actionListener); + button.setSelected (startValue); + return button; + } + + static public final JCheckBox addCheckButtonConfig (String action, ActionListener actionListener, boolean defaultValue, + Container container) { + JCheckBox button = newCheckButtonConfig (action, actionListener, defaultValue); + container.add (button); + return button; + } + + static public final JCheckBox addCheckButtonConfig (String action, ActionListener actionListener, boolean defaultValue, + Container container, GridBagConstraints constraint) { + JCheckBox button = newCheckButtonConfig (action, actionListener, defaultValue); + // XXX le test suivant a été supprimer, il faut vérifier que cela tourne toujours + //if (container != null) + addComponent (button, container, constraint); + return button; + } + + // ======================================== + static public final JRadioButton newRadioButton (String action, ActionListener actionListener, ButtonGroup group, String selected) { + JRadioButton button = new JRadioButton (Bundle.getAction (action), loadActionIcon (action+OFF), + action.equals (selected)); + button.setSelectedIcon(loadActionIcon (action+ON)); + activeButton (button, action, actionListener); + group.add (button); + return button; + } + + static public final void addRadioButton (List actions, ActionListener actionListener, ButtonGroup group, String selected, + Container container) { + for (String action : actions) { + JRadioButton button = newRadioButton (action, actionListener, group, selected); + container.add (button); + } + } + + static public final JRadioButton addRadioButton (String action, ActionListener actionListener, ButtonGroup group, String selected, + Container container, GridBagConstraints constraint) { + JRadioButton button = newRadioButton (action, actionListener, group, selected); + addComponent (button, container, constraint); + return button; + } + + // ======================================== + static public final JButton newButton (String action, ActionListener actionListener) { + JButton button = new JButton (Bundle.getAction (action), loadActionIcon (action)); + activeButton (button, action, actionListener); + return button; + } + static public final void newButton (List actions, ActionListener actionListener) { + for (String action : actions) + newButton (action, actionListener); + } + + static public final JButton addButton (String action, ActionListener actionListener, Container container) { + JButton button = newButton (action, actionListener); + container.add (button); + return button; + } + static public final void addButton (List actions, ActionListener actionListener, Container container) { + for (String action : actions) + addButton (action, actionListener, container); + } + static public final JButton addButton (String action, ActionListener actionListener, Container container, GridBagConstraints constraint) { + JButton button = newButton (action, actionListener); + addComponent (button, container, constraint); + return button; + } + + // ======================================== + static public final JButton newIconButton (String action, ActionListener actionListener) { + JButton button = new JButton (loadActionIcon (action)); + button.setAlignmentX (Component.CENTER_ALIGNMENT); + activeButton (button, action, actionListener); + return button; + } + static public final void newIconButton (List actions, ActionListener actionListener) { + for (String action : actions) + newIconButton (action, actionListener); + } + + static public final JButton addIconButton (String action, ActionListener actionListener, Container container) { + JButton button = newIconButton (action, actionListener); + container.add (button); + return button; + } + static public final void addIconButton (List actions, ActionListener actionListener, Container container) { + for (String action : actions) + addIconButton (action, actionListener, container); + } + static public final JButton addIconButton (String action, ActionListener actionListener, Container container, GridBagConstraints constraint) { + JButton button = new JButton (loadActionIcon (action)); + activeButton (button, action, actionListener); + addComponent (button, container, constraint); + return button; + } + + static public final void unBoxButton (Container container) { + for (AbstractButton button : collectButtons (null, container).values ()) + unBoxButton (button); + } + static public final void unBoxButton (AbstractButton button) { + button.setUI (buttonNoUI); + button.setBorder (buttonNoBorder); + } + + // ======================================== + static public final void addMenuItem (String action, ActionListener actionListener, Container container) { + JMenuItem item = new JMenuItem (Bundle.getAction (action), loadActionIcon (action)); + activeButton (item, action, actionListener); + container.add (item); + } + static public final void addMenuItem (List actions, ActionListener actionListener, Container container) { + for (String action : actions) + addMenuItem (action, actionListener, container); + } + + + // ======================================== + static public Hashtable> allCheckBox = new Hashtable> (); + static public void updateCheckBox (String action, boolean value) { + ArrayList buttons = allCheckBox.get (action); + if (buttons == null) + return; + Config.setBoolean (action+Config.checkedPostfix, value); + for (AbstractButton button : buttons) + if (button.isSelected () != value) + button.setSelected (value); + } + + static public final void addCheckMenuItem (List actions, ActionListener actionListener, Container container) { + for (String action : actions) + addCheckMenuItem (action, actionListener, container); + } + static public final JCheckBoxMenuItem addCheckMenuItem (String action, ActionListener actionListener, Container container) { + return addCheckMenuItem (action, actionListener, Config.getBoolean (action+Config.checkedPostfix, false), container); + } + static public final JCheckBoxMenuItem addCheckMenuItem (String action, ActionListener actionListener, + boolean defaultValue, Container container) { + boolean startValue = Config.getBoolean (action+Config.checkedPostfix, defaultValue); + JCheckBoxMenuItem item = new JCheckBoxMenuItem (Bundle.getAction (action), loadActionIcon (action+OFF), startValue); + item.setSelectedIcon (loadActionIcon (action+ON)); + activeButton (item, action, actionListener); + container.add (item); + ArrayList checkList = allCheckBox.get (action); + if (checkList == null) + allCheckBox.put (action, checkList = new ArrayList ()); + checkList.add (item); + return item; + } + + // ======================================== + static public final JMenu addJMenu (JMenuBar jMenuBar, String menuId) { + JMenu menu = new JMenu (Bundle.getTitle (menuId)); + menu.setActionCommand (menuId); + Bundle.addLocalizedMenu (menu); + jMenuBar.add (menu); + return menu; + } + + // ======================================== + static public void addComponent (Component component, Container container, GridBagConstraints constraint) { + GridBagLayout gridbag = (GridBagLayout) container.getLayout (); + gridbag.setConstraints (component, constraint); + container.add (component); + } + + // ======================================== + static public final Dimension space = new Dimension (10, 10); + static public void addTextFieldSlider (JTextField textField, JLabel label, JSlider slider, Container container, GridBagConstraints constraint) { + JPanel panel = getGridBagPanel (); + addComponent (textField, panel, GBC); + addComponent (label, panel, GBC); + addComponent (slider, panel, GBCBNL); + addComponent (panel, container, constraint); + } + + // ======================================== + static public JLabel newLabel (String messageId, int position) { + JLabel jLabel = new JLabel (Bundle.getLabel (messageId), position); + Bundle.addLocalizedLabel (jLabel, messageId); + return jLabel; + } + + // ======================================== + static public JLabel addLabel (String messageId, int position, Container container) { + JLabel jLabel = newLabel (messageId, position); + container.add (jLabel); + return jLabel; + } + + // ======================================== + static public JLabel addLabel (String messageId, int position, Container container, GridBagConstraints constraint) { + JLabel jLabel = newLabel (messageId, position); + addComponent (jLabel, container, constraint); + return jLabel; + } + + // ======================================== + static public JComboBox newEnum (Class> enumClass, Enum defaultValue) { + JComboBox jComboBox = Bundle.getEnum (enumClass, defaultValue); + Bundle.addLocalizedEnum (jComboBox, enumClass); + return jComboBox; + } + + // ======================================== + static public JComboBox addEnum (Class> enumClass, Enum defaultValue, Container container) { + JComboBox jComboBox = newEnum (enumClass, defaultValue); + container.add (jComboBox); + return jComboBox; + } + + // ======================================== + static public JComboBox addEnum (Class> enumClass, Enum defaultValue, Container container, GridBagConstraints constraint) { + JComboBox jComboBox = newEnum (enumClass, defaultValue); + addComponent (jComboBox, container, constraint); + return jComboBox; + } + + // ======================================== + static public final void setColumnLabels (JTable jTable, String [] columnLabels) { + for (int i = 0; i < columnLabels.length; i++) + jTable.getColumnModel ().getColumn (i).setHeaderValue (Bundle.getLabel (columnLabels [i])); + try { + Container parent = jTable.getParent (); + parent.repaint (); + } catch (Exception e) { + } + } + + static public int viewToModel (JTable table, int vColIndex) { + if (vColIndex >= table.getColumnCount()) { + return -1; + } + return table.getColumnModel ().getColumn (vColIndex).getModelIndex (); + } + public int modelToView (JTable table, int mColIndex) { + for (int c = 0; c < table.getColumnCount (); c++) { + TableColumn col = table.getColumnModel ().getColumn (c); + if (col.getModelIndex () == mColIndex) { + return c; + } + } + return -1; + } + + // ======================================== + static public boolean packBug = false; + static public final void packWindow (final Component startComponent) { + if (packBug) + return; + SwingUtilities.invokeLater (new Runnable() { + public void run () { + for (Component component = startComponent; + component != null; + component = component.getParent ()) { + try { + ((Window) component).pack (); + return; + } catch (Exception e) { + } + } + } + }); + } + + // ======================================== + static public final JFrame newJFrame (String title, Component component, boolean exit) { + JFrame jFrame = new JFrame (title); + jFrame.addWindowListener (new WindowAdapter () { + public void windowClosing (WindowEvent e) { + if (exit) + System.exit (0); + jFrame.setVisible (false); + } + }); + jFrame.getContentPane ().add (component, BorderLayout.CENTER); + jFrame.pack (); + jFrame.setLocationRelativeTo (null); + jFrame.setVisible (true); + return jFrame; + } + + // ======================================== + static public ImageIcon loadActionIcon (String action) { + return loadImageIcon (Config.dataDirname, Config.iconsDirname, Config.buttonDirname, action+Config.iconsExt); + } + + // ======================================== + static public ImageIcon loadImageIcon (String... names) { + URL url = Config.getDataUrl (names); + return (url != null) ? new ImageIcon (url) : null; + } + + // ======================================== + static public Image loadImage (String... names) { + try { + return loadImageIcon (names).getImage (); + } catch (Exception e) { + return null; + } + } + + // ======================================== + static public final AudioInputStream loadAudio (String... names) { + URL url = Config.getDataUrl (names); + try { + return AudioSystem.getAudioInputStream (url); + } catch (Exception e) { + return null; + } + } + + static public final void play (AudioInputStream stream) { + if (stream == null) + return; + try { + AudioFormat format = stream.getFormat (); + DataLine.Info info = new DataLine.Info (Clip.class, format); + Clip clip = (Clip) AudioSystem.getLine (info); + clip.open (stream); + clip.addLineListener (new LineListener () { + public void update (LineEvent event) { + LineEvent.Type type = event.getType(); + if (type == LineEvent.Type.START) { + // System.err.println ("Playback started."); + } else if (type == LineEvent.Type.STOP) { + // System.er.println ("Playback completed."); + clip.close (); + } + } + }); + clip.start (); + } catch (LineUnavailableException e) { + Log.keepLastException ("Audio line for playing back is unavailable", e); + } catch (IOException e) { + Log.keepLastException ("Error playing the audio file", e); + } + + } + + // ======================================== + @SuppressWarnings({"rawtypes", "unchecked"}) + static public final Hashtable collectMethod (Class javaClass, List... actions) { + Hashtable actionsMethod = new Hashtable (); + for (List arg : actions) + for (String action : arg) { + try { + actionsMethod.put (action, javaClass.getMethod ("action"+action)); + } catch (NoSuchMethodException e) { + try { + actionsMethod.put (action, javaClass.getMethod ("action"+action, new Class[] { boolean.class })); + } catch (NoSuchMethodException e1) { + Log.keepLastException ("Util::collectMethod can't find methode "+"action"+action, e1); + e.printStackTrace (); + } + } + } + return actionsMethod; + } + + // ======================================== + static public final Hashtable collectButtons (Hashtable buttons, JMenu jMenu) { + if (buttons == null) + buttons = new Hashtable (); + for (int i = 0; i < jMenu.getItemCount (); i++) { + try { + AbstractButton button = jMenu.getItem (i); + buttons.put (button.getActionCommand (), button); + } catch (Exception e) { + } + } + return buttons; + } + + static public class ActionControl { + String action; + int key; + boolean control; + public ActionControl (String action, int key) { + this (action, key, true); + } + public ActionControl (String action, int key, boolean control) { + this.action = action; + this.key = key; + this.control = control; + } + } + static public void setAccelerator (Hashtable buttons, ActionControl... actionsControl) { + for (ActionControl actionControl : actionsControl) + ((JMenuItem) buttons.get (actionControl.action)).setAccelerator (KeyStroke.getKeyStroke (actionControl.key, actionControl.control ? InputEvent.CTRL_DOWN_MASK : 0)); + } + + + // ======================================== + static public final Hashtable collectButtons (Hashtable buttons, Container container) { + if (buttons == null) + buttons = new Hashtable (); + for (Component component : container.getComponents ()) { + try { + AbstractButton button = (AbstractButton) component; + buttons.put (button.getActionCommand (), button); + } catch (Exception e) { + } + } + return buttons; + } + + // ======================================== + @SuppressWarnings("rawtypes") + static public final void actionPerformed (final Hashtable actionsMethod, ActionEvent e, final Object object) { + String cmd = null; + try { + final Object source = e.getSource (); + if (source instanceof AbstractButton) + cmd = ((AbstractButton) source).getActionCommand (); + else if (source instanceof JComboBox) + cmd = ((JComboBox) source).getActionCommand (); + final String cmd2 = cmd; + if (cmd2 != null) + SwingUtilities.invokeLater (new Runnable() { + public void run () { + if (source instanceof JCheckBoxMenuItem) { + try { + boolean value = ((JCheckBoxMenuItem) source).isSelected (); + actionsMethod.get (cmd2).invoke (object, value); + updateCheckBox (cmd2, value); + return; + } catch (IllegalArgumentException e2) { + } catch (Exception e1) { + Log.keepLastException ("Util::actionPerformed (action "+cmd2+" on "+object+")", e1); + return; + } + } + try { + actionsMethod.get (cmd2).invoke (object); + } catch (Exception e1) { + Log.keepLastException ("Util::actionPerformed (action "+cmd2+" on "+object+")", e1); + } + } + }); + } catch (Exception e2) { + e2.printStackTrace (); + Log.keepLastException ("Util::actionPerformed (action "+cmd+" on "+object+")", e2); + } + } + + // ======================================== + @SuppressWarnings("unchecked") + static public List merge (List... args) { + List result = new ArrayList (); + for (List arg : args) + result.addAll (arg); + return result; + } + + // ======================================== + // static public File checkExt (File file, String ext) { + // try { + // String fileExt = file.getName (); + // fileExt = fileExt.substring (fileExt.lastIndexOf (".")).toLowerCase (); + // if (ext.toLowerCase ().equals (fileExt)) + // return file; + // } catch (Exception e) { + // } + // return new File (file.getParent (), file.getName ()+ext); + // } + + static public String getExtention (File file) { + return getExtention (file.getName ()); + } + static public String getExtention (String filename) { + try { + filename = filename.substring (filename.lastIndexOf (".")).toLowerCase (); + return filename.substring (1); + } catch (Exception e) { + return null; + } + } + + static public String getBase (File file) { + return getBase (file.getName ()); + } + static public String getBase (String filename) { + try { + return filename.substring (0, filename.lastIndexOf (".")); + } catch (Exception e) { + return filename; + } + } + + static public String changeExtention (String filename, String extention) { + try { + return filename.substring (0, filename.lastIndexOf ("."))+"."+extention; + } catch (Exception e) { + return filename+"."+extention; + } + } + + // ======================================== + static public void backup (File file, String extention, String backExtention) { + if (!file.exists ()) + return; + String newFileName = file.getName (); + if (newFileName.endsWith ("."+extention)) + newFileName = newFileName.substring (0, newFileName.length () - ("."+extention).length ()); + File backFile = new File (file.getParent (), newFileName+"."+backExtention); + backFile.delete (); + try { + Files.move (file.toPath (), backFile.toPath (), StandardCopyOption.REPLACE_EXISTING); + } catch (Exception e) { + } + } + + // ======================================== + static public void copy (InputStream src, OutputStream dst, byte[] tmp, ProgressState progressState) + throws IOException { + copy (src, dst, tmp, progressState, true, true); + } + static public void copy (InputStream src, OutputStream dst, byte[] tmp, ProgressState progressState, boolean srcClose, boolean dstClose) + throws IOException { + try { + if (tmp == null) + tmp = new byte[bufSize]; + for (;;) { + if (progressState != null && progressState.isInterrupted ()) + return; + int nbRead = src.read (tmp, 0, tmp.length); + if (nbRead < 0) + break; + dst.write (tmp, 0, nbRead); + if (progressState != null && !progressState.addValue (nbRead)) + return; + } + } finally { + dst.flush (); + try { + if (dstClose) + dst.close (); + if (srcClose) + src.close (); + } catch (Exception e) { + } + } + } + + // ======================================== + static public final String ctrlName (String s) { + return s.replaceAll ("[^0-9a-zA-Z_\\-.]", ""); + } + + // ======================================== + static public final String toCapital (final String s) { + return s.substring (0, 1).toUpperCase ()+s.substring (1).toLowerCase (); + } + + static public final boolean containsOne (Collection set1, Collection set2) { + try { + if (set1.size () > set2.size ()) { + Collection tmp = set1; + set1 = set2; + set2 = tmp; + } + for (T e : set1) + if (set2.contains (e)) + return true; + } catch (Exception e) { + } + return false; + } + + static public final boolean containsPart (String subString, Collection set) { + if (set == null) + return false; + for (String s : set) + if (s.indexOf (subString) >= 0) + return true; + return false; + } + + // ======================================== + @SuppressWarnings("rawtypes") + static public final String getClassName (final Class aClass) { + String[] path = aClass.getName ().split ("\\."); + return path [path.length-1]; + } + + // ======================================== + /** + retourne vrai si s1 existe et est égale à s2 + */ + static public boolean cmp (String s1, String s2) { + return (s1 == s2) || ((s1 != null) && (s1.equals (s2))); + } + + // ======================================== + static int max (int... values) { + int result = 0; + for (int val : values) + result = Math.max (result, val); + return result; + } + + // ======================================== + static public void sleep (int s) { + try { + Thread.sleep (s*1000); + } catch (InterruptedException e) { + } + } + + // ======================================== + static public void deciSleep (int s) { + try { + Thread.sleep (s*100); + } catch (InterruptedException e) { + } + } + + // ======================================== + static private String convertToHex (byte[] data) { + StringBuffer buf = new StringBuffer (); + for (int i = 0; i < data.length; i++) { + int halfbyte = (data[i] >>> 4) & 0x0F; + int two_halfs = 0; + do { + if ((0 <= halfbyte) && (halfbyte <= 9)) + buf.append ((char) ('0' + halfbyte)); + else + buf.append ((char) ('a' + (halfbyte - 10))); + halfbyte = data[i] & 0x0F; + } while (two_halfs++ < 1); + } + return buf.toString (); + } + + public static String removeAccent (String source) { + return Normalizer.normalize (source, Normalizer.Form.NFD).replaceAll ("[\u0300-\u036F]", ""); + } + + static public String sha1 (String text) + throws NoSuchAlgorithmException, UnsupportedEncodingException { + MessageDigest md; + md = MessageDigest.getInstance ("SHA-1"); + byte[] sha1hash = new byte[40]; + md.update (text.getBytes ("iso-8859-1"), 0, text.length ()); + sha1hash = md.digest (); + return convertToHex (sha1hash); + } + + // ======================================== +} diff --git a/src/java/misc/XML.java b/src/java/misc/XML.java new file mode 100644 index 0000000..7dbd36d --- /dev/null +++ b/src/java/misc/XML.java @@ -0,0 +1,114 @@ +package misc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Hashtable; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +public class XML { + + // ======================================== + /** une idée de Kohsuke Kawaguchi + http://weblogs.java.net/blog/kohsuke/archive/2005/07/socket_xml_pitf.html */ + static public class NoWaittingNoCloseInputStream extends java.io.FilterInputStream { + public NoWaittingNoCloseInputStream (InputStream in) { super (in); } + + public int read (byte[] b, int off, int len) throws IOException { + if (super.available () <= 0) + return -1; + int nb = super.read (b, off, len); + return nb; + } + public void close () throws IOException {} + } + + // ======================================== + static public Document readDocument (InputStream stream) + throws java.io.IOException { + try { + DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance ().newDocumentBuilder (); + Document document = documentBuilder.parse (new NoWaittingNoCloseInputStream (stream)); + document.normalizeDocument (); + return document; + } catch (javax.xml.parsers.ParserConfigurationException e) { + throw new IOException (e); + } catch (org.xml.sax.SAXException e) { + throw new IOException (e); + } + } + + // ======================================== + static public void writeDocument (Document document, OutputStream stream) { + try { + Source source = new DOMSource (document); + Result result = new StreamResult (stream); + Transformer xformer = TransformerFactory.newInstance ().newTransformer (); + xformer.setOutputProperty (OutputKeys.INDENT, "yes"); + xformer.setOutputProperty ("{http://xml.apache.org/xslt}indent-amount", "2"); + xformer.transform (source, result); + stream.write ("\n".getBytes ()); + stream.flush (); + } catch (Exception e) { + Log.keepLastException ("XML::writeDocument", e); + } + } + + // ======================================== + static public void writeElement (Element element, OutputStream stream) { + try { + DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance ().newDocumentBuilder (); + Document document = documentBuilder.newDocument (); + document.setXmlStandalone (true); + document.appendChild (document.importNode (element, true)); + XML.writeDocument (document, stream); + stream.flush (); + } catch (Exception e) { + Log.keepLastException ("XML::writeElement", e); + } + } + + // ======================================== + static public final void putToken (Hashtable hashtable, String token, String value) { + hashtable.put (token, (value == null) ? "" : value); + } + + // ======================================== + static public final Hashtable node2hashtable (Node child) { + Hashtable hashtable = new Hashtable (); + for (; child != null; child = child.getNextSibling ()) { + if (child.getNodeType () == Node.ELEMENT_NODE) { + Element elementTag = (Element) child; + String token = child.getNodeName (); + NodeList nodeList = ((Element) child).getChildNodes (); + if (nodeList.getLength () > 0) + hashtable.put (token, ((Text) nodeList.item (0)).getWholeText ()); + } + } + return hashtable; + } + + // ======================================== + static public final void hashtable2node (Document document, Element container, Hashtable hashtable) { + for (String token : hashtable.keySet ()) { + Element tag = document.createElement (token); + tag.appendChild (document.createTextNode (hashtable.get (token))); + container.appendChild (tag); + } + } + + // ======================================== +} diff --git a/src/java/misc/package-info.java b/src/java/misc/package-info.java new file mode 100644 index 0000000..5505354 --- /dev/null +++ b/src/java/misc/package-info.java @@ -0,0 +1,34 @@ +/** + * Ordre de lecture des classes
    + *
  • Log
  • + *
  • Config
  • + *
  • Bundle
  • + *
  • Util
  • + *
  • StateNotifier
  • + *
  • ProgressState
  • + + *
  • SpinnerSlider
  • + *
  • DatePanel
  • + *
  • HourPanel
  • + *
  • ImagePreview
  • + *
  • TitledDialog
  • + *
  • HtmlDialog
  • + + *
  • ApplicationManager
  • + *
  • OwnFrame
  • + *
  • Controller
  • + *
  • HelpManager
  • + *
  • ToolBarManager
  • + + *
  • Guide
  • + + *
  • XML
  • + + *
  • ColorCheckedLine
  • + *
  • CommandLineServer
  • + *
  • CommandLineWaiter
  • + + *
+ * @author F. Merciol + */ +package misc; diff --git a/src/java/network/Client.java b/src/java/network/Client.java new file mode 100644 index 0000000..f56a549 --- /dev/null +++ b/src/java/network/Client.java @@ -0,0 +1,147 @@ +package network; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; + +import org.w3c.dom.Element; + +import misc.Log; +import misc.XML; + +public class Client extends Protocol { + + // ======================================== + static public final boolean trace = false; + static public final boolean socketNotURL = false; + + // ======================================== + private String host; + private int port; + private String uri = "/"; + private Proxy proxy; + + // ======================================== + public Client (String host, int port) { + super (ClientProtocolType); + this.host = host; + this.port = port; + } + + public Client (String host, int port, String uri) { + this (host, port); + this.uri = uri; + } + + public Client (String host, int port, String uri, String proxyHost, int proxyPort) { + this (host, port, uri); + proxy = new Proxy (Proxy.Type.HTTP, new InetSocketAddress (proxyHost, proxyPort)); + if (trace) + System.err.println (type+": proxy: "+proxy.address ()); + } + + public Client (String host, int port, String proxyHost, int proxyPort) { + this (host, port, "/", proxyHost, proxyPort); + } + + // ======================================== + public void start () { + (new Thread () { + public void run () { + Log.writeLog ("Protocol", "Client started pulling to "+host+":"+port+"("+proxy+")"); + for (currentThread = Thread.currentThread (); currentThread == Thread.currentThread (); ) { + long startTime = System.currentTimeMillis (); + try { + + // XXX prendre la date + + Log.writeLog ("Protocol", "client connected to "+host+":"+port+"("+proxy+")"); + URLConnection call = XMLConnection.getXMLConnection (new URL ("http", host, port, uri), proxy); + exchangeXML (call, true); + } catch (IOException e) { + // include : ConnectException, MalformedURLException, UnknownHostException + long stopTime = System.currentTimeMillis (); + if (stopTime - startTime < 1000) { + Log.keepLastException ("Client::start", e); + break; + } + // XXX si date trop courte sortir de la boucle + System.err.println ("coucou:"+e); + } + } + Log.writeLog ("Protocol", "Client stopped pulling to "+host+":"+port+"("+proxy+")"); + } + }).start (); + } + + // ======================================== + public synchronized void send (String applicationName, Element applicationArgument) { + super.send (applicationName, applicationArgument); + if (trace) + System.err.println (type+":send: immediat send "+applicationName); + // puis force l'envoie immediat + (new Thread () { + public void run () { + try { + Log.writeLog ("Protocol", "client connected to "+host+":"+port+"("+proxy+")"); + URLConnection call = XMLConnection.getXMLConnection (new URL ("http", host, port, uri), proxy); + exchangeXML (call, false); + } catch (IOException e) { + //} catch (UnknownHostException e) { + Log.keepLastException ("Client::send", e); + } + } + }).start (); + } + + + + // ======================================== + private void exchangeXML (URLConnection urlConnection, boolean askWaiting) + throws IOException { + OutputStream out = urlConnection.getOutputStream (); + PendingRequest pendingRequest = getPendingRequest (); + if (askWaiting) + pendingRequest.request.getDocumentElement ().setAttribute (waitAnswerToken, "yes"); + XML.writeDocument (pendingRequest.request, out); + out.flush (); + out.close (); + InputStream in = urlConnection.getInputStream (); + Element bundle = getBundle (in); + in.close (); + broadcastApplications (bundle); + } + + // ======================================== + private PendingRequest pendingRequest = new PendingRequest (); + + // ======================================== + private synchronized PendingRequest getPendingRequest () { + if (trace) + System.err.println (type+":getPendingRequest:"); + PendingRequest old = pendingRequest; + pendingRequest = new PendingRequest (); + return old; + } + + // ======================================== + protected void addPendingApplication (String applicationName, Element applicationArgument) { + Element applicationNode = pendingRequest.request.createElement (applicationToken); + applicationNode.setAttribute (nameToken, applicationName); + if (applicationArgument != null) + applicationNode.appendChild (pendingRequest.request.importNode (applicationArgument, true)); + pendingRequest.localPaquet.appendChild (applicationNode); + pendingRequest.readyToSend = true; + if (trace) { + System.err.println (type+":addPendingApplication: "); + XML.writeDocument (pendingRequest.request, System.err); + System.err.flush (); + } + } + + // ======================================== +} diff --git a/src/java/network/HTTPInputStream.java b/src/java/network/HTTPInputStream.java new file mode 100644 index 0000000..80ce88b --- /dev/null +++ b/src/java/network/HTTPInputStream.java @@ -0,0 +1,61 @@ +package network; + +import java.io.IOException; +import java.io.InputStream; +import java.io.FilterInputStream; +import java.util.Hashtable; + +public class HTTPInputStream extends FilterInputStream { + + // ======================================== + public String command; + public Hashtable headers = new Hashtable (); + private String lastHeader; + + // ======================================== + public HTTPInputStream (InputStream in) + throws IOException { + super (in); + parseHeaders (); + } + + // ======================================== + private void parseHeaders () + throws IOException { + for (;;) { + String line = readLine (); + if (line.equals ("")) { + break; + } + int pos = line.indexOf (":"); + if (pos < 0) { + if (lastHeader == null) + command = line; + else + throw new IllegalArgumentException (line+" is not a mime line"); + } else { + lastHeader = line.substring (0, pos).trim (); + headers.put (lastHeader, line.substring (pos+1).trim ()); + } + } + } + + // ======================================== + public String readLine () + throws IOException { + StringBuffer result = new StringBuffer (); + for (;;) { + int c = super.read (); + if (c < 0) + break; + if (c == '\r') + continue; + if (c == '\n') + break; + result.append ((char) c); + } + return result.toString (); + } + + // ======================================== +} diff --git a/src/java/network/HTTPOutputStream.java b/src/java/network/HTTPOutputStream.java new file mode 100644 index 0000000..42184e6 --- /dev/null +++ b/src/java/network/HTTPOutputStream.java @@ -0,0 +1,49 @@ +package network; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Hashtable; + +public class HTTPOutputStream extends ByteArrayOutputStream { + + // ======================================== + private static SimpleDateFormat dateFormat = new SimpleDateFormat ("EEE, d MMM yyyy HH:mm:ss z"); + + // ======================================== + private OutputStream out; + private String status = "HTTP/1.0 200 OK"; + public Hashtable headers = new Hashtable (); + + // ======================================== + public HTTPOutputStream (OutputStream out) { + this.out = out; + headers.put ("Server", "Merciol"); + headers.put ("Date", dateFormat.format (new Date ())); + headers.put ("Content-Type", "text/html"); + headers.put ("Connection", "close"); + } + + public void setStatus (String status) { + this.status = status; + } + + public void setHeader (String key, String value) { + headers.put (key, value); + } + + public void close () + throws IOException { + out.write ((status+"\n").getBytes ()); + for (String key : headers.keySet ()) { + out.write ((key+": "+headers.get (key)+"\n").getBytes ()); + } + out.write (("Content-Length: "+size ()+"\n").getBytes ()); + out.write ("\n".getBytes ()); + writeTo (out); + } + + // ======================================== +} diff --git a/src/java/network/Protocol.java b/src/java/network/Protocol.java new file mode 100644 index 0000000..db50d77 --- /dev/null +++ b/src/java/network/Protocol.java @@ -0,0 +1,225 @@ +package network; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Hashtable; +import java.util.List; +import java.util.Random; +import java.util.Vector; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import misc.Log; +import misc.XML; + +/** + + + + msg + + + +*/ + +public abstract class Protocol { + + // remplace + // a placer avant la création de socket dans le cas ou elle n'a pas déjà été fait avant l'utilisation de X11 (via swing) + static public final void setIpV4 () { + java.util.Properties props = System.getProperties (); + props.setProperty ("java.net.preferIPv4Stack", ""+true); + System.setProperties (props); + } + + static { + setIpV4 (); + } + + // ======================================== + static public final boolean trace = false; + + static public final String bundleToken = "bundle"; + static public final String waitAnswerToken = "waitAnswer"; + static public final String paquetToken = "paquet"; + static public final String pearIdToken = "pearId"; + static public final String applicationToken = "application"; + static public final String nameToken = "name"; + static public final String argumentToken = "argument"; + + // ======================================== + static public final String ServerProtocolType = "ServerProtocol"; + static public final String ClientProtocolType = "ClientProtocol"; + + /** socket, serverHTTP or clientHTTP */ + protected String type; + public String getType () { return type; } + protected final String pearId = Long.toHexString ((new Random ()).nextLong ()).toUpperCase (); + + public Protocol (String type) { + this.type = type; + if (trace) + System.err.println (type+":pearId: "+pearId); + } + + // ======================================== + protected Thread currentThread; + + public abstract void start (); + + public synchronized boolean isAlive () { + return currentThread != null && currentThread.isAlive (); + } + + public void stop () { + currentThread = null; + } + + // ======================================== + public class PendingRequest { + public Document request; + public Element bundle; + public Element localPaquet; + public boolean readyToSend; + + public PendingRequest () { + try { + DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance ().newDocumentBuilder (); + request = documentBuilder.newDocument (); + request.setXmlStandalone (true); + bundle = request.createElement (bundleToken); + request.appendChild (bundle); + localPaquet = request.createElement (paquetToken); + localPaquet.setAttribute (pearIdToken, pearId); + bundle.appendChild (localPaquet); + } catch (ParserConfigurationException e) { + Log.keepLastException ("Protocol::PendingRequest", e); + } + } + } + + // ======================================== + static public Element getBundle (InputStream in) + throws IOException { + Document request = null; + request = XML.readDocument (in); + if (trace) { + System.err.println ("Protocol:getBundle: "); + XML.writeDocument (request, System.err); + System.err.flush (); + } + return request.getDocumentElement (); + } + + // ======================================== + protected void broadcastApplication (final String applicationName, final Element applicationArgument) { + Vector waiters = namedWaiters.get (applicationName); + if (waiters == null) { + if (trace) + Log.writeLog ("Protocol", "no "+applicationName+" application registered for "+type); + return; + } + for (final Waiter waiter : waiters) + (new Thread () { + public void run () { + if (trace) + System.err.println (type+" start "+applicationName); + waiter.receive (applicationArgument); + } + }).start (); + } + + // ======================================== + // c'est la réception + protected synchronized void broadcastApplications (Element bundle) { + NodeList paquets = bundle.getElementsByTagName (paquetToken); + for (int i = paquets.getLength () - 1; i >= 0; i--) { + Element paquet = (Element) paquets.item (i); + NodeList applications = paquet.getElementsByTagName (applicationToken); + for (int j = applications.getLength () - 1; j >= 0; j--) { + Element application = (Element) applications.item (j); + String applicationName = application.getAttribute (nameToken); + NodeList applicationArguments = application.getElementsByTagName (argumentToken); + if (applicationArguments.getLength () > 1) + throw new IllegalArgumentException ("too many argument for application "+applicationName); + broadcastApplication (applicationName, (Element) applicationArguments.item (0)); + } + } + } + + // ======================================== + abstract protected void addPendingApplication (String applicationName, Element applicationArgument); + + // ======================================== + // gestion des applications + // ======================================== + + private Hashtable> namedWaiters = new Hashtable> (); + + public synchronized void addWaiter (String applicationName, Waiter waiter) { + if (trace) + System.err.println (type+" addWaiter: "+applicationName+" waiter:"+waiter); + if (waiter == null) + return; + Vector waiters = namedWaiters.get (applicationName); + if (waiters == null) { + waiters = new Vector (); + namedWaiters.put (applicationName, waiters); + } + if (! waiters.contains (waiter)) + waiters.add (waiter); + } + + public synchronized void removeWaiter (String applicationName, Waiter waiter) { + if (trace) + System.err.println (type+" removeWaiter: "+applicationName+" waiter:"+waiter); + if (waiter == null) + return; + Vector waiters = namedWaiters.get (applicationName); + if (waiters == null) + return; + waiters.remove (waiter); + if (waiters.size () < 1) + namedWaiters.remove (applicationName); + } + + // ======================================== + // c'est l'envoi + public synchronized void send (String applicationName, Element applicationArgument) { + if (trace) + System.err.println (type+": send "+applicationName); + addPendingApplication (applicationName, applicationArgument); + } + + // ======================================== + // c'est l'envoi + public synchronized void send (String applicationName, String... args) { + if (trace) + System.err.print (type+": send "+applicationName); + Element applicationArgument = createArgument (); + for (int i = 0; i < args.length; i += 2) { + if (trace) + System.err.print (" "+args[i]+"=\""+args[i+1]+"\""); + applicationArgument.setAttribute (args[i], args[i+1]); + } + if (trace) + System.err.println (); + send (applicationName, applicationArgument); + } + + // ======================================== + static public Element createArgument () { + try { + Document document = DocumentBuilderFactory.newInstance ().newDocumentBuilder ().newDocument (); + return document.createElement (argumentToken); + } catch (ParserConfigurationException e) { + return null; + } + } + + // ======================================== +} diff --git a/src/java/network/ProtocolManager.java b/src/java/network/ProtocolManager.java new file mode 100644 index 0000000..765aab9 --- /dev/null +++ b/src/java/network/ProtocolManager.java @@ -0,0 +1,318 @@ +package network; + +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; +import java.util.TreeSet; +import java.util.Vector; + +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.SwingConstants; + +import misc.ApplicationManager; +import misc.Bundle; +import misc.Config; +import misc.OwnFrame; +import misc.Util; +import static misc.Util.GBC; +import static misc.Util.GBCNL; + +@SuppressWarnings ("serial") public class ProtocolManager implements ApplicationManager, ActionListener { + + // ======================================== + private OwnFrame controller; + private Protocol protocol; + + private JPanel protoPanel; + private ButtonGroup group = new ButtonGroup (); + private JComboBox nsHostCB; + private JComboBox hostCB; + private JComboBox portCB; + private JComboBox uriCB; + private JCheckBox proxyCheckBox; + private JComboBox proxyHostCB; + private JComboBox proxyPortCB; + + public Protocol getProtocol () { return protocol; } + public void setProtocol (Protocol protocol) { + if (this.protocol != null) + this.protocol.stop (); + this.protocol = protocol; + if (protocol != null) + protocol.start (); + + Thread.yield (); + updateCommands (); + updateProtocol (); + broadcastStartProtocol (); + } + + // ======================================== + public ProtocolManager (OwnFrame controller) { + this.controller = controller; + createGUI (); + start (); + } + + // ======================================== + protected Thread currentThread; + + public void stop () { + currentThread = null; + } + + public void start () { + (new Thread () { + public void run () { + for (currentThread = Thread.currentThread (); currentThread == Thread.currentThread (); ) { + updateCommands (); + Util.sleep (5); + } + } + }).start (); + } + + // ======================================== + public void createGUI () { + final TreeSet hosts = new TreeSet (); + hosts.add ("localhost"); + nsHostCB = new JComboBox (new Vector (hosts)); + nsHostCB.setSelectedItem ("localhost"); + + + (new Thread () { + public void run () { + try { + for (Enumeration e = NetworkInterface.getNetworkInterfaces (); e.hasMoreElements (); ) { + NetworkInterface ni = e.nextElement (); + for (Enumeration e2 = ni.getInetAddresses (); e2.hasMoreElements (); ) { + InetAddress ia = e2.nextElement (); + for (String host : Arrays.asList (ia.getHostAddress (), ia.getHostName ())) + if (!hosts.contains (host)) { + nsHostCB.addItem (host); + hosts.add (host); + } + } + } + } catch (Exception e3) { + } + } + }).start (); + + hostCB = new JComboBox (); + portCB = new JComboBox (); + uriCB = new JComboBox (); + proxyHostCB = new JComboBox (); + proxyPortCB = new JComboBox (); + + Config.loadJComboBox ("hostProtocol", hostCB, "localhost"); + Config.loadJComboBoxInteger ("portProtocol", portCB, "8080"); + Config.loadJComboBox ("uriProtocol", uriCB, "/"); + Config.loadJComboBox ("hostProxy", proxyHostCB, "localhost"); + Config.loadJComboBoxInteger ("portProxy", proxyPortCB, "3128"); + + hostCB.setEditable (true); + portCB.setEditable (true); + uriCB.setEditable (true); + proxyHostCB.setEditable (true); + proxyPortCB.setEditable (true); + + String protocolType = Config.getString (protocolTypeToken, setServerToken); + protoPanel = Util.getGridBagPanel (); + + Util.addLabelFields (protoPanel, "AvailableNetworkCard", nsHostCB); + Util.addLabel ("ConnectionType", SwingConstants.CENTER, protoPanel, GBCNL); + + Util.addRadioButton (setServerToken, this, group, protocolType, protoPanel, GBC); + Util.addComponent (hostCB, protoPanel, GBC); + Util.addComponent (new JLabel (":"), protoPanel, GBC); + Util.addComponent (portCB, protoPanel, GBCNL); + + Util.addRadioButton (setClientToken, this, group, protocolType, protoPanel, GBC); + Util.addComponent (uriCB, protoPanel, GBCNL); + + proxyCheckBox = Util.addCheckButtonConfig (setProxyToken, this, false, protoPanel, GBC); + Util.addComponent (proxyHostCB, protoPanel, GBC); + Util.addComponent (new JLabel (":"), protoPanel, GBC); + Util.addComponent (proxyPortCB, protoPanel, GBCNL); + + + Util.addComponent (new JLabel (), protoPanel, GBCNL); + + Util.addRadioButton (unsetProtocolToken, this, group, protocolType, protoPanel, GBC); + Util.addComponent (new JLabel (), protoPanel, GBCNL); + + if (unsetProtocolToken.equals (protocolType)) + actionUnsetProtocol (); + else if (setClientToken.equals (protocolType)) + actionSetClient (); + else + actionSetServer (); + } + + // ======================================== + static public final List actionsNames = Arrays.asList ("LinkType"); + static public final String protocolTypeToken = "ProtocolType"; + static public final String setServerToken = "SetServer"; + static public final String setClientToken = "SetClient"; + static public final String unsetProtocolToken = "UnsetProtocol"; + static public final List typeActionsNames = + Arrays.asList (setClientToken, setServerToken, unsetProtocolToken); + static public final String setProxyToken = "SetProxy"; + @SuppressWarnings("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (ProtocolManager.class, + Util.merge (actionsNames, typeActionsNames, + Arrays.asList (setProxyToken))); + + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + + // ======================================== + public synchronized void setIPEnable (boolean enable) { + hostCB.setEnabled (enable); + portCB.setEnabled (enable); + uriCB.setEnabled (enable); + proxyCheckBox.setEnabled (enable); + } + public synchronized void setProxyEnable (boolean enable) { + proxyHostCB.setEnabled (enable); + proxyPortCB.setEnabled (enable); + } + + // ======================================== + public synchronized void actionSetServer () { + setIPEnable (false); + portCB.setEnabled (true); + setProxyEnable (false); + } + + public synchronized void actionSetClient () { + setIPEnable (true); + actionSetProxy (); + } + + public synchronized void actionSetProxy () { + boolean selected = proxyCheckBox.isSelected (); + Config.setBoolean (setProxyToken+Config.checkedPostfix, selected); + setProxyEnable (selected); + } + + public synchronized void actionUnsetProtocol () { + setIPEnable (false); + setProxyEnable (false); + } + + // ======================================== + public synchronized void actionLinkType () { + if (JOptionPane.OK_OPTION != + JOptionPane.showConfirmDialog (controller.getJFrame (), protoPanel, + Bundle.getTitle ("LinkType"), + JOptionPane.OK_CANCEL_OPTION)) + return; + String protocolType = group.getSelection ().getActionCommand (); + Config.setString (protocolTypeToken, protocolType); + Config.saveJComboBox ("hostProtocol", hostCB); + Config.saveJComboBoxInteger ("portProtocol", portCB); + Config.saveJComboBox ("uriProtocol", uriCB); + Config.saveJComboBox ("hostProxy", proxyHostCB); + Config.saveJComboBoxInteger ("portProxy", proxyPortCB); + + Protocol protocol = null; + if (unsetProtocolToken.equals (protocolType)) + ; + } else if (setClientToken.equals (protocolType)) { + if (proxyCheckBox.isSelected ()) + protocol = new Client (hostCB.getItemAt (hostCB.getSelectedIndex ()), + portCB.getItemAt (portCB.getSelectedIndex ()), + uriCB.getItemAt (uriCB.getSelectedIndex ()), + proxyHostCB.getItemAt (proxyHostCB.getSelectedIndex ()), + proxyPortCB.getItemAt (proxyPortCB.getSelectedIndex ())); + else + protocol = new Client (hostCB.getItemAt (hostCB.getSelectedIndex ()), + portCB.getItemAt (portCB.getSelectedIndex ()), + uriCB.getItemAt (uriCB.getSelectedIndex ())); + } else if (setServerToken.equals (protocolType)) + protocol = new Server (portCB.getItemAt (portCB.getSelectedIndex ())); + setProtocol (protocol); + } + + // ======================================== + private Vector noNetworkCommands = new Vector (); + public synchronized void addNoNetworkCommand (AbstractButton noNetworkCommand) { + if (noNetworkCommand == null) + return; + boolean isAlive = protocol != null && protocol.isAlive (); + noNetworkCommands.add (noNetworkCommand); + noNetworkCommand.setEnabled (!isAlive); + } + public synchronized void updateCommands () { + boolean isAlive = protocol != null && protocol.isAlive (); + for (AbstractButton noNetworkCommand : noNetworkCommands) + noNetworkCommand.setEnabled (!isAlive); + } + + // ======================================== + private Vector waiters = new Vector (); + public synchronized void addWaiter (Waiter waiter) { + waiters.add (waiter); + } + + public synchronized void updateProtocol () { + for (Waiter waiter : waiters) + waiter.setProtocol (protocol); + } + + // ======================================== + Vector protocolObservers = new Vector (); + public void addProtocolObserver (ProtocolObserver protocolObserver) { + protocolObservers.add (protocolObserver); + } + public void removeProtocolObserver (ProtocolObserver protocolObserver) { + protocolObservers.remove (protocolObserver); + } + + // ======================================== + public void broadcastStartProtocol () { + if (protocol != null) + for (ProtocolObserver protocolObserver : protocolObservers) + protocolObserver.startProtocol (protocol); + } + + // ======================================== + public void addMenuItem (JMenu... jMenu) { + Util.addMenuItem (actionsNames, this, jMenu[0]); + } + + // ======================================== + public void addIconButtons (Container... container) { + Util.addIconButton (actionsNames, this, container[0]); + } + + // ======================================== + public void addActiveButtons (Hashtable buttons) { + // XXX ??? fournir la sous-liste + for (String action : Arrays.asList (GameManager.actionPlayersClass, GameManager.actionCut, + GameManager.actionEmpty, GameManager.actionOpen)) + addNoNetworkCommand (buttons.get (action)); + } + + // ======================================== +} diff --git a/src/java/network/ProtocolObserver.java b/src/java/network/ProtocolObserver.java new file mode 100644 index 0000000..1d3f5ec --- /dev/null +++ b/src/java/network/ProtocolObserver.java @@ -0,0 +1,6 @@ +package network; + +public interface ProtocolObserver { + + public void startProtocol (Protocol protocol); +} diff --git a/src/java/network/Server.java b/src/java/network/Server.java new file mode 100644 index 0000000..1a1bb7e --- /dev/null +++ b/src/java/network/Server.java @@ -0,0 +1,278 @@ +package network; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.util.Hashtable; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import misc.Log; +import misc.XML; + +public class Server extends Protocol { + + // ======================================== + static public final boolean trace = false; + + static public final String loginApplicationName = "login"; + static public final String askToken = "ask"; + static public final String connectToken = "connect"; + static public final String disconnectToken = "disconnect"; + static public final String userToken = "user"; + static public final String acceptToken = "accept"; + static public final String unknownToken = "unknown"; + + // ======================================== + private int port; + + public Server (int port) { + super (ServerProtocolType); + this.port = port; + } + + // ======================================== + public void start () { + (new Thread () { + public void run () { + try { + final ServerSocket serverSocket = new ServerSocket (port); + Log.writeLog ("Protocol", "Server started on port "+port); + // pour pouvoir arreter proprement l'activité + for (currentThread = Thread.currentThread (); currentThread == Thread.currentThread (); ) { + try { + final Socket call = serverSocket.accept (); + (new Thread () { + public void run () { + try { + Log.writeLog ("Protocol", "Server accept "+call); + exchangeXML (new HTTPInputStream (call.getInputStream ()), + new HTTPOutputStream (call.getOutputStream ())); + call.close (); + } catch (SocketException e) { + Log.writeLog ("Protocol", "Server connection lost "+call); + try { + serverSocket.close (); + Log.writeLog ("Protocol", "Server close"); + } catch (IOException e2) { + Log.writeLog ("Protocol", "Server: "+e2); + } + } catch (IOException e) { + Log.keepLastException ("Server::start", e); + } + } + }).start (); + } catch (SocketTimeoutException e) { + } + } + serverSocket.close (); + } catch (SocketException e) { + Log.writeLog ("Protocol", "Server connection lost: "+e); + } catch (IOException e) { + //} catch (SocketException e) { + Log.keepLastException ("Server::start", e); + } + Log.writeLog ("Protocol", "Server stopped on port "+port); + } + }).start (); + } + + // ======================================== + public void stop () { + currentThread = null; + try { + // force le accept pour sortir du run + (new Socket ("localhost", port)).close (); + } catch (Exception e) { + } + pendingRequests = new Hashtable (); + } + + // ======================================== + private String getAskAndDelLogin (Element paquet) { + if (trace) + System.err.println (type+":getConnectAndDelLogin:"); + + String result = null; + NodeList applications = paquet.getElementsByTagName (applicationToken); + for (int j = applications.getLength () - 1; j >= 0; j--) { + Element application = (Element) applications.item (j); + if (loginApplicationName.equals (application.getAttribute (nameToken))) { + NodeList applicationArguments = application.getElementsByTagName (argumentToken); + if (applicationArguments.getLength () < 1) + continue; + Element applicationArgument = (Element) applicationArguments.item (0); + String ask = applicationArgument.getAttribute (askToken); + if (connectToken.equals (ask) || disconnectToken.equals (ask)) + result = ask; + paquet.removeChild (application); + } + } + return result; + } + + // ======================================== + private Element createUserAnswer (String state) { + Element argument = createArgument (); + argument.setAttribute (userToken, state); + return argument; + } + + // ======================================== + /** Server way : receive then send. */ + private synchronized void exchangeXML (InputStream in, OutputStream out) + throws IOException { + Element bundle = getBundle (in); + String waitRequest = bundle.getAttribute (waitAnswerToken); + NodeList paquets = bundle.getElementsByTagName (paquetToken); + if (paquets.getLength () != 1) + // Si c'est un client, il n'y a qu'un paquet + Log.writeLog ("Protocol", "exchangeXML: Only one paquet expected ("+paquets.getLength ()+")!"); + // XXX test si nombre incohérant (normalement un seul paquet par bundle) + Element paquet = (Element) paquets.item (0); + String pearId = paquet.getAttribute (pearIdToken); + addClient (pearId); + + String ask = getAskAndDelLogin (paquet); + if (ask != null) { + try { + DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance ().newDocumentBuilder (); + Document emptyRequest = documentBuilder.newDocument (); + emptyRequest.setXmlStandalone (true); + + Element applicationNode = emptyRequest.createElement (applicationToken); + applicationNode.setAttribute (nameToken, loginApplicationName); + applicationNode.appendChild (emptyRequest.importNode (createUserAnswer (connectToken.equals (ask) ? acceptToken : unknownToken), true)); + + addPendingApplicationToClient (pearId, applicationNode); + } catch (ParserConfigurationException e) { + Log.keepLastException ("Server::exchangeXML", e); + } + } + + broadcastClients (paquet, pearId); + broadcastApplications (bundle); + PendingRequest pendingRequest = getPendingRequest (pearId, waitRequest != null && "yes".equals (waitRequest)); + XML.writeDocument (pendingRequest.request, out); + out.flush (); + out.close (); + } + + // ======================================== + private Hashtable pendingRequests = new Hashtable (); + + // ======================================== + private synchronized void addClient (String pearId) { + if (trace) + System.err.println (type+":addClient: "+pearId); + // XXX si pearId est null ??? + if (pearId == null || pendingRequests.get (pearId) != null) + return; + resetClient (pearId); + // YYY nouveau client => startProtocol + } + + // ======================================== + private synchronized void resetClient (String pearId) { + if (trace) + System.err.println (type+":resetClient: "+pearId); + pendingRequests.put (pearId, new PendingRequest ()); + } + + // ======================================== + private synchronized void sendClient (String toClientId, Element paquet) { + PendingRequest pendingRequest = pendingRequests.get (toClientId); + pendingRequest.bundle.appendChild (pendingRequest.request.importNode (paquet, true)); + pendingRequest.readyToSend = true; + notifyAll (); + if (trace) { + System.err.println (type+":broadcastClients: "+pearId); + XML.writeDocument (pendingRequest.request, System.err); + System.err.flush (); + } + } + + // ======================================== + private synchronized void broadcastClients (Element paquet, String fromClientId) { + NodeList applications = paquet.getElementsByTagName (applicationToken); + if (applications.getLength () < 1) + return; + for (String toClientId : pendingRequests.keySet ()) { + if (toClientId.equals (fromClientId)) + continue; + sendClient (toClientId, paquet); + } + } + + // ======================================== + private synchronized PendingRequest getPendingRequest (String pearId, boolean waitRequest) { + if (waitRequest) { + if (trace) + System.err.println (type+":getPendingRequest: waitting for "+pearId); + for (;;) { + PendingRequest pendingRequest = pendingRequests.get (pearId); + if (pendingRequest.readyToSend) + break; + try { + wait (); + } catch (InterruptedException e) { + } + } + if (trace) + System.err.println (type+": getPendingRequest "+pearId+" is ready"); + } + PendingRequest old = pendingRequests.get (pearId); + resetClient (pearId); + return old; + } + + // ======================================== + protected void addPendingApplicationToClient (String pearId, Element applicationNode) { + PendingRequest pendingRequest = pendingRequests.get (pearId); + pendingRequest.localPaquet.appendChild (pendingRequest.request.importNode (applicationNode, true)); + pendingRequest.readyToSend = true; + notifyAll (); + if (trace) { + System.err.println (type+":addPendingApplication: "+pearId); + XML.writeDocument (pendingRequest.request, System.err); + System.err.flush (); + } + } + + // ======================================== + protected void addPendingApplication (String applicationName, Element applicationArgument) { + try { + if (loginApplicationName.equals (applicationName)) { + String ask = applicationArgument.getAttribute (askToken); + if (connectToken.equals (ask)) + broadcastApplication (loginApplicationName, createUserAnswer (acceptToken)); + else if (disconnectToken.equals (ask)) + broadcastApplication (loginApplicationName, createUserAnswer (unknownToken)); + return; + } + + DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance ().newDocumentBuilder (); + Document emptyRequest = documentBuilder.newDocument (); + emptyRequest.setXmlStandalone (true); + + Element applicationNode = emptyRequest.createElement (applicationToken); + applicationNode.setAttribute (nameToken, applicationName); + if (applicationArgument != null) + applicationNode.appendChild (emptyRequest.importNode (applicationArgument, true)); + for (String pearId : pendingRequests.keySet ()) + addPendingApplicationToClient (pearId, applicationNode); + } catch (ParserConfigurationException e) { + Log.keepLastException ("Server::addPendingApplication", e); + } + } + + // ======================================== +} diff --git a/src/java/network/Waiter.java b/src/java/network/Waiter.java new file mode 100644 index 0000000..a5a573b --- /dev/null +++ b/src/java/network/Waiter.java @@ -0,0 +1,14 @@ +package network; + +import org.w3c.dom.Element; + +public interface Waiter { + + // ======================================== + public void setProtocol (Protocol protocol); + + // ======================================== + public void receive (Element argument); + + // ======================================== +} diff --git a/src/java/network/XMLConnection.java b/src/java/network/XMLConnection.java new file mode 100644 index 0000000..a91ceb1 --- /dev/null +++ b/src/java/network/XMLConnection.java @@ -0,0 +1,39 @@ +package network; + +import java.io.IOException; +import java.net.URLConnection; +import java.net.URL; +import java.net.Proxy; + +public class XMLConnection { + + // ======================================== + static public URLConnection getXMLConnection (URL url) + throws IOException { + URLConnection urlConnection = url.openConnection (); + urlConnection.setDoOutput (true); + urlConnection.setUseCaches (false); + urlConnection.setRequestProperty ("Accept", "application/xml"); + urlConnection.setRequestProperty ("User-Agent", "Merciol"); + urlConnection.setRequestProperty ("Content-type", "application/xml"); + return urlConnection; + } + + // ======================================== + static public URLConnection getXMLConnection (URL url, Proxy proxy) + throws IOException{ + URLConnection urlConnection = (proxy == null) ? url.openConnection () : url.openConnection (proxy); + urlConnection.setDoOutput (true); + urlConnection.setUseCaches (false); + urlConnection.setRequestProperty ("Accept", "application/xml"); + urlConnection.setRequestProperty ("User-Agent", "Merciol"); + urlConnection.setRequestProperty ("Content-type", "application/xml"); + return urlConnection; + } + + public void connect () { + throw new IllegalArgumentException ("Can't reconnect an XMLConnection"); + } + + // ======================================== +} diff --git a/src/java/network/chat/Chat.java b/src/java/network/chat/Chat.java new file mode 100644 index 0000000..f896967 --- /dev/null +++ b/src/java/network/chat/Chat.java @@ -0,0 +1,192 @@ +package network.chat; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.Random; +import java.util.Vector; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +import misc.Bundle; +import misc.Config; +import misc.Log; +import misc.XML; +import network.Protocol; +import network.Waiter; + +/** + * Modèle. + + * + * + * + * + * azertyuiop + * + * + * + */ + +public class Chat implements Waiter { + + // ======================================== + static public final String applicationName = "chat"; + static public final String quoteToken = "quote"; + static public final String sequenceToken = "sequence"; + static public final String dateToken = "date"; + static public final String speakerToken = "speaker"; + static public final SimpleDateFormat dateFormat = new SimpleDateFormat ("HH:mm:ss"); + + private Protocol protocol; + private String pseudo; + + private int lastSequence; + private Vector dialog = new Vector (); + private boolean modified = false; + + public synchronized void setProtocol (Protocol protocol) { + if (this.protocol != null) + this.protocol.removeWaiter (applicationName, this); + this.protocol = protocol; + if (protocol != null) + protocol.addWaiter (applicationName, this); + } + + static public String getRandomPseudo () { + return + Bundle.getString ("anonymous", null)+"_"+Integer.toHexString ((new Random ()).nextInt ()).toUpperCase ().substring (0, 4); + } + public String getPseudo () { return pseudo; } + public void setPseudo (String pseudo) { + this.pseudo = pseudo; + broadcastRenameSpeaker (); + Config.setString ("chatLogin", pseudo); + } + + public boolean getModified () { return modified; } + + // ======================================== + public Chat (String pseudo) { + this.pseudo = pseudo; + dateFormat.setLenient (false); + } + + public Chat (String pseudo, Protocol protocol) { + this (pseudo); + setProtocol (protocol); + } + + // ======================================== + public void clear () { + dialog = new Vector (); + modified = false; + broadcastClearChat (); + } + + // ======================================== + public void save (File file) + throws IOException { + try { + file.setExecutable (false); + + Collections.sort (dialog, new ChatQuote (0, new Date (), "", "")); + + DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance ().newDocumentBuilder (); + Document document = documentBuilder.newDocument (); + document.setXmlStandalone (true); + + Element applicationNode = document.createElement (applicationName); + document.appendChild (applicationNode); + for (ChatQuote quote : dialog) { + Element quoteElement = document.createElement (quoteToken); + applicationNode.appendChild (quoteElement); + quoteElement.setAttribute (sequenceToken, ""+quote.sequence); + quoteElement.setAttribute (dateToken, dateFormat.format (quote.date)); + quoteElement.setAttribute (speakerToken, quote.speaker); + quoteElement.appendChild (document.createTextNode (quote.sentence)); + } + FileOutputStream out = new FileOutputStream (file); + XML.writeDocument (document, out); + out.close (); + modified = false; + broadcastChatModifiedChange (); + } catch (ParserConfigurationException e) { + Log.keepLastException ("Chat::save", e); + } + } + + // ======================================== + public synchronized void say (String sentence) { + if (sentence == null) + sentence = ""; + ChatQuote quote = new ChatQuote (++lastSequence, new Date (), pseudo, sentence); + broadcastTalk (quote); + if (protocol == null) + return; + Element argument = Protocol.createArgument (); + argument.setAttribute (sequenceToken, ""+quote.sequence); + argument.setAttribute (dateToken, dateFormat.format (quote.date)); + argument.setAttribute (speakerToken, quote.speaker); + argument.appendChild (argument.getOwnerDocument ().createTextNode (quote.sentence)); + protocol.send (applicationName, argument); + } + + // ======================================== + public synchronized void receive (Element argument) { + String speaker = argument.getAttribute (speakerToken); + Date date = new Date (); + try { + dateFormat.parse (argument.getAttribute (dateToken)); + } catch (ParseException e) { + Log.keepLastException ("Chat::receive", e); + } + int sequence = Integer.parseInt (argument.getAttribute (sequenceToken)); + lastSequence = Math.max (lastSequence, sequence); + String sentence = ""; + NodeList nodeList = argument.getChildNodes (); + if (nodeList.getLength () > 0) + sentence = ((Text) nodeList.item (0)).getWholeText (); + broadcastTalk (new ChatQuote (sequence, date, speaker, sentence)); + } + + // ======================================== + Vector chatObservers = new Vector (); + public void addChatObserver (ChatObserver chatObserver) { chatObservers.add (chatObserver); } + public void removeChatObserver (ChatObserver chatObserver) { chatObservers.remove (chatObserver); } + + public void broadcastTalk (ChatQuote quote) { + dialog.add (quote); + modified = true; + for (ChatObserver chatObserver : chatObservers) + chatObserver.talk (quote); + broadcastChatModifiedChange (); + } + + public void broadcastRenameSpeaker () { + for (ChatObserver chatObserver : chatObservers) + chatObserver.renameSpeaker (pseudo); + } + + public void broadcastChatModifiedChange () { + for (ChatObserver chatObserver : chatObservers) + chatObserver.chatModifiedChange (modified); + } + + public void broadcastClearChat () { + for (ChatObserver chatObserver : chatObservers) + chatObserver.clearChat (); + broadcastChatModifiedChange (); + } + + // ======================================== +} diff --git a/src/java/network/chat/ChatController.java b/src/java/network/chat/ChatController.java new file mode 100644 index 0000000..56b8ff0 --- /dev/null +++ b/src/java/network/chat/ChatController.java @@ -0,0 +1,107 @@ +package network.chat; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Image; +import java.text.MessageFormat; +import javax.swing.JMenuBar; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import network.login.Login; +import network.login.LoginManager; +import network.ProtocolManager; +import misc.Controller; +import misc.Bundle; +import misc.Config; +import misc.HelpManager; +import misc.MultiToolBarBorderLayout; +import misc.Util; + +/** + Créer et relie le modèle avec le graphisme. +*/ +public class ChatController extends Controller implements ChatObserver { + + // ======================================== + public ChatController () { + super (new Chat (Config.getString ("chatLogin", Bundle.getString ("anonymous", null)))); + } + + // ======================================== + Chat chat; + Login login; + + ProtocolManager protocolManager; + ChatManager chatManager; + LoginManager loginManager; + HelpManager helpManager; + + JChat jChat; + JChatMenuBar jChatMenuBar; + + // ======================================== + public String getTitle () { return MessageFormat.format (Bundle.getTitle ("Chat"), chat.getPseudo ()); } + public Image getIcon () { return Util.loadImage (Config.getString ("ChatIcon", "data/images/chat/chat.png")); } + + // ======================================== + protected boolean tryClosingWindows () { + Config.save ("Chat"); + if (chat.getModified ()) { + switch (JOptionPane.showConfirmDialog (jFrame, Bundle.getMessage ("SaveChat"), + Bundle.getTitle ("ChatNotSaved"), + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE)) { + case JOptionPane.YES_OPTION: + chatManager.actionSaveAs (); + return true; + case JOptionPane.NO_OPTION: + return true; + case JOptionPane.CANCEL_OPTION: + case JOptionPane.CLOSED_OPTION: + return false; + } + } + return true; + } + + // ======================================== + protected void createModel (Chat chat) { + this.chat = chat; + login = new Login (Config.getString ("pseudo", Login.getRandomLogin ())); + chat.addChatObserver (this); + } + + // ======================================== + protected Component createGUI () { + JPanel contentPane = new JPanel (new MultiToolBarBorderLayout ()); + + protocolManager = new ProtocolManager (this); + protocolManager.addWaiter (login); + protocolManager.addWaiter (chat); + protocolManager.start (); + + chatManager = new ChatManager (this, chat); + loginManager = new LoginManager (this, login); + + helpManager = new HelpManager (this, "Chat"); + + jChat = new JChat (protocolManager, loginManager, chatManager); + + contentPane.add (jChat, BorderLayout.CENTER); + return contentPane; + } + + // ======================================== + protected JMenuBar createMenuBar () { + jChatMenuBar = new JChatMenuBar (this, protocolManager, loginManager, chatManager, helpManager, null); + return jChatMenuBar; + } + + // ======================================== + public void talk (ChatQuote quote) {} + public void renameSpeaker (String speaker) { jFrame.setTitle (getTitle ()); } + public void chatModifiedChange (boolean modified) {} + public void clearChat () {} + + // ======================================== +} diff --git a/src/java/network/chat/ChatManager.java b/src/java/network/chat/ChatManager.java new file mode 100644 index 0000000..5408125 --- /dev/null +++ b/src/java/network/chat/ChatManager.java @@ -0,0 +1,142 @@ +package network.chat; + +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.Vector; +import javax.swing.AbstractButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JMenu; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileNameExtensionFilter; + +import misc.ApplicationManager; +import misc.Bundle; +import misc.Config; +import misc.OwnFrame; +import misc.Util; + +/** + comportement déclanché par des actionneurs graphiques (menu ou bouton). +*/ +@SuppressWarnings ("serial") public class ChatManager implements ApplicationManager, ActionListener, ChatObserver { + + // ======================================== + private OwnFrame controller; + + static public final String actionPseudo = "Pseudo"; + static public final String extention = "chat"; + static public final String actionClear = "Clear"; + static public final List loginActionsNames = Arrays.asList (actionPseudo); + static public final List saveActionsNames = Arrays.asList (actionClear, "SaveAs"); + @SuppressWarnings("unchecked") + static public final List actionsNames = + Util.merge (loginActionsNames, saveActionsNames, Arrays.asList ("ManageExtention")); + @SuppressWarnings("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (ChatManager.class, actionsNames); + + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + + // ======================================== + private Chat chat; + + public Chat getChat () { return chat; } + + // ======================================== + public ChatManager (OwnFrame controller, Chat chat) { + this.controller = controller; + this.chat = chat; + chat.addChatObserver (this); + jFileChooser.setFileFilter (new FileNameExtensionFilter (Bundle.getLabel ("ChatFilter"), extention)); + jFileChooser.setAccessory (manageExtensionCheckBox = + Util.newCheckButtonConfig ("ManageExtention", this, true)); + } + + // ======================================== + public void actionManageExtention () { + // rien a faire + } + + public void actionPseudo () { + String newName = + (String) JOptionPane.showInputDialog (controller.getJFrame (), Bundle.getLabel ("NewPseudo"), + Bundle.getTitle ("ChangePseudo"), JOptionPane.INFORMATION_MESSAGE, + null, null, Chat.getRandomPseudo ()); + if (newName == null) + return; + chat.setPseudo (newName); + } + + public synchronized void actionClear () { + if (chat.getModified () && + JOptionPane.YES_OPTION != + JOptionPane.showConfirmDialog (controller.getJFrame (), Bundle.getMessage ("RealyClearChat"), + Bundle.getTitle ("ChatNotSaved"), JOptionPane.WARNING_MESSAGE)) + return; + chat.clear (); + } + + final JFileChooser jFileChooser = new JFileChooser (Config.getString ("ChatDir", "data/chat")); + final JCheckBox manageExtensionCheckBox; + + public synchronized void actionSaveAs () { + jFileChooser.setFileSelectionMode (JFileChooser.FILES_ONLY); + if (jFileChooser.showSaveDialog (controller.getJFrame ()) != JFileChooser.APPROVE_OPTION) + return; + File file = jFileChooser.getSelectedFile (); + Config.setString ("ChatDir", file.getParent ()); + Config.save ("Chat"); + try { + if (manageExtensionCheckBox.isSelected () && !file.getName ().endsWith ("."+extention)) + file = new File (file.getParent (), file.getName () + "."+extention); + chat.save (file); + } catch (IOException e) { + JOptionPane.showMessageDialog (controller.getJFrame (), e.getMessage (), "alert", JOptionPane.WARNING_MESSAGE); + } + } + + // ======================================== + private Vector saveCommands = new Vector (); + public void addSaveCommand (AbstractButton saveCommand) { + saveCommands.add (saveCommand); + saveCommand.setEnabled (chat.getModified ()); + } + + // ======================================== + public void talk (ChatQuote quote) {} + public void renameSpeaker (String speaker) {} + public void chatModifiedChange (boolean modified) { + for (AbstractButton saveCommand : saveCommands) + saveCommand.setEnabled (modified); + } + public void clearChat () {} + + // ======================================== + public void addMenuItem (JMenu... jMenu) { + Util.addMenuItem (loginActionsNames, this, jMenu[0]); + Util.addMenuItem (saveActionsNames, this, jMenu[0]); + } + + // ======================================== + public void addIconButtons (Container... containers) { + Util.addIconButton (loginActionsNames, this, containers[0]); + Util.addIconButton (saveActionsNames, this, containers[0]); + } + + // ======================================== + public void addActiveButtons (Hashtable buttons) { + addSaveCommand (buttons.get (actionClear)); + } + + // ======================================== +} diff --git a/src/java/network/chat/ChatObserver.java b/src/java/network/chat/ChatObserver.java new file mode 100644 index 0000000..9d618fd --- /dev/null +++ b/src/java/network/chat/ChatObserver.java @@ -0,0 +1,17 @@ +package network.chat; + +import java.util.Date; + +/** + Modificaion de modèle qui peuvent être observé. +*/ +public interface ChatObserver { + + // ======================================== + public void talk (ChatQuote quote); + public void renameSpeaker (String speaker); + public void chatModifiedChange (boolean modified); + public void clearChat (); + + // ======================================== +} diff --git a/src/java/network/chat/ChatQuote.java b/src/java/network/chat/ChatQuote.java new file mode 100644 index 0000000..dfe41a5 --- /dev/null +++ b/src/java/network/chat/ChatQuote.java @@ -0,0 +1,45 @@ +package network.chat; + +import java.util.Comparator; +import java.util.Date; + +/** + Modificaion de modèle qui peuvent être observé. +*/ +public class ChatQuote implements Comparator { + + int sequence; + Date date; + String speaker; + String sentence; + + // ======================================== + public ChatQuote (int sequence, Date date, String speaker, String sentence) { + this.sequence = sequence; + this.date = date; + this.speaker = speaker; + this.sentence = sentence; + } + + // ======================================== + public int compare (ChatQuote o1, ChatQuote o2) { + if (o1.sequence != o2.sequence) + return o1.sequence - o2.sequence; + int diff = o1.date.compareTo (o2.date); + if (diff != 0) + return diff; + diff = o1.speaker.compareTo (o2.speaker); + if (diff != 0) + return diff; + return o1.sentence.compareTo (o2.sentence); + } + + // ======================================== + public boolean equals (ChatQuote obj) { + return + sentence == obj.sentence && date == obj.date && + speaker.equals (obj.speaker) && sentence.equals (obj.sentence); + } + + // ======================================== +} diff --git a/src/java/network/chat/JChat.java b/src/java/network/chat/JChat.java new file mode 100644 index 0000000..9f282f7 --- /dev/null +++ b/src/java/network/chat/JChat.java @@ -0,0 +1,150 @@ +package network.chat; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collections; +import java.util.Date; +import java.util.Hashtable; +import java.util.Vector; +import javax.swing.AbstractButton; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.plaf.basic.BasicBorders; +import javax.swing.text.BadLocationException; + +import misc.Config; +import misc.Log; +import misc.Util; +import network.ProtocolManager; +import network.login.Login; +import network.login.LoginManager; +import network.login.LoginObserver; + +@SuppressWarnings ("serial") public class JChat extends JPanel implements ChatObserver, LoginObserver, ActionListener { + + // ======================================== + private Chat chat; + + public JTextArea forum; + public Vector dialog; + public JScrollPane jScrollPane; + public JLabel pseudo; + public JTextField message; + + public Chat getChat () { return chat; } + + // ======================================== + public JChat (ProtocolManager protocolManager, LoginManager loginManager, ChatManager chatManager) { + super (new BorderLayout ()); + chat = chatManager.getChat (); + chat.addChatObserver (this); + loginManager.getLogin ().addLoginObserver (this); + + forum = new JTextArea (Integer.parseInt (Config.getString ("ChatRows", "18")), + Integer.parseInt (Config.getString ("ChatColumns", "64"))); + dialog = new Vector (); + jScrollPane = new JScrollPane (forum); + jScrollPane.setVerticalScrollBarPolicy (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + forum.setEditable (false); + message = new JTextField (); + message.setEnabled (true); + message.addActionListener (this); + + pseudo = new JLabel (chat.getPseudo ()+" : "); + pseudo.setEnabled (true); + add (jScrollPane, BorderLayout.CENTER); + JPanel buttonsPanel = new JPanel (null); + buttonsPanel.setLayout (new BoxLayout (buttonsPanel, BoxLayout.X_AXIS)); + Util.addIconButton (ChatManager.loginActionsNames, chatManager, buttonsPanel); + Util.addIconButton (ProtocolManager.actionsNames, protocolManager, buttonsPanel); + Util.addIconButton (ChatManager.saveActionsNames, chatManager, buttonsPanel); + Hashtable buttons = new Hashtable (); + Util.collectButtons (buttons, buttonsPanel); + + Border buttonBorder = + BorderFactory.createCompoundBorder (new EtchedBorder (), + BorderFactory.createCompoundBorder (BasicBorders.getMenuBarBorder (), + new EmptyBorder (3, 3, 3, 3))); + for (Component component : buttonsPanel.getComponents ()) { + try { + AbstractButton button = (AbstractButton) component; + button.setBorder (buttonBorder); + } catch (Exception e) { + } + } + chatManager.addSaveCommand (buttons.get (ChatManager.actionClear)); + JPanel footer = new JPanel (new BorderLayout ()); + footer.add (pseudo, BorderLayout.WEST); + footer.add (message, BorderLayout.CENTER); + footer.add (buttonsPanel, BorderLayout.EAST); + add (footer, BorderLayout.SOUTH); + } + + // ======================================== + public void actionPerformed (ActionEvent e) { + actionSay (); + } + + public void actionSay () { + chat.say (message.getText ()); + message.setText (""); + } + + // ======================================== + private ChatQuote control = new ChatQuote (0, new Date (), "", ""); + + public synchronized void talk (ChatQuote quote) { + dialog.add (quote); + Collections.sort (dialog, control); + String newLine = Chat.dateFormat.format (quote.date)+" "+quote.speaker+" > "+quote.sentence+"\n"; + int quoteIndex = dialog.indexOf (quote); + try { + int lineOffset = forum.getLineStartOffset (quoteIndex); + forum.insert (newLine, lineOffset); + forum.setCaretPosition (lineOffset); + } catch (BadLocationException e) { + try { + forum.append (newLine); + forum.setCaretPosition (forum.getLineEndOffset (forum.getLineCount ()-1)); + } catch (BadLocationException e2) { + Log.keepLastException ("JChat::talk", e2); + } + } + } + + public void renameSpeaker (String speaker) { + pseudo.setText (speaker+" : "); + } + + public void chatModifiedChange (boolean modified) { } + + public void clearChat () { + forum.setText (""); + dialog = new Vector (); + message.setText (""); + } + + // ======================================== + public void updateLogin (String login) {} + public void updateGroup (String groupName) {} + public void updateGroups (String[] groupsName) {} + public void infoGroup (Login.Group group) {} + public void loginState (String state) { + boolean connected = !Login.unknownToken.equals (state); + pseudo.setEnabled (connected); + message.setEnabled (connected); + } + + // ======================================== +} diff --git a/src/java/network/chat/JChatDialog.java b/src/java/network/chat/JChatDialog.java new file mode 100644 index 0000000..8f0e1d5 --- /dev/null +++ b/src/java/network/chat/JChatDialog.java @@ -0,0 +1,44 @@ +package network.chat; + +import java.awt.Frame; +import java.text.MessageFormat; + +import misc.Bundle; +import misc.TitledDialog; + +/** + fenêtre d'habillage pour inclusion. +*/ +@SuppressWarnings ("serial") public class JChatDialog extends TitledDialog implements ChatObserver { + + // ======================================== + public String pseudo; + + // ======================================== + public JChatDialog (Frame frame, JChat jChat) { + super (frame, "Chat"); + pseudo = jChat.getChat ().getPseudo (); + jChat.getChat ().addChatObserver (this); + add (jChat); + updateBundle (); + Bundle.addBundleObserver (this); + } + + // ======================================== + public void updateBundle () { + setTitle (getTitle ()); + } + + // ======================================== + public String getTitle () { + return MessageFormat.format (Bundle.getTitle (titleId), pseudo); + } + + // ======================================== + public void talk (ChatQuote quote) {} + public void renameSpeaker (String speaker) { pseudo = speaker; updateBundle (); } + public void chatModifiedChange (boolean modified) {} + public void clearChat () {} + + // ======================================== +} diff --git a/src/java/network/chat/JChatMenuBar.java b/src/java/network/chat/JChatMenuBar.java new file mode 100644 index 0000000..797b8f7 --- /dev/null +++ b/src/java/network/chat/JChatMenuBar.java @@ -0,0 +1,55 @@ +package network.chat; + +import java.util.Hashtable; +import javax.swing.AbstractButton; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JMenu; +import javax.swing.JMenuBar; + +import network.ProtocolManager; +import network.login.LoginManager; + +import misc.ApplicationManager; +import misc.Log; +import misc.ToolBarManager; +import misc.Util; + +/** + La barre de menu. +*/ +@SuppressWarnings ("serial") public class JChatMenuBar extends JMenuBar { + + // ======================================== + public JChatMenuBar (ApplicationManager controllerManager, + ProtocolManager protocolManager, LoginManager loginManager, ChatManager chatManager, + ApplicationManager helpManager, ToolBarManager toolBarManager) { + setLayout (new BoxLayout (this, BoxLayout.X_AXIS)); + JMenu fileMenu = Util.addJMenu (this, "File"); + JMenu networkMenu = Util.addJMenu (this, "Network"); + add (Box.createHorizontalGlue ()); + JMenu helpMenu = Util.addJMenu (this, "Help"); + + Util.addMenuItem (ChatManager.loginActionsNames, chatManager, fileMenu); + Util.addMenuItem (ChatManager.saveActionsNames, chatManager, fileMenu); + controllerManager.addMenuItem (fileMenu); + protocolManager.addMenuItem (networkMenu); + loginManager.addMenuItem (networkMenu); + helpManager.addMenuItem (helpMenu); + if (toolBarManager != null) + toolBarManager.addMenuItem (helpMenu); + + Hashtable buttons = new Hashtable (); + Util.collectButtons (buttons, fileMenu); + Util.collectButtons (buttons, networkMenu); + Util.collectButtons (buttons, helpMenu); + + controllerManager.addActiveButtons (buttons); + helpManager.addActiveButtons (buttons); + chatManager.addSaveCommand (buttons.get (ChatManager.actionClear)); + if (toolBarManager != null) + toolBarManager.addActiveButtons (buttons); + } + + // ======================================== +} diff --git a/src/java/network/chat/LaunchChat.java b/src/java/network/chat/LaunchChat.java new file mode 100644 index 0000000..a2bf418 --- /dev/null +++ b/src/java/network/chat/LaunchChat.java @@ -0,0 +1,32 @@ +package network.chat; + +import javax.swing.SwingUtilities; + +import misc.Bundle; +import misc.Config; + +/** + Lance le controleur. +*/ +public class LaunchChat { + + // ======================================== + static public void main (String[] args) { + Config.setPWD (LaunchChat.class); + Config.load ("Chat"); + Bundle.load ("Help"); + Bundle.load ("ToolBar"); + Bundle.load ("Controller"); + Bundle.load ("Protocol"); + Bundle.load ("Login"); + Bundle.load ("Chat"); + + SwingUtilities.invokeLater (new Runnable () { + public void run () { + new ChatController (); + } + }); + } + + // ======================================== +} diff --git a/src/java/network/login/JLogin.java b/src/java/network/login/JLogin.java new file mode 100644 index 0000000..2d86ca7 --- /dev/null +++ b/src/java/network/login/JLogin.java @@ -0,0 +1,66 @@ +package network.login; + +import java.awt.Component; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Hashtable; +import javax.swing.AbstractButton; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.plaf.basic.BasicBorders; + +import misc.Util; +import network.ProtocolManager; +import static misc.Util.GBC; +import static misc.Util.GBCNL; + +@SuppressWarnings ("serial") public class JLogin extends JPanel implements LoginObserver { + + // ======================================== + private Login login; + + public JLabel loggedLabel = new JLabel (); + public JLabel groupLabel = new JLabel (); + private JLabel stateLabel = new JLabel (); + + public Login getLogin () { return login; } + + // ======================================== + public JLogin (ProtocolManager protocolManager, LoginManager loginManager) { + super (new GridBagLayout ()); + login = loginManager.getLogin (); + login.addLoginObserver (this); + + Util.addLabelFields (this, "Logged", loggedLabel); + Util.addLabelFields (this, "Group", groupLabel); + Util.addLabelFields (this, "State", stateLabel); + } + + // ======================================== + public void updateLogin (String login) { + loggedLabel.setText (login); + } + + public void updateGroup (String group) { + groupLabel.setText (group); + } + + public void updateGroups (String[] groupsName) { + } + + public void infoGroup (Login.Group group) { + } + + public void loginState (String state) { + stateLabel.setText (state); + } + + // ======================================== +} diff --git a/src/java/network/login/JLoginMenuBar.java b/src/java/network/login/JLoginMenuBar.java new file mode 100644 index 0000000..7e7a525 --- /dev/null +++ b/src/java/network/login/JLoginMenuBar.java @@ -0,0 +1,53 @@ +package network.login; + +import java.util.Hashtable; +import javax.swing.AbstractButton; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JMenu; +import javax.swing.JMenuBar; + +import network.ProtocolManager; + +import misc.ApplicationManager; +import misc.Log; +import misc.ToolBarManager; +import misc.Util; + +/** + La barre de menu. +*/ +@SuppressWarnings ("serial") public class JLoginMenuBar extends JMenuBar { + + // ======================================== + public JLoginMenuBar (ApplicationManager controllerManager, + ProtocolManager protocolManager, LoginManager loginManager, + ApplicationManager helpManager, ToolBarManager toolBarManager) { + setLayout (new BoxLayout (this, BoxLayout.X_AXIS)); + JMenu fileMenu = Util.addJMenu (this, "File"); + JMenu networkMenu = Util.addJMenu (this, "Network"); + JMenu loginMenu = Util.addJMenu (this, "Connection"); + add (Box.createHorizontalGlue ()); + JMenu helpMenu = Util.addJMenu (this, "Help"); + + controllerManager.addMenuItem (fileMenu); + protocolManager.addMenuItem (networkMenu); + loginManager.addMenuItem (loginMenu); + helpManager.addMenuItem (helpMenu); + toolBarManager.addMenuItem (helpMenu); + + Hashtable buttons = new Hashtable (); + Util.collectButtons (buttons, fileMenu); + Util.collectButtons (buttons, networkMenu); + Util.collectButtons (buttons, loginMenu); + Util.collectButtons (buttons, helpMenu); + + controllerManager.addActiveButtons (buttons); + protocolManager.addActiveButtons (buttons); + loginManager.addActiveButtons (buttons); + helpManager.addActiveButtons (buttons); + toolBarManager.addActiveButtons (buttons); + } + + // ======================================== +} diff --git a/src/java/network/login/JLoginToolBar.java b/src/java/network/login/JLoginToolBar.java new file mode 100644 index 0000000..660fa86 --- /dev/null +++ b/src/java/network/login/JLoginToolBar.java @@ -0,0 +1,44 @@ +package network.login; + +import java.awt.Component; +import java.util.Hashtable; +import javax.swing.AbstractButton; +import javax.swing.JToolBar; + +import network.ProtocolManager; + +import misc.ApplicationManager; +import misc.ToolBarManager; +import misc.Util; + +public class JLoginToolBar { + static public String defaultCardinalPoint = "North"; + + public JLoginToolBar (ApplicationManager controllerManager, + ProtocolManager protocolManager, LoginManager loginManager, + ApplicationManager helpManager, ToolBarManager toolBarManager) { + JToolBar fileToolBar = toolBarManager.newJToolBar ("File", defaultCardinalPoint); + JToolBar loginToolBar = toolBarManager.newJToolBar ("Connection", defaultCardinalPoint); + JToolBar protocolToolBar = toolBarManager.newJToolBar ("Network", defaultCardinalPoint); + JToolBar helpToolBar = toolBarManager.newJToolBar ("Help", defaultCardinalPoint); + + controllerManager.addIconButtons (fileToolBar); + loginManager.addIconButtons (loginToolBar); + protocolManager.addIconButtons (protocolToolBar); + if (helpManager != null) + helpManager.addIconButtons (helpToolBar); + toolBarManager.addIconButtons (helpToolBar); + + Hashtable buttons = new Hashtable (); + Util.collectButtons (buttons, fileToolBar); + Util.collectButtons (buttons, loginToolBar); + Util.collectButtons (buttons, protocolToolBar); + Util.collectButtons (buttons, helpToolBar); + + controllerManager.addActiveButtons (buttons); + loginManager.addActiveButtons (buttons); + protocolManager.addActiveButtons (buttons); + helpManager.addActiveButtons (buttons); + toolBarManager.addActiveButtons (buttons); + } +} diff --git a/src/java/network/login/LaunchLogin.java b/src/java/network/login/LaunchLogin.java new file mode 100644 index 0000000..b157293 --- /dev/null +++ b/src/java/network/login/LaunchLogin.java @@ -0,0 +1,27 @@ +package network.login; + +import javax.swing.SwingUtilities; +import misc.Bundle; +import misc.Config; + +public class LaunchLogin { + + // ======================================== + static public void main (String[] args) { + Config.setPWD (LaunchLogin.class); + Config.load ("Login"); + Bundle.load ("Help"); + Bundle.load ("ToolBar"); + Bundle.load ("Controller"); + Bundle.load ("Protocol"); + Bundle.load ("Login"); + + SwingUtilities.invokeLater (new Runnable () { + public void run () { + new LoginController (); + } + }); + } + + // ======================================== +} diff --git a/src/java/network/login/Login.java b/src/java/network/login/Login.java new file mode 100644 index 0000000..6df813b --- /dev/null +++ b/src/java/network/login/Login.java @@ -0,0 +1,370 @@ +package network.login; + +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Random; +import java.util.Vector; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import misc.Bundle; +import misc.Log; +import misc.Util; +import network.Protocol; +import network.Waiter; + +/** + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +public class Login implements Waiter { + + static public final boolean trace = false; + + // ======================================== + static public final String applicationName = "login"; + static public final String userToken = "user"; + static public final String askToken = "ask"; + static public final String loginToken = "login"; + static public final String challengeToken = "challenge"; + static public final String tryIndexToken = "try"; + static public final String resultToken = "result"; + static public final String oldToken = "old"; + static public final String newToken = "new"; + static public final String unknownToken = "unknown"; + static public final String rejectToken = "reject"; + static public final String acceptToken = "accept"; + static public final String connectToken = "connect"; + static public final String disconnectToken = "disconnect"; + static public final String changepassToken = "changepass"; + static public final String groupToken = "group"; + static public final String groupsToken = "groups"; + static public final String setGroupToken = "setGroup"; + static public final String infoToken = "info"; + static public final String nameToken = "name"; + static public final String joinToken = "join"; + static public final String leftToken = "left"; + static public final String createdToken = "created"; + static public final String updatedToken = "updated"; + static public final String ipToken = "ip"; + static public final String updatedByToken = "updatedBy"; + static public final String adminToken = "admin"; + static public final String memberToken = "member"; + static public final String createGroupToken = "createGroup"; + static public final String removeGroupToken = "removeGroup"; + static public final String addGroupAdminToken = "addGroupAdmin"; + static public final String removeGroupAdminToken = "removeGroupAdmin"; + static public final String addGroupMemberToken = "addGroupMember"; + static public final String removeGroupMemberToken = "removeGroupMember"; + + // ======================================== + private Protocol protocol; + /** unknown, reject, accept, untrusted */ + private String state = unknownToken; + private String login = ""; + private String tryIndex = ""; + private String cifferPasswd = ""; + private String challenge; + private String group = ""; + private String[] groups = new String [0]; + + public synchronized void setProtocol (Protocol protocol) { + if (trace) + System.err.println ("setProtocol: protocol:"+protocol); + if (this.protocol != null) + this.protocol.removeWaiter (applicationName, this); + this.protocol = protocol; + if (protocol != null) + protocol.addWaiter (applicationName, this); + } + + public String getState () { return state; } + public String getTryIndex () { return tryIndex; } + public String getLogin () { return login; } + public String getGroup () { return group; } + public Protocol getProtocol () { return protocol; } + + // ======================================== + static public String getRandomLogin () { + return + Bundle.getString ("anonymous", null)+"_"+Integer.toHexString ((new Random ()).nextInt ()).toUpperCase ().substring (0, 4); + } + + // ======================================== + public Login (String login) { + this.login = login; + } + + public Login (String login, Protocol protocol) { + this (login); + setProtocol (protocol); + } + + // ======================================== + public synchronized void unlog () { + if (protocol == null) + return; + protocol.send (applicationName, askToken, disconnectToken); + } + + // ======================================== + public synchronized void log (String login, String cifferPasswd) { + if (trace) + System.err.println ("log: login:"+login+" cifferPasswd:"+cifferPasswd+" challenge:"+challenge); + if (login == null) + return; + if (protocol == null) + return; + this.login = login; + try { + if (challenge != null) { + this.cifferPasswd = cifferPasswd; + protocol.send (applicationName, askToken, connectToken, loginToken, login, resultToken, Util.sha1 (challenge+cifferPasswd)); + } else + protocol.send (applicationName, askToken, connectToken, loginToken, login); + } catch (Exception e) { + Log.keepLastException ("Login::log", e); + protocol.send (applicationName, askToken, connectToken, loginToken, login); + } + } + + // ======================================== + public synchronized void setGroup (String groupName) { + if (protocol == null) + return; + protocol.send (applicationName, askToken, setGroupToken, groupToken, groupName); + } + + public synchronized void unsetGroup () { + setGroup (""); + } + + public synchronized void askGroups () { + if (protocol == null) + return; + groups = new String [0]; + protocol.send (applicationName, askToken, groupsToken); + } + + public synchronized void askGroup (String groupName) { + if (protocol == null) + return; + protocol.send (applicationName, askToken, groupToken, groupToken, groupName); + } + + public synchronized void createGroup (String groupName) { + if (protocol == null) + return; + protocol.send (applicationName, askToken, createGroupToken, groupToken, groupName); + } + + public synchronized void removeGroup (String groupName) { + if (protocol == null) + return; + protocol.send (applicationName, askToken, removeGroupToken, groupToken, groupName); + } + + public synchronized void addGroupAdmin (String groupName, String admin) { + if (protocol == null) + return; + protocol.send (applicationName, askToken, addGroupAdminToken, groupToken, groupName, loginToken, admin); + } + + public synchronized void removeGroupAdmin (String groupName, String admin) { + if (protocol == null) + return; + protocol.send (applicationName, askToken, removeGroupAdminToken, groupToken, groupName, loginToken, admin); + } + + public synchronized void addGroupMember (String groupName, String member) { + if (protocol == null) + return; + protocol.send (applicationName, askToken, addGroupMemberToken, groupToken, groupName, loginToken, member); + } + + public synchronized void removeGroupMember (String groupName, String member) { + if (protocol == null) + return; + protocol.send (applicationName, askToken, removeGroupMemberToken, groupToken, groupName, loginToken, member); + } + + // ======================================== + public synchronized void receiveUser (String state, String result, String challenge, String tryIndex) { + if (unknownToken.equals (state)) { + this.state = state; + this.challenge = challenge; + this.tryIndex = tryIndex; + group = ""; + broadcastUpdateGroup (); + broadcastLoginState (); + } else if (rejectToken.equals (state)) { + this.state = state; + broadcastLoginState (); + } else if (acceptToken.equals (state)) { + this.tryIndex = ""; + String check = null; + try { + check = Util.sha1 (this.challenge+login+cifferPasswd); + } catch (Exception e) { + } + if (trace) + System.err.println ("receive: challenge:"+this.challenge+" login:"+login+" cifferPasswd:"+cifferPasswd+" check:"+check); + if (result.equals (check)) { + this.state = state; + broadcastLoginState (); + broadcastUpdateLogin (); + } else { + this.state = "Untrusted"; + broadcastLoginState (); + } + } else + System.err.println ("receive: unknown state:"+state+" challenge:"+challenge+" result:"+result); + } + + public class Group { + public String name; + public String created; + public String updated; + public String ip; + public String updatedBy; + public String[] admin = new String [0]; + public String[] member = new String [0]; + } + + // ======================================== + public synchronized void receive (Element argument) { + String state = argument.getAttribute (userToken); + if (!state.isEmpty ()) { + String tryIndex = argument.getAttribute (tryIndexToken); + String challenge = argument.getAttribute (challengeToken); + String result = argument.getAttribute (resultToken); + if (trace) + System.err.println ("receive: state:"+state+" challenge:"+challenge+" result:"+result); + receiveUser (state, result, challenge, tryIndex); + return; + } + String groupA = argument.getAttribute (groupToken); + if (!groupA.isEmpty ()) { + if (joinToken.equals (groupA)) + this.group = argument.getAttribute (nameToken); + else if (leftToken.equals (groupA)) + this.group = ""; + broadcastUpdateGroup (); + return; + } + String info = argument.getAttribute (infoToken); + if (!info.isEmpty ()) { + if (groupsToken.equals (info)) { + NodeList groupsName = argument.getElementsByTagName (groupToken); + Vector result = new Vector (); + for (int i = 0; i < groupsName.getLength (); i++) { + Element groupName = (Element) groupsName.item (i); + result.add (groupName.getAttribute (nameToken)); + } + groups = result.toArray (new String [0]); + Arrays.sort (groups); + broadcastUpdateGroups (); + } else if (groupToken.equals (info)) { + + Group group = new Group (); + NodeList groupsNL = argument.getElementsByTagName (groupToken); + // XXX vérification 1 seul élément + Element groupE = (Element) groupsNL.item (0); + group.name = groupE.getAttribute (nameToken); + group.created = groupE.getAttribute (createdToken); + group.updated = groupE.getAttribute (updatedToken); + group.ip = groupE.getAttribute (ipToken); + group.updatedBy = groupE.getAttribute (updatedByToken); + + NodeList AdminNL = argument.getElementsByTagName (adminToken); + Vector result = new Vector (); + for (int i = 0; i < AdminNL.getLength (); i++) { + Element AdminE = (Element) AdminNL.item (i); + result.add (AdminE.getAttribute (nameToken)); + } + group.admin = result.toArray (new String [0]); + Arrays.sort (group.admin); + NodeList memberNL = argument.getElementsByTagName (memberToken); + result = new Vector (); + for (int i = 0; i < memberNL.getLength (); i++) { + Element memberE = (Element) memberNL.item (i); + result.add (memberE.getAttribute (nameToken)); + } + group.member = result.toArray (new String [0]); + Arrays.sort (group.member); + + broadcastInfoGroup (group); + } + // XXX passwdUnchanged + // XXX passwdUpdated + } + } + + // ======================================== + Vector loginObservers = new Vector (); + public void addLoginObserver (LoginObserver loginObserver) { loginObservers.add (loginObserver); } + public void removeLoginObserver (LoginObserver loginObserver) { loginObservers.remove (loginObserver); } + + public void broadcastUpdateLogin () { + if (trace) + System.err.println ("broadcastUpdateLogin: login:"+login); + for (LoginObserver loginObserver : loginObservers) + loginObserver.updateLogin (login); + } + + public void broadcastUpdateGroup () { + if (trace) + System.err.println ("broadcastUpdateGroup: group:"+group); + for (LoginObserver loginObserver : loginObservers) + loginObserver.updateGroup (group); + } + + public void broadcastUpdateGroups () { + if (trace) + System.err.println ("broadcastUpdateGroups: groups size:"+groups.length); + for (LoginObserver loginObserver : loginObservers) + loginObserver.updateGroups (groups); + } + + public void broadcastInfoGroup (Group group) { + if (trace) + System.err.println ("broadcastInfoGroup: group:"); + for (LoginObserver loginObserver : loginObservers) + loginObserver.infoGroup (group); + } + + public void broadcastLoginState () { + if (trace) + System.err.println ("broadcastLoginState: state:"+state); + for (LoginObserver loginObserver : loginObservers) + loginObserver.loginState (state); + } + + // ======================================== +} diff --git a/src/java/network/login/LoginController.java b/src/java/network/login/LoginController.java new file mode 100644 index 0000000..06ec3d6 --- /dev/null +++ b/src/java/network/login/LoginController.java @@ -0,0 +1,87 @@ +package network.login; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Image; +import java.text.MessageFormat; +import javax.swing.JMenuBar; +import javax.swing.JPanel; + +import network.ProtocolManager; + +import misc.Controller; +import misc.Bundle; +import misc.Config; +import misc.HelpManager; +import misc.MultiToolBarBorderLayout; +import misc.ToolBarManager; +import misc.Util; + +public class LoginController extends Controller implements LoginObserver { + + // ======================================== + public LoginController () { + super (new Login (Config.getString ("pseudo", Login.getRandomLogin ()))); + } + + // ======================================== + Login login; + + ProtocolManager protocolManager; + LoginManager loginManager; + HelpManager helpManager; + ToolBarManager toolBarManager; + + JLogin jLogin; + JLoginMenuBar jLoginMenuBar; + JLoginToolBar jLoginToolBar; + + // ======================================== + public String getTitle () { return MessageFormat.format (Bundle.getTitle ("Login"), login.getLogin ()); } + public Image getIcon () { return Util.loadImage (Config.getString ("LoginIcon", "data/images/login/login.png")); } + + // ======================================== + protected boolean tryClosingWindows () { + Config.save ("Login"); + return true; + } + + // ======================================== + protected void createModel (Login login) { + this.login = login; + login.addLoginObserver (this); + } + + // ======================================== + protected Component createGUI () { + JPanel contentPane = new JPanel (new MultiToolBarBorderLayout ()); + toolBarManager = new ToolBarManager (getIcon (), contentPane); + + protocolManager = new ProtocolManager (this); + protocolManager.addWaiter (login); + protocolManager.start (); + + loginManager = new LoginManager (this, login); + helpManager = new HelpManager (this, "Login"); + jLogin = new JLogin (protocolManager, loginManager); + + jLoginToolBar = new JLoginToolBar (this, protocolManager, loginManager, helpManager, toolBarManager); + contentPane.add (jLogin, BorderLayout.CENTER); + return contentPane; + } + + // ======================================== + protected JMenuBar createMenuBar () { + jLoginMenuBar = new JLoginMenuBar (this, protocolManager, loginManager, helpManager, toolBarManager); + return jLoginMenuBar; + } + + // ======================================== + public void updateLogin (String login) {} + public void updateGroup (String group) {} + public void updateGroups (String[] groupsName) {} + public void infoGroup (Login.Group group) {} + public void loginState (String state) {} + + // ======================================== +} diff --git a/src/java/network/login/LoginManager.java b/src/java/network/login/LoginManager.java new file mode 100644 index 0000000..fafd2d0 --- /dev/null +++ b/src/java/network/login/LoginManager.java @@ -0,0 +1,443 @@ +package network.login; + +import java.awt.Container; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.Vector; + +import javax.swing.AbstractButton; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JMenu; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import misc.ApplicationManager; +import misc.Bundle; +import misc.Config; +import misc.Log; +import misc.OwnFrame; +import misc.Util; +import static misc.Util.GBC; +import static misc.Util.GBCNL; + +/** + XXX loginTitle a mettre à jour "Login"+Bundle.titlePostfix + + comportement déclanché par des actionneurs graphiques (menu ou bouton). +*/ +@SuppressWarnings ("serial") public class LoginManager implements ApplicationManager, ActionListener, LoginObserver { + + // ======================================== + static public final String actionLog = "Log"; + static public final String actionUnlog = "Unlog"; + static public final String actionJoinGroup = "JoinGroup"; + static public final String actionLeftGroup = "LeftGroup"; + static public final String actionEditLogin = "EditLogin"; + static public final String actionEditGroup = "EditGroup"; + static public final String actionCreateGroup = "CreateGroup"; + static public final String actionRemoveGroup = "RemoveGroup"; + static public final String actionAddAdmin = "AddAdmin"; + static public final String actionRemoveAdmin = "RemoveAdmin"; + static public final String actionAddMember = "AddMember"; + static public final String actionRemoveMember = "RemoveMember"; + + static public final List loginActionsNames = + Arrays.asList (actionLog, actionUnlog, actionJoinGroup, actionLeftGroup, + actionEditLogin, actionEditGroup); + static public final List dialogActionsNames = + Arrays.asList (actionCreateGroup, actionRemoveGroup, + actionAddAdmin, actionRemoveAdmin, actionAddMember, actionRemoveMember); + @SuppressWarnings ("unchecked") + static public final List actionsNames = + Util.merge (loginActionsNames, dialogActionsNames); + @SuppressWarnings ("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (LoginManager.class, actionsNames); + + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + + // ======================================== + private OwnFrame controller; + private Login login; + + private JPanel loginPanel; + private JComboBox pseudoCB; + private JPasswordField pseudoPasswordField; + private JLabel tryLabel; + + private JPanel groupPanel; + private JList groupsList; + + private JPanel editLoginPanel; + + private JLabel pseudoLabel; + private JLabel createdAtLoginLabel; + private JLabel updatedAtLoginLabel; + private JLabel updatedByIPLoginLabel; + private JLabel updatedByLoginLabel; + private JTextField emailTF; + private JPasswordField oldPasswordField; + private JPasswordField newPasswordField; + private JPasswordField confirmPasswordField; + + private String askedGroupName; + + private JPanel editGroupPanel; + private JComboBox groupNameCB; + private JList groupsNameList; + private JButton createGroupButton; + private JButton removeGroupButton; + + private JLabel groupNameLabel; + private JLabel createdLabel; + private JLabel updatedLabel; + private JLabel ipLabel; + private JLabel updatedByLabel; + + private JComboBox adminCB; + private JList adminList; + private JButton addAdminButton; + private JButton removeAdminButton; + + private JComboBox memberCB; + private JList memberList; + private JButton addMemberButton; + private JButton removeMemberButton; + + public Login getLogin () { return login; } + + public void addMenuItem (JMenu... jMenu) { + int idx = 0; + Util.addMenuItem (loginActionsNames, this, jMenu[idx++]); + } + public void addIconButtons (Container... containers) { + int idx = 0; + Util.addIconButton (loginActionsNames, this, containers[idx++]); + } + + // ======================================== + public LoginManager (OwnFrame controller, Login login) { + this.controller = controller; + this.login = login; + + for (String actionName : Arrays.asList (actionLog, actionUnlog, + actionEditLogin, actionEditGroup, actionJoinGroup, actionLeftGroup)) + commands.put (actionName, new Vector ()); + + + pseudoCB = new JComboBox (); + Config.loadJComboBox ("pseudo", pseudoCB, Login.getRandomLogin ()); + pseudoCB.setEditable (true); + pseudoPasswordField = new JPasswordField (); + tryLabel = new JLabel (); + + loginPanel = Util.getGridBagPanel (); + + Util.addLabelFields (loginPanel, "Login", pseudoCB); + Util.addLabelFields (loginPanel, "Password", pseudoPasswordField); + Util.addLabelFields (loginPanel, "Try", tryLabel); + + groupsList = new JList (); + groupsList.setSelectionMode (ListSelectionModel.SINGLE_INTERVAL_SELECTION); + groupsList.setLayoutOrientation (JList.VERTICAL_WRAP); + groupsList.setVisibleRowCount (-1); + JScrollPane listScroller = new JScrollPane (groupsList); + listScroller.setPreferredSize (new Dimension (250, 80)); + + groupPanel = Util.getGridBagPanel (); + + Util.addLabel ("JoinGroup", SwingConstants.RIGHT, groupPanel, GBCNL); + Util.addComponent (listScroller, groupPanel, GBCNL); + + groupsNameList = new JList (); + groupsNameList.setSelectionMode (ListSelectionModel.SINGLE_INTERVAL_SELECTION); + groupsNameList.setLayoutOrientation (JList.VERTICAL_WRAP); + groupsNameList.setVisibleRowCount (-1); + JScrollPane groupsNameListScroller = new JScrollPane (groupsNameList); + groupsNameListScroller.setPreferredSize (new Dimension (200, 50)); + + groupsNameList.addListSelectionListener (new ListSelectionListener () { + public void valueChanged (ListSelectionEvent e) { + actionSelectGroup (); + } + }); + + adminList = new JList (); + adminList.setSelectionMode (ListSelectionModel.SINGLE_INTERVAL_SELECTION); + adminList.setLayoutOrientation (JList.VERTICAL_WRAP); + adminList.setVisibleRowCount (-1); + JScrollPane adminListScroller = new JScrollPane (adminList); + adminListScroller.setPreferredSize (new Dimension (200, 50)); + + memberList = new JList (); + memberList.setSelectionMode (ListSelectionModel.SINGLE_INTERVAL_SELECTION); + memberList.setLayoutOrientation (JList.VERTICAL_WRAP); + memberList.setVisibleRowCount (-1); + JScrollPane memberListScroller = new JScrollPane (memberList); + memberListScroller.setPreferredSize (new Dimension (200, 50)); + + editLoginPanel = Util.getGridBagPanel (); + + pseudoLabel = new JLabel (); + createdAtLoginLabel = new JLabel (); + updatedAtLoginLabel = new JLabel (); + updatedByLoginLabel = new JLabel (); + updatedByIPLoginLabel = new JLabel (); + emailTF = new JTextField (20); + oldPasswordField = new JPasswordField (10); + newPasswordField = new JPasswordField (10); + confirmPasswordField = new JPasswordField (10); + + Util.addLabelFields (editLoginPanel, "Login", pseudoLabel); + Util.addLabelFields (editLoginPanel, "CreatedAt", createdAtLoginLabel); + Util.addLabelFields (editLoginPanel, "UpdatedAt", updatedAtLoginLabel); + Util.addLabelFields (editLoginPanel, "UpdatedBy", updatedByLoginLabel); + Util.addLabelFields (editLoginPanel, "UpdatedByIP", updatedByIPLoginLabel); + Util.addLabelFields (editLoginPanel, "Email", emailTF); + Util.addLabelFields (editLoginPanel, "OldPasswd", oldPasswordField); + Util.addLabelFields (editLoginPanel, "NewPasswd", newPasswordField); + Util.addLabelFields (editLoginPanel, "ConfirmPasswd", confirmPasswordField); + + groupNameCB = new JComboBox (); + adminCB = new JComboBox (); + memberCB = new JComboBox (); + Config.loadJComboBox ("groupName", groupNameCB, ""); + Config.loadJComboBox ("groupAdmin", adminCB, ""); + Config.loadJComboBox ("groupMember", memberCB, ""); + groupNameCB.setEditable (true); + adminCB.setEditable (true); + memberCB.setEditable (true); + + editGroupPanel = Util.getGridBagPanel (); + + Util.addComponent (groupNameCB, editGroupPanel, GBC); + createGroupButton = Util.addButton (actionCreateGroup, this, editGroupPanel, GBCNL); + Util.addComponent (groupsNameListScroller, editGroupPanel, GBC); + removeGroupButton = Util.addButton (actionRemoveGroup, this, editGroupPanel, GBCNL); + + groupNameLabel = new JLabel (); + createdLabel = new JLabel (); + updatedLabel = new JLabel (); + ipLabel = new JLabel (); + updatedByLabel = new JLabel (); + + Util.addLabelFields (editGroupPanel, "GroupName", groupNameLabel); + Util.addLabelFields (editGroupPanel, "CreatedAt", createdLabel); + Util.addLabelFields (editGroupPanel, "UpdatedAt", updatedLabel); + Util.addLabelFields (editGroupPanel, "UpdatedByIP", ipLabel); + Util.addLabelFields (editGroupPanel, "UpdatedBy", updatedByLabel); + + + Util.addComponent (adminCB, editGroupPanel, GBC); + addAdminButton = Util.addButton (actionAddAdmin, this, editGroupPanel, GBCNL); + Util.addComponent (adminListScroller, editGroupPanel, GBC); + removeAdminButton = Util.addButton (actionRemoveAdmin, this, editGroupPanel, GBCNL); + + Util.addComponent (memberCB, editGroupPanel, GBC); + addMemberButton = Util.addButton (actionAddMember, this, editGroupPanel, GBCNL); + Util.addComponent (memberListScroller, editGroupPanel, GBC); + removeMemberButton = Util.addButton (actionRemoveMember, this, editGroupPanel, GBCNL); + + login.addLoginObserver (this); + } + + // ======================================== + public void actionLog () { + tryLabel.setText (login.getTryIndex ()); + if (JOptionPane.showConfirmDialog (controller.getJFrame (), + loginPanel, + Bundle.getTitle ("Login"), + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) + return; + Config.saveJComboBox ("pseudo", pseudoCB); + String passwd = null; + try { + if (pseudoPasswordField.getPassword ().length > 0) + passwd = Util.sha1 (new String (pseudoPasswordField.getPassword ())); + } catch (Exception e) { + Log.keepLastException ("LoginManager::actionLog", e); + } + login.log ((String) pseudoCB.getSelectedItem (), passwd); + } + + // ======================================== + public void actionUnlog () { + login.unlog (); + } + + // ======================================== + public void actionJoinGroup () { + login.askGroups (); + if (JOptionPane. + showConfirmDialog (controller.getJFrame (), + groupPanel, + Bundle.getTitle ("Group"), + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) + return; + login.setGroup (groupsList.getSelectedValue ()); + } + + // ======================================== + public void actionLeftGroup () { + login.unsetGroup (); + } + + // ======================================== + public void actionEditLogin () { + if (JOptionPane.showConfirmDialog (controller.getJFrame (), + editLoginPanel, + Bundle.getTitle ("ManageLogin"), + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) + return; + // XXX ecrire la fonction : edit login, password et email + JOptionPane. + showMessageDialog (controller.getJFrame (), + "Cette fonction n'est encore pas developpee", + "Developpement en cours", + JOptionPane.QUESTION_MESSAGE); + } + + // ======================================== + public void actionEditGroup () { + login.askGroups (); + JOptionPane. + showMessageDialog (controller.getJFrame (), + editGroupPanel, + Bundle.getTitle ("ManageGroup"), + JOptionPane.QUESTION_MESSAGE); + groupsNameList.setListData (new String [0]); + adminList.setListData (new String [0]); + memberList.setListData (new String [0]); + Config.saveJComboBox ("groupName", groupNameCB); + Config.saveJComboBox ("groupAdmin", adminCB); + Config.saveJComboBox ("groupMember", memberCB); + } + + // ======================================== + public void actionSelectGroup () { + infoGroup (null); + askedGroupName = groupsNameList.getSelectedValue (); + if (!askedGroupName.isEmpty ()) + login.askGroup (askedGroupName); + } + + // ======================================== + public void actionCreateGroup () { + login.createGroup ((String) groupNameCB.getSelectedItem ()); + } + public void actionRemoveGroup () { + login.removeGroup (groupsNameList.getSelectedValue ()); + } + public void actionAddAdmin () { + login.addGroupAdmin (groupNameLabel.getText (), adminCB.getItemAt (adminCB.getSelectedIndex ())); + } + public void actionRemoveAdmin () { + login.removeGroupAdmin (groupNameLabel.getText (), adminList.getSelectedValue ()); + } + public void actionAddMember () { + login.addGroupMember (groupNameLabel.getText (), memberCB.getItemAt (memberCB.getSelectedIndex ())); + } + public void actionRemoveMember () { + login.removeGroupMember (groupNameLabel.getText (), memberList.getSelectedValue ()); + } + + // ======================================== + public void updateLogin (String login) {} + + public void updateGroup (String group) { + updateCommands (); + groupsList.setSelectedValue (group, true); + } + + public void updateGroups (String[] groupsName) { + if (groupsName == null) + return; + groupsNameList.setListData (groupsName); + if (askedGroupName != null && Arrays.binarySearch (groupsName, askedGroupName) < 0) { + askedGroupName = null; + infoGroup (null); + } + groupsList.setListData (groupsName); + groupsList.setSelectedValue (login.getGroup (), true); + } + + public void infoGroup (Login.Group group) { + if (askedGroupName == null || group == null) { + groupNameLabel.setText (""); + createdLabel.setText (""); + updatedLabel.setText (""); + ipLabel.setText (""); + updatedByLabel.setText (""); + adminList.setListData (new String [0]); + memberList.setListData (new String [0]); + } else if (askedGroupName.equals (group.name)) { + groupNameLabel.setText (group.name); + createdLabel.setText (group.created); + updatedLabel.setText (group.updated); + ipLabel.setText (group.ip); + updatedByLabel.setText (group.updatedBy); + adminList.setListData (group.admin); + memberList.setListData (group.member); + } + } + + public void loginState (String state) { + updateCommands (); + if (Login.unknownToken.equals (state)) { + actionLog (); + } + } + + // ======================================== + private Hashtable> commands = new Hashtable> (); + public void addActiveButtons (Hashtable buttons) { + for (String actionName : loginActionsNames) + commands.get (actionName).add (buttons.get (actionName)); + updateCommands (); + } + + // public void removeCommand (String actionName, AbstractButton command) { + // commands.get (actionName).remove (command); + // } + + // ======================================== + public synchronized void updateCommands () { + boolean isUnknowned = Login.unknownToken.equals (login.getState ()); + for (AbstractButton logCommand : commands.get (actionLog)) + logCommand.setEnabled (isUnknowned); + for (AbstractButton unlogCommand : commands.get (actionUnlog)) + unlogCommand.setEnabled (!isUnknowned); + for (AbstractButton editLoginCommand : commands.get (actionEditLogin)) + editLoginCommand.setEnabled (!isUnknowned); + for (AbstractButton editGroupCommand : commands.get (actionEditGroup)) + editGroupCommand.setEnabled (!isUnknowned); + boolean isGroup = !login.getGroup ().isEmpty (); + for (AbstractButton joinGroupCommand : commands.get (actionJoinGroup)) + joinGroupCommand.setEnabled (!isUnknowned && !isGroup); + for (AbstractButton leftGroupCommand : commands.get (actionLeftGroup)) + leftGroupCommand.setEnabled (!isUnknowned && isGroup); + } + + // ======================================== +} diff --git a/src/java/network/login/LoginObserver.java b/src/java/network/login/LoginObserver.java new file mode 100644 index 0000000..7f57da4 --- /dev/null +++ b/src/java/network/login/LoginObserver.java @@ -0,0 +1,16 @@ +package network.login; + +/** + Modificaion de modèle qui peuvent être observé. +*/ +public interface LoginObserver { + + // ======================================== + public void updateLogin (String login); + public void updateGroup (String groupName); + public void updateGroups (String[] groupsName); + public void infoGroup (Login.Group group); + public void loginState (String state); + + // ======================================== +} diff --git a/src/java/overview.html b/src/java/overview.html new file mode 100644 index 0000000..e36b8f1 --- /dev/null +++ b/src/java/overview.html @@ -0,0 +1,24 @@ + +

+ En résumé, les fichiers sources qui vous sont présentés, + sont les versions succéssives de la même application, + pour vous permettre d'aprendre Java pas après pas. +

+

+ La première version à consulter est bien évidement + la version 1. + Les versions suivantes suivent l'ordre numérique. +

+

+ Ce texte provient de la documentation mise dans le "pseudo" fichier HTML de nom + "overview.html" à la racines des fichiers sources. + Voici un ordre de lecture des paquetages. +

    +
  • misc
  • +
  • tool
  • +
  • network
  • +
+ + @author F. Merciol +

+ diff --git a/src/java/tool/LaunchBundleManager.java b/src/java/tool/LaunchBundleManager.java new file mode 100644 index 0000000..5c8cb17 --- /dev/null +++ b/src/java/tool/LaunchBundleManager.java @@ -0,0 +1,29 @@ +package tool; + +import javax.swing.SwingUtilities; + +import misc.Bundle; +import misc.Config; + +import tool.controller.BundleManagerController; + +public class LaunchBundleManager { + + // ======================================== + static public void main (String [] arg) { + Config.setPWD (LaunchBundleManager.class); + Config.load ("BundleManager"); + Bundle.load ("Help"); + Bundle.load ("ToolBar"); + Bundle.load ("Controller"); + Bundle.load ("BundleManager"); + + SwingUtilities.invokeLater (new Runnable() { + public void run() { + new BundleManagerController (); + } + }); + } + + // ======================================== +} diff --git a/src/java/tool/controller/BundleManagerController.java b/src/java/tool/controller/BundleManagerController.java new file mode 100644 index 0000000..86d0abc --- /dev/null +++ b/src/java/tool/controller/BundleManagerController.java @@ -0,0 +1,110 @@ +package tool.controller; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Image; +import java.text.MessageFormat; +import javax.swing.JMenuBar; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSplitPane; + +import misc.Bundle; +import misc.Config; +import misc.Controller; +import misc.HelpManager; +import misc.MultiToolBarBorderLayout; +import misc.ToolBarManager; +import misc.Util; +import tool.model.BundleManagerModel; +import tool.model.BundleManagerObserver; +import tool.view.BundleManagerManager; +import tool.view.BundleManagerMenu; +import tool.view.BundleManagerToolBar; +import tool.view.BundleManagerView; +import tool.view.SourceIteratorView; + +public class BundleManagerController extends Controller implements BundleManagerObserver { + + // ======================================== + public BundleManagerController () { + super (new BundleManagerModel ()); + } + + // ======================================== + BundleManagerModel bundleManagerModel; + + BundleManagerManager bundleManagerManager; + HelpManager helpManager; + + BundleManagerView bundleManagerView; + BundleManagerMenu bundleManagerMenu; + + BundleManagerToolBar bundleManagerToolBar; + ToolBarManager toolBarManager; + + // ======================================== + public String getTitle () { + String bundleName = bundleManagerManager.getBundleName (); + return MessageFormat.format (Bundle.getTitle ("BundleEditor"), + (bundleName == null) ? Bundle.getAction ("Empty") : bundleName); + } + public Image getIcon () { return Util.loadImage (Config.getString ("BundleIcon", "data/images/bundle/bundle.png")); } + + // ======================================== + protected void createModel (BundleManagerModel bundleManagerModel) { + this.bundleManagerModel = bundleManagerModel; + bundleManagerModel.addBundleObserver (this); + } + + // ======================================== + protected Component createGUI () { + JPanel contentPane = new JPanel (new MultiToolBarBorderLayout ()); + toolBarManager = new ToolBarManager (getIcon (), contentPane); + + bundleManagerView = new BundleManagerView (bundleManagerModel); + bundleManagerManager = new BundleManagerManager (this, bundleManagerModel, bundleManagerView); + + JSplitPane splitPane = new JSplitPane (JSplitPane.VERTICAL_SPLIT, + bundleManagerView, new SourceIteratorView ()); + splitPane.setOneTouchExpandable (true); + + helpManager = new HelpManager (this, "BundleManager"); + + bundleManagerToolBar = new BundleManagerToolBar (this, bundleManagerManager, helpManager, toolBarManager); + contentPane.add (splitPane, BorderLayout.CENTER); + return contentPane; + } + + // ======================================== + protected JMenuBar createMenuBar () { + return bundleManagerMenu = new BundleManagerMenu (this, bundleManagerManager, helpManager, toolBarManager); + } + + // ======================================== + protected boolean tryClosingWindows () { + toolBarManager.saveLocation (); + Config.save ("BundleManager"); + if (bundleManagerModel.getModified ()) { + switch (JOptionPane.showConfirmDialog (jFrame, Bundle.getLabel ("SaveBundle"), Bundle.getTitle ("BundleNotSaved"), + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE)) { + case JOptionPane.YES_OPTION: + bundleManagerManager.actionSave (); + return true; + case JOptionPane.NO_OPTION: + return true; + case JOptionPane.CANCEL_OPTION: + case JOptionPane.CLOSED_OPTION: + return false; + } + } + return true; + } + + // ======================================== + public void dataModifiedChange () { jFrame.setTitle (getTitle ()); } + public void localesUpdated () {} + public void dataUpdated () {} + + // ======================================== +} diff --git a/src/java/tool/model/BundleManagerModel.java b/src/java/tool/model/BundleManagerModel.java new file mode 100644 index 0000000..09f37e7 --- /dev/null +++ b/src/java/tool/model/BundleManagerModel.java @@ -0,0 +1,248 @@ +package tool.model; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.lang.reflect.Method; +import java.text.MessageFormat; +import java.util.Hashtable; +import java.util.Properties; +import java.util.TreeSet; +import java.util.Vector; + +import misc.Bundle; +import misc.Log; + +public class BundleManagerModel { + + // ======================================== + static public final String extention = ".properties"; + + private Hashtable bundles; + private TreeSet keys; + private TreeSet locales; + + // ======================================== + public final TreeSet getKeys () { + return keys; + } + + // ======================================== + public String getKeyByIndex (int idx) { + return (String) keys.toArray () [idx]; + } + + // ======================================== + public final TreeSet getLocales () { + return locales; + } + + // ======================================== + public String getLocaleByIndex (int idx) { + return (String) locales.toArray () [idx]; + } + + // ======================================== + private boolean modified = false; + public boolean getModified () { + return modified; + } + + // ======================================== + public BundleManagerModel () { + empty (); + } + + // ======================================== + public void empty () { + bundles = new Hashtable (); + keys = new TreeSet (); + locales = new TreeSet (); + modified = false; + broadcastMethode ("localesUpdated"); + broadcastMethode ("dataUpdated"); + broadcastMethode ("dataModifiedChange"); + } + + // ======================================== + public void open (File bundleDir, final String bundleName, boolean merge) { + File [] bundleFile = bundleDir.listFiles (new FilenameFilter () { + public boolean accept (File dir, String name) { + return name.startsWith (bundleName) && name.endsWith (extention); + } + }); + if (!merge) + empty (); + int bundleNameLength = bundleName.length (); + int extentionLength = extention.length (); + for (File file : bundleFile) { + String fileName = file.getName (); + locales.add (fileName.substring (bundleNameLength, fileName.length ()-extentionLength)); + } + for (String locale : locales) { + try { + Properties properties = new Properties (); + FileInputStream fileInputStream = new FileInputStream (new File (bundleDir, bundleName+locale+extention)); + properties.load (fileInputStream); + fileInputStream.close (); + Properties old = bundles.get (locale); + if (old != null && merge) { + for (Object key : old.keySet ()) + properties.put (key, old.get (key)); + } + bundles.put (locale, properties); + for (Object key : properties.keySet ()) + keys.add ((String) key); + } catch (IOException e) { + System.err.println (MessageFormat.format (Bundle.getException ("CantLoad"), locale)); + } + } + modified = merge; + broadcastMethode ("localesUpdated"); + broadcastMethode ("dataUpdated"); + broadcastMethode ("dataModifiedChange"); + } + + // ======================================== + public void saveAs (File bundleDir, final String bundleName) { + // XXX en cas de réécriture dans le meme repertoire supprimer les "locale" supprimes + bundleDir.mkdirs (); + boolean trouble = false; + for (String locale : locales) { + try { + FileOutputStream fileOutputStream = new FileOutputStream (new File (bundleDir, bundleName+locale+extention)); + bundles.get (locale).store (fileOutputStream, Bundle.getString ("HeaderBundleFile", null)); + fileOutputStream.close (); + } catch (IOException e) { + System.err.println (MessageFormat.format (Bundle.getException ("CantSave"), locale)); + trouble = true; + } + } + modified = trouble; + broadcastMethode ("dataModifiedChange"); + } + + // ======================================== + public void addKey (String newKey) { + if (keys.contains (newKey)) + throw new IllegalArgumentException (MessageFormat.format (Bundle.getException ("DuplicatedKey"), newKey)); + keys.add (newKey); + broadcastMethode ("dataUpdated"); + } + + // ======================================== + public void renameKey (String key, String newKey) { + if (keys.contains (newKey)) + throw new IllegalArgumentException (MessageFormat.format (Bundle.getException ("DuplicatedKey"), newKey)); + for (String locale : locales) { + try { + Properties properties = bundles.get (locale); + String val = properties.getProperty (key); + properties.remove (key); + properties.setProperty (newKey, val); + modified = true; + } catch (Exception e) { + // key not defined on this locale + } + } + keys.remove (key); + keys.add (newKey); + broadcastMethode ("dataUpdated"); + broadcastMethode ("dataModifiedChange"); + } + + // ======================================== + public void removeKey (String key) { + if (!keys.contains (key)) + return; + for (String locale : locales) { + try { + Properties properties = bundles.get (locale); + properties.remove (key); + modified = true; + } catch (Exception e) { + // key not defined on this locale + } + } + keys.remove (key); + broadcastMethode ("dataUpdated"); + broadcastMethode ("dataModifiedChange"); + } + + // ======================================== + public void add (String locale) { + if (locales.contains (locale)) + return; + locales.add (locale); + bundles.put (locale, new Properties ()); + modified = true; + broadcastMethode ("localesUpdated"); + broadcastMethode ("dataModifiedChange"); + } + + // ======================================== + public void renameLocale (String locale, String newLocale) { + if (!locales.contains (locale)) + return; + if (locales.contains (newLocale)) + throw new IllegalArgumentException (MessageFormat.format (Bundle.getException ("DuplicatedLocale"), + newLocale)); + locales.remove (locale); + bundles.put (newLocale, bundles.remove (locale)); + locales.add (newLocale); + modified = true; + broadcastMethode ("localesUpdated"); + broadcastMethode ("dataModifiedChange"); + } + + // ======================================== + public void removeLocale (String locale) { + if (!locales.contains (locale)) + return; + locales.remove (locale); + bundles.remove (locale); + modified = true; + broadcastMethode ("localesUpdated"); + broadcastMethode ("dataModifiedChange"); + } + + // ======================================== + public String get (String locale, String key) { + try { + return bundles.get (locale).getProperty (key); + } catch (Exception e) { + return null; + } + } + + // ======================================== + public void set (String locale, String key, String val) { + String oldVal = get (locale, key); + if (val == oldVal || val.equals (oldVal)) + return; + bundles.get (locale).setProperty (key, val); + modified = true; + broadcastMethode ("dataUpdated"); + broadcastMethode ("dataModifiedChange"); + } + + // ======================================== + Vector bundleObservers = new Vector (); + public void addBundleObserver (BundleManagerObserver bundleObserver) { bundleObservers.add (bundleObserver); } + public void removeBundleObserver (BundleManagerObserver bundleObserver) { bundleObservers.remove (bundleObserver); } + + // ======================================== + public void broadcastMethode (String methodeName) { + try { + Method methode = BundleManagerObserver.class.getMethod (methodeName); + for (BundleManagerObserver bundleObserver : bundleObservers) + methode.invoke (bundleObserver); + } catch (Exception e) { + Log.keepLastException ("BundleManagerModel::broadcastMethode", e); + } + } + + // ======================================== +} diff --git a/src/java/tool/model/BundleManagerObserver.java b/src/java/tool/model/BundleManagerObserver.java new file mode 100644 index 0000000..32d03a9 --- /dev/null +++ b/src/java/tool/model/BundleManagerObserver.java @@ -0,0 +1,11 @@ +package tool.model; + +public interface BundleManagerObserver { + + // ======================================== + public void dataModifiedChange (); + public void localesUpdated (); + public void dataUpdated (); + + // ======================================== +} diff --git a/src/java/tool/model/SourceIteratorModel.java b/src/java/tool/model/SourceIteratorModel.java new file mode 100644 index 0000000..81e6196 --- /dev/null +++ b/src/java/tool/model/SourceIteratorModel.java @@ -0,0 +1,104 @@ +package tool.model; + +import java.io.*; +import java.util.*; + +public class SourceIteratorModel { + + // ======================================== + Vector files = new Vector (); + Enumeration javaEnumeration; + + // ======================================== + public SourceIteratorModel (File dir) { + add (dir); + javaEnumeration = files.elements (); + } + + // ======================================== + private void add (File dir) { + for (File file : + dir.listFiles (new FilenameFilter () { + public boolean accept (File dir, String name) { + return name.endsWith (".java"); + } + })) + files.add (file); + for (File subdir : + dir.listFiles (new FileFilter () { + public boolean accept (File child) { + return child.isDirectory (); + } + })) + add (subdir); + } + + // ======================================== + public boolean hasMoreJava () { + return javaEnumeration.hasMoreElements (); + } + + // ======================================== + public File nextJava () { + return javaEnumeration.nextElement (); + } + + // ======================================== + String nextString = null; + int currentLine = 0; + File currentFile; + BufferedReader currentBuffered = null; + + // ======================================== + public int getCurrentLine () { + return currentLine; + } + + // ======================================== + public File getCurrentFile () { + return currentFile; + } + + // ======================================== + public boolean hasMoreString () { + for (;;) { + if (nextString != null) + return true; + if (currentBuffered == null) { + if (! hasMoreJava ()) + return false; + try { + currentLine = -1; + currentFile = nextJava (); + currentBuffered = new BufferedReader (new FileReader (currentFile)); + } catch (FileNotFoundException e) { + System.err.println ("skip not found file ("+e+")"); + continue; + } + } + try { + String line = currentBuffered.readLine (); + currentLine++; + if (line == null) { + currentBuffered = null; + continue; + } + if (line.indexOf ("\"") >= 0) + nextString = line; + } catch (IOException e) { + System.err.println ("skip premature end of file ("+e+")"); + } + } + } + + // ======================================== + public String nextString () { + if (! hasMoreString ()) + return null; + String result = nextString; + nextString = null; + return result; + } + + // ======================================== +} diff --git a/src/java/tool/model/package-info.java b/src/java/tool/model/package-info.java new file mode 100644 index 0000000..67db557 --- /dev/null +++ b/src/java/tool/model/package-info.java @@ -0,0 +1,9 @@ +/** + * Ordre de lecture des paquetages et des classes
    + *
  • BundleManagerObserver
  • + *
  • BundleManagerModel
  • + *
  • SourceIteratorModel
  • + *
+ * @author F. Merciol + */ +package tool.model; diff --git a/src/java/tool/package-info.java b/src/java/tool/package-info.java new file mode 100644 index 0000000..ba1e708 --- /dev/null +++ b/src/java/tool/package-info.java @@ -0,0 +1,11 @@ +/** + * Ordre de lecture des paquetages et des classes
    + *
  • model
  • + *
  • view
  • + *
  • controller
  • + *
  • LaunchBundleManager
  • + *
+ * @author F. Merciol + */ +package tool; + diff --git a/src/java/tool/view/BundleManagerManager.java b/src/java/tool/view/BundleManagerManager.java new file mode 100644 index 0000000..2ee7f14 --- /dev/null +++ b/src/java/tool/view/BundleManagerManager.java @@ -0,0 +1,286 @@ +package tool.view; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.Locale; +import java.util.Vector; +import java.util.regex.Pattern; +import javax.swing.AbstractButton; +import javax.swing.Box; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.filechooser.FileNameExtensionFilter; + +import misc.ApplicationManager; +import misc.Bundle; +import misc.Config; +import misc.OwnFrame; +import misc.Util; +import tool.model.BundleManagerModel; +import tool.model.BundleManagerObserver; + +@SuppressWarnings ("serial") public class BundleManagerManager + implements ApplicationManager, ActionListener, BundleManagerObserver { + + // ======================================== + BundleManagerModel bundleManagerModel; + + OwnFrame controller; + + BundleManagerView bundleManagerView; + JFileChooser jFileChooser = new JFileChooser (Config.getString ("BundleDir", ".")); + File bundleDir = null; + String bundleName = null; + + public String getBundleName () { return bundleName; } + + // ======================================== + static public final List fileActionsNames = Arrays.asList ("Empty", "Open", "Merge", "Save", "SaveAs"); + static public final List modelActionsNames = Arrays.asList ("AddKey", "RemoveKey", "AddLocale", "RenameLocale", "RemoveLocale"); + @SuppressWarnings ("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (BundleManagerManager.class, Util.merge (fileActionsNames, modelActionsNames)); + + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + + // ======================================== + public void updateBundle () { + jFileChooser.setFileFilter (new FileNameExtensionFilter (Bundle.getLabel ("BundelPropertieFile"), "properties")); + } + + // ======================================== + public BundleManagerManager (OwnFrame controller, BundleManagerModel bundleManagerModel, BundleManagerView bundleManagerView) { + this.controller = controller; + this.bundleManagerModel = bundleManagerModel; + this.bundleManagerView = bundleManagerView; + jFileChooser.setMultiSelectionEnabled (false); + + Bundle.addBundleObserver (this); + bundleManagerModel.addBundleObserver (this); + updateBundle (); + } + + // ======================================== + public void actionEmpty () { + if (bundleManagerModel.getModified () && + JOptionPane.showConfirmDialog (controller.getJFrame (), Bundle.getLabel ("RealyDiscardBundle"), + Bundle.getTitle ("BundleNotSaved"), + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION) + return; + bundleName = null; + bundleManagerModel.empty (); + } + + // ======================================== + private void actionOpen (boolean merge) { + if (bundleManagerModel.getModified () && + JOptionPane.showConfirmDialog (controller.getJFrame (), Bundle.getLabel ("RealyDiscardBundle"), + Bundle.getTitle ("BundleNotSaved"), + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION) + return; + jFileChooser.setFileSelectionMode (JFileChooser.FILES_ONLY); + if (jFileChooser.showOpenDialog (controller.getJFrame ()) != JFileChooser.APPROVE_OPTION) + return; + File file = jFileChooser.getSelectedFile (); + setBundleDir (file.getParentFile ()); + setBundleName (file.getName ()); + bundleManagerModel.open (bundleDir, bundleName, merge); + Config.save ("BundleManager"); + } + + // ======================================== + public void actionOpen () { + actionOpen (false); + } + + // ======================================== + public void actionMerge () { + actionOpen (true); + } + + // ======================================== + private void setBundleName (String fileName) { + if (fileName.endsWith (BundleManagerModel.extention)) + fileName = fileName.substring (0, fileName.length () - BundleManagerModel.extention.length ()); + Pattern pattern = Pattern.compile (".*_[a-zA-Z]{2}"); + if (pattern.matcher (fileName).matches ()) + fileName = fileName.substring (0, fileName.length () - 3); + if (pattern.matcher (fileName).matches ()) + fileName = fileName.substring (0, fileName.length () - 3); + bundleName = fileName; + } + + // ======================================== + public void actionSave () { + if (bundleDir == null || bundleName == null) { + actionSaveAs (); + return; + } + bundleManagerModel.saveAs (bundleDir, bundleName); + Config.save ("BundleManager"); + } + + // ======================================== + public void actionSaveAs () { + jFileChooser.setFileSelectionMode ((bundleName == null) ? + JFileChooser.FILES_ONLY : JFileChooser.FILES_AND_DIRECTORIES); + if (jFileChooser.showSaveDialog (controller.getJFrame ()) != JFileChooser.APPROVE_OPTION) + return; + File file = jFileChooser.getSelectedFile (); + if (file.isDirectory ()) + setBundleDir (file); + else { + setBundleDir (file.getParentFile ()); + setBundleName (file.getName ()); + } + actionSave (); + } + + // ======================================== + public void actionAddKey () { + String newKey = + JOptionPane.showInputDialog (controller.getJFrame (), Bundle.getLabel ("NewKey"), + Bundle.getTitle ("AddKey"), + JOptionPane.INFORMATION_MESSAGE); + if (newKey == null) + return; + bundleManagerModel.addKey (newKey); + } + + // ======================================== + public void actionRemoveKey () { + if (bundleManagerView.bundleJTable.getSelectedRowCount () != 1) { + JOptionPane.showMessageDialog (controller.getJFrame (), Bundle.getMessage ("NoKey"), Bundle.getTitle ("NoKey"), + JOptionPane.WARNING_MESSAGE); + return; + } + int selected = bundleManagerView.bundleJTable.getSelectedRow (); + bundleManagerModel.removeKey (bundleManagerModel.getKeyByIndex (selected)); + } + + // ======================================== + public void actionAddLocale () { + + Locale [] locales = Bundle.getAllLocales (); + JComboBox localesList = Bundle.getJComboFlags (locales); + localesList.setEditable (true); + JPanel jPanel = new JPanel (new BorderLayout ()); + jPanel.add (new JLabel (Bundle.getMessage ("AddLocale")), BorderLayout.PAGE_START); + jPanel.add (localesList, BorderLayout.CENTER); + + if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog (controller.getJFrame (), jPanel, Bundle.getTitle ("AddLocale"), + JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE)) + return; + bundleManagerModel.add ("_"+locales[localesList.getSelectedIndex ()].toString ()); + } + + // ======================================== + public void actionRenameLocale () { + if (bundleManagerView.bundleJTable.getSelectedColumnCount () != 1) { + JOptionPane.showMessageDialog (controller.getJFrame (), Bundle.getMessage ("NoLocale"), + Bundle.getTitle ("NoLocale"), + JOptionPane.WARNING_MESSAGE); + return; + } + String newLocale = + JOptionPane.showInputDialog (controller.getJFrame (), Bundle.getMessage ("RenameLocale"), + Bundle.getTitle ("RenameLocale"), + JOptionPane.INFORMATION_MESSAGE); + if (newLocale == null) + return; + bundleManagerModel.renameLocale (bundleManagerView.bundleColumnNames.get (bundleManagerView.bundleJTable.getSelectedColumn ()), newLocale); + } + + // ======================================== + public void actionRemoveLocale () { + if (bundleManagerView.bundleJTable.getSelectedColumnCount () != 1) { + JOptionPane.showMessageDialog (controller.getJFrame (), Bundle.getMessage ("NoLocale"), + Bundle.getTitle ("NoLocale"), + JOptionPane.WARNING_MESSAGE); + return; + } + bundleManagerModel.removeLocale (bundleManagerView.bundleColumnNames.get (bundleManagerView.bundleJTable.getSelectedColumn ())); + } + + // ======================================== + private void setBundleDir (File dir) { + bundleDir = dir; + try { + Config.setString ("BundleDir", dir.getCanonicalPath ()); + } catch (Exception e) { + } + } + + // ======================================== + private Vector saveCommands = new Vector (); + public void addSaveCommand (AbstractButton saveCommand) { + if (saveCommand == null) + return; + saveCommands.add (saveCommand); + saveCommand.setEnabled (bundleName != null && bundleManagerModel.getModified ()); + } + + // ======================================== + private Vector saveAsCommands = new Vector (); + public void addSaveAsCommand (AbstractButton saveAsCommand) { + if (saveAsCommand == null) + return; + saveAsCommands.add (saveAsCommand); + saveAsCommand.setEnabled (bundleManagerModel.getModified ()); + } + + // ======================================== + public void broadcastSaveUpdate () { + boolean enable = bundleName != null && bundleManagerModel.getModified (); + for (AbstractButton saveCommand : saveCommands) + saveCommand.setEnabled (enable); + } + + // ======================================== + public void broadcastSaveAsUpdate () { + boolean enable = bundleManagerModel.getModified (); + for (AbstractButton saveAsCommand : saveAsCommands) + saveAsCommand.setEnabled (enable); + } + + // ======================================== + public void dataModifiedChange () { + broadcastSaveUpdate (); + broadcastSaveAsUpdate (); + } + public void localesUpdated () {} + public void dataUpdated () {} + + // ======================================== + public void addMenuItem (JMenu... jMenu) { + int idx = 0; + Util.addMenuItem (fileActionsNames, this, jMenu[idx++]); + Util.addMenuItem (modelActionsNames, this, jMenu[idx++]); + } + public void addIconButtons (Container... containers) { + int idx = 0; + Util.addIconButton (fileActionsNames, this, containers[idx++]); + Util.addIconButton (modelActionsNames, this, containers[idx++]); + } + public void addActiveButtons (Hashtable buttons) { + addSaveCommand (buttons.get ("Save")); + addSaveAsCommand (buttons.get ("SaveAs")); + } + + // ======================================== +} diff --git a/src/java/tool/view/BundleManagerMenu.java b/src/java/tool/view/BundleManagerMenu.java new file mode 100644 index 0000000..bd27046 --- /dev/null +++ b/src/java/tool/view/BundleManagerMenu.java @@ -0,0 +1,44 @@ +package tool.view; + +import java.util.Hashtable; +import javax.swing.AbstractButton; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JMenu; +import javax.swing.JMenuBar; + +import misc.ApplicationManager; +import misc.HelpManager; +import misc.Log; +import misc.ToolBarManager; +import misc.Util; + +@SuppressWarnings ("serial") public class BundleManagerMenu extends JMenuBar { + // ======================================== + public BundleManagerMenu (ApplicationManager controllerManager, ApplicationManager bundleManagerManager, + ApplicationManager helpManager, ToolBarManager toolBarManager) { + setLayout (new BoxLayout (this, BoxLayout.X_AXIS)); + + JMenu fileMenu = Util.addJMenu (this, "File"); + JMenu bundleMenu = Util.addJMenu (this, "Bundle"); + add (Box.createHorizontalGlue ()); + JMenu helpMenu = Util.addJMenu (this, "Help"); + + controllerManager.addMenuItem (fileMenu); + bundleManagerManager.addMenuItem (fileMenu, bundleMenu); + helpManager.addMenuItem (helpMenu); + toolBarManager.addMenuItem (helpMenu); + + Hashtable buttons = new Hashtable (); + Util.collectButtons (buttons, fileMenu); + Util.collectButtons (buttons, bundleMenu); + Util.collectButtons (buttons, helpMenu); + + controllerManager.addActiveButtons (buttons); + bundleManagerManager.addActiveButtons (buttons); + helpManager.addActiveButtons (buttons); + toolBarManager.addActiveButtons (buttons); + } + + // ======================================== +} diff --git a/src/java/tool/view/BundleManagerToolBar.java b/src/java/tool/view/BundleManagerToolBar.java new file mode 100644 index 0000000..c567ef7 --- /dev/null +++ b/src/java/tool/view/BundleManagerToolBar.java @@ -0,0 +1,45 @@ +package tool.view; + +import java.awt.Component; +import java.util.Hashtable; +import javax.swing.AbstractButton; +import javax.swing.Box; +import javax.swing.JComponent; +import javax.swing.JToolBar; + +import misc.ApplicationManager; +import misc.Bundle; +import misc.HelpManager; +import misc.Log; +import misc.ToolBarManager; +import misc.Util; + +public class BundleManagerToolBar { + + // ======================================== + static public String defaultCardinalPoint = "North"; + + public BundleManagerToolBar (ApplicationManager controllerManager, BundleManagerManager bundleManagerManager, + HelpManager helpManager, ToolBarManager toolBarManager) { + JToolBar fileToolBar = toolBarManager.newJToolBar ("File", defaultCardinalPoint); + JToolBar bundleToolBar = toolBarManager.newJToolBar ("Bundle", defaultCardinalPoint); + JToolBar helpToolBar = toolBarManager.newJToolBar ("Help", defaultCardinalPoint); + + controllerManager.addIconButtons (fileToolBar); + bundleManagerManager.addIconButtons (fileToolBar, bundleToolBar); + helpManager.addIconButtons (helpToolBar); + toolBarManager.addIconButtons (helpToolBar); + + Hashtable buttons = new Hashtable (); + Util.collectButtons (buttons, fileToolBar); + Util.collectButtons (buttons, bundleToolBar); + Util.collectButtons (buttons, helpToolBar); + + controllerManager.addActiveButtons (buttons); + bundleManagerManager.addActiveButtons (buttons); + helpManager.addActiveButtons (buttons); + toolBarManager.addActiveButtons (buttons); + } + + // ======================================== +} diff --git a/src/java/tool/view/BundleManagerView.java b/src/java/tool/view/BundleManagerView.java new file mode 100644 index 0000000..f0b35b2 --- /dev/null +++ b/src/java/tool/view/BundleManagerView.java @@ -0,0 +1,108 @@ +package tool.view; + +import java.awt.BorderLayout; +import java.util.Vector; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.table.DefaultTableModel; + +import misc.Bundle; +import tool.model.BundleManagerModel; +import tool.model.BundleManagerObserver; + +@SuppressWarnings ("serial") public class BundleManagerView extends JPanel implements BundleManagerObserver { + + // ======================================== + BundleManagerModel bundleManagerModel; + + // ======================================== + Vector bundleColumnNames; + DefaultTableModel bundleTableModel; + JTable bundleJTable; + + // ======================================== + public void updateBundle () { + localesUpdated (); + } + + // ======================================== + public BundleManagerView (final BundleManagerModel bundleManagerModel) { + super (new BorderLayout ()); + this.bundleManagerModel = bundleManagerModel; + bundleJTable = new JTable (); + bundleJTable.getColumnModel ().setColumnSelectionAllowed (true); + bundleJTable.setSelectionMode (ListSelectionModel.SINGLE_SELECTION); + JScrollPane scrollBundle = new JScrollPane (bundleJTable); + bundleJTable.setFillsViewportHeight (true); + add (scrollBundle, BorderLayout.CENTER); + bundleManagerModel.addBundleObserver (this); + updateBundle (); + Bundle.addBundleObserver (this); + } + + // ======================================== + public void dataModifiedChange () {} + + // ======================================== + public void localesUpdated () { + bundleColumnNames = new Vector (); + bundleColumnNames.add (Bundle.getLabel ("Keys")); + bundleColumnNames.addAll (bundleManagerModel.getLocales ()); + bundleTableModel = new DefaultTableModel (bundleColumnNames, 0) { + + private static final long serialVersionUID = 1L; + + public String getColumnName(int col) { + return bundleColumnNames.get (col); + } + + public int getColumnCount () { + return bundleColumnNames.size (); + } + + public Object getValueAt (int row, int col) { + String key = bundleManagerModel.getKeyByIndex (row); + if (col == 0) + return key; + String locale = bundleManagerModel.getLocaleByIndex (col-1); + return bundleManagerModel.get (locale, key); + } + + public void setValueAt (Object value, int row, int col) { + String key = bundleManagerModel.getKeyByIndex (row); + if (col == 0) { + String newKey = (String) value; + if (newKey.equals (key)) + return; + bundleManagerModel.renameKey (key, newKey); + return; + } + String locale = bundleManagerModel.getLocaleByIndex (col-1); + bundleManagerModel.set (locale, key, (String) value); + //fireTableCellUpdated (row, col); + } + public boolean isCellEditable (int row, int column) { + return true; + } + }; + bundleJTable.setModel (bundleTableModel); + dataUpdated (); + } + + // ======================================== + public void dataUpdated () { + bundleTableModel.setRowCount (0); + for (String key : bundleManagerModel.getKeys ()) { + Vector row = new Vector (); + row.add (key); + for (String locale : bundleManagerModel.getLocales ()) + row.add (bundleManagerModel.get (locale, key)); + bundleTableModel.addRow (row); + } + bundleTableModel.fireTableDataChanged (); + } + + // ======================================== +} diff --git a/src/java/tool/view/SourceIteratorView.java b/src/java/tool/view/SourceIteratorView.java new file mode 100644 index 0000000..b8a9250 --- /dev/null +++ b/src/java/tool/view/SourceIteratorView.java @@ -0,0 +1,127 @@ +package tool.view; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; + +import misc.Bundle; +import misc.Config; +import misc.Util; +import tool.model.SourceIteratorModel; + +@SuppressWarnings ("serial") public class SourceIteratorView extends JPanel implements ActionListener { + + // ======================================== + SourceIteratorModel sourceIteratorModel = null; + + // ======================================== + String[] columnLabels = { "FileName", "LineNumber", "Line" }; + DefaultTableModel sourceIteratorTableModel; + JTable sourceIteratorJTable; + + // ======================================== + static public final List actionsNames = Arrays.asList ("Init", "Next"); + @SuppressWarnings ("unchecked") + static public final Hashtable actionsMethod = + Util.collectMethod (SourceIteratorView.class, actionsNames); + + public void actionPerformed (ActionEvent e) { + Util.actionPerformed (actionsMethod, e, this); + } + + // ======================================== + public void updateBundle () { + Util.setColumnLabels (sourceIteratorJTable, columnLabels); + } + + // ======================================== + DefaultTableCellRenderer greenRendered = new DefaultTableCellRenderer (); + DefaultTableCellRenderer whiteRendered = new DefaultTableCellRenderer (); + + // ======================================== + public SourceIteratorView () { + super (new BorderLayout ()); + + greenRendered.setBackground (Color.GREEN); + whiteRendered.setBackground (Color.WHITE); + + int [] columnWidth = { + 200, + 5, + 1000 + }; + sourceIteratorTableModel = new DefaultTableModel (columnLabels, 1); + sourceIteratorJTable = new JTable (sourceIteratorTableModel); + updateBundle (); + for (int i = 0; i < sourceIteratorTableModel.getColumnCount (); i++) + sourceIteratorJTable.getColumnModel ().getColumn (i).setPreferredWidth (columnWidth [i]); + sourceIteratorJTable.getColumnModel ().setColumnSelectionAllowed (true); + sourceIteratorJTable.setSelectionMode (ListSelectionModel.SINGLE_SELECTION); + add (sourceIteratorJTable.getTableHeader (), BorderLayout.PAGE_START); + add (sourceIteratorJTable, BorderLayout.CENTER); + + JPanel buttonPanel = new JPanel (); + Util.addButton (actionsNames, this, buttonPanel); + add (buttonPanel, BorderLayout.PAGE_END); + + Bundle.addBundleObserver (this); + } + + // ======================================== + JFileChooser jFileChooser = new JFileChooser (Config.getString ("SourceIteratorDir", ".")); + + // ======================================== + public void actionInit () { + jFileChooser.setFileSelectionMode (JFileChooser.DIRECTORIES_ONLY); + if (jFileChooser.showOpenDialog (this) != JFileChooser.APPROVE_OPTION) + return; + File dir = jFileChooser.getSelectedFile (); + sourceIteratorModel = new SourceIteratorModel (dir); + actionNext (); + try { + Config.setString ("SourceIteratorDir", dir.getCanonicalPath ()); + } catch (Exception e) { + } + } + + + // ======================================== + public void actionNext () { + if (sourceIteratorModel == null) { + JOptionPane.showMessageDialog (this, Bundle.getMessage ("NoSourceDir"), Bundle.getTitle ("NoSource"), + JOptionPane.WARNING_MESSAGE); + return; + } + if (! sourceIteratorModel.hasMoreString ()) { + sourceIteratorTableModel.setRowCount (0); + sourceIteratorTableModel.addRow (new String [] {"", "", ""}); + sourceIteratorModel = null; + return; + } + String next = sourceIteratorModel.nextString (); + sourceIteratorTableModel.setRowCount (0); + sourceIteratorTableModel.addRow (new String [] { + ""+sourceIteratorModel.getCurrentFile (), + ""+sourceIteratorModel.getCurrentLine (), + next + }); + + sourceIteratorJTable.getColumnModel ().getColumn (2).setCellRenderer + ((next.indexOf ("Bundle.") >= 0 || next.indexOf ("Config.") >= 0 ) ? greenRendered : whiteRendered); + } + + // ======================================== +} diff --git a/src/javaTest/misc/BundleTest.java b/src/javaTest/misc/BundleTest.java new file mode 100644 index 0000000..0687d8c --- /dev/null +++ b/src/javaTest/misc/BundleTest.java @@ -0,0 +1,80 @@ +package misc; + +import java.util.Locale; +import javax.swing.JButton; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.JUnitCore; + +public class BundleTest extends Assert { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (BundleTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("misc.BundleTest"); + } + + // ======================================== + static public int countObserver; + static public int countActioner; + + @BeforeClass static public void setBundleObserver () { + Bundle.addBundleObserver (new Object () { + public void updateBundle () { + countObserver++; + } + }); + Bundle.load ("BundleManager", Locale.US); + String action = "Quit"; + JButton button = new JButton (Bundle.getAction (action)) { + private static final long serialVersionUID = 1L; + + public void setText (String text) { + super.setText (text); + countActioner++; + } + }; + Bundle.addLocalizedActioner (button); + button.setActionCommand (action); + } + + // ======================================== + @Test public void setLocale () { + Bundle.load ("BundleManager", Locale.US); + int oldValue = countObserver; + Bundle.setLocale (Locale.FRANCE); + assertTrue (oldValue+1 == countObserver); + assertTrue (countActioner == countObserver); + } + + @Test public void load () { + int oldValue = countObserver; + Bundle.load ("BundleManager"); + assertTrue (oldValue+1 == countObserver); + assertTrue (countActioner == countObserver); + } + + @Test public void getLocales () { + Locale[] locales = Bundle.getApplicationsLocales (); + assertTrue (locales.length > 0); + Bundle.load ("BundleManager"); + for (Locale locale : locales) + Bundle.setLocale (locale); + } + + @Test public void getString () { + Bundle.load ("BundleManager", Locale.US); + String s1 = Bundle.getTitle ("BundleEditor"); + Bundle.setLocale (Locale.FRANCE); + String s2 = Bundle.getTitle ("BundleEditor"); + assertNotNull (s1); + assertNotNull (s2); + assertFalse (s1.equals (s2)); + } + + // ======================================== +} diff --git a/src/javaTest/misc/ColorCheckedLineTest.java b/src/javaTest/misc/ColorCheckedLineTest.java new file mode 100644 index 0000000..e0d397e --- /dev/null +++ b/src/javaTest/misc/ColorCheckedLineTest.java @@ -0,0 +1,46 @@ +package misc; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.JUnitCore; + +public class ColorCheckedLineTest extends Assert { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (ColorCheckedLineTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("misc.ColorCheckedLineTest"); + } + + // ======================================== + @Test public void ok () { + try { + System.err.print ("Test ok : "); + + // something right + + System.err.println (ColorCheckedLine.MSG_OK); + } catch (Exception e) { + System.err.println (ColorCheckedLine.MSG_OK); + } + } + + // ======================================== + @Test public void ko () { + try { + System.err.print ("Test ko : "); + + // something wrong + int i = 0; + i = 1/i; + + System.err.println (ColorCheckedLine.MSG_OK); + } catch (Exception e) { + System.err.println (ColorCheckedLine.MSG_KO); + } + } + + // ======================================== +} diff --git a/src/javaTest/misc/ConfigTest.java b/src/javaTest/misc/ConfigTest.java new file mode 100644 index 0000000..620490f --- /dev/null +++ b/src/javaTest/misc/ConfigTest.java @@ -0,0 +1,153 @@ +package misc; + +import java.awt.Container; +import java.awt.Point; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.PrintWriter; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.JUnitCore; + +public class ConfigTest extends Assert { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (ConfigTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("misc.ConfigTest"); + } + + // ======================================== + static public final String testApplicationName = "Test"; + static public final String testConfigFileName = + Config.configSystemDir+System.getProperty ("file.separator")+testApplicationName+Config.configExt; + static public final String result = "result"; + static public final String booleanTrue = "booleanTrue"; + static public final String example = "example"; + static public final String value = "value"; + + static public final String configContent = + "\n"+ + "\n"+ + "\n"+ + " [x=100,y=200]\n"+ + " "+value+"\n"+ + " "+true+"\n"+ + "\n"; + + public int getLines (String fileName) + throws FileNotFoundException, IOException { + long fileSize = (new File (fileName)).length (); + LineNumberReader lineNumberReader = new LineNumberReader (new FileReader (fileName)); + lineNumberReader.skip (fileSize); + return lineNumberReader.getLineNumber (); + } + + @BeforeClass static public void createTestConfig () + throws FileNotFoundException { + PrintWriter configFile = new PrintWriter (testConfigFileName); + configFile.write (configContent); + configFile.close (); + } + + @AfterClass static public void removeTestConfig () { + (new File (testConfigFileName)).delete (); + } + + + // ======================================== + @Test public void load () { + boolean findFile = false; + for (String fileName : + (new File (Config.configSystemDir)).list (new FilenameFilter () { + public boolean accept (File dir, String name) { + return name.endsWith (Config.configExt); + } + })) { + findFile = true; + Config.setPWD (ConfigTest.class); + Config.load (fileName.substring (0, fileName.length () - Config.configExt.length ())); + assertTrue (Config.isLoaded ()); + } + assertTrue (findFile); + } + + @Test public void getString () { + Config.setPWD (ConfigTest.class); + Config.load (testApplicationName); + assertEquals (result, Config.getString (null, result)); + assertEquals (value, Config.getString (example)); + } + + @Test (expected=IllegalArgumentException.class) public void getStringNullKey () { + Config.getString (null); + } + + @Test public void getBoolean () { + Config.setPWD (ConfigTest.class); + Config.load (testApplicationName); + assertTrue (Config.getBoolean (booleanTrue, false)); + assertFalse (Config.getBoolean (example, false)); + } + + @Test (expected=IllegalArgumentException.class) public void getBooleanNullKey () { + Config.getBoolean (null); + } + + @Test public void save () + throws FileNotFoundException, IOException { + int lines = getLines (testConfigFileName); + Config.setPWD (ConfigTest.class); + Config.load (testApplicationName); + Config.setString ("a", "b"); + Config.save (testApplicationName); + assertTrue (lines < getLines (testConfigFileName)); + Config.load (testApplicationName); + assertEquals ("b", Config.getString ("a")); + } + + @Test public void setString () { + Config.setPWD (ConfigTest.class); + Config.load (testApplicationName); + Config.setString ("a", "b"); + assertEquals ("b", Config.getString ("a")); + } + + @Test public void setBoolean () { + Config.setPWD (ConfigTest.class); + Config.load (testApplicationName); + Config.setBoolean ("bool", true); + assertTrue (Config.getBoolean ("bool")); + Config.setBoolean ("bool", false); + assertFalse (Config.getBoolean ("bool")); + } + + @Test public void saveLocation () { + Config.setPWD (ConfigTest.class); + Config.load (testApplicationName); + assertNull (Config.getString ("test"+Config.locationPostfix, null)); + Container container = new Container (); + container.setLocation (10, 20); + Config.saveLocation ("test", container); + assertEquals ("[x=10,y=20]", Config.getString ("test"+Config.locationPostfix)); + } + + @Test public void loadLocation () { + Config.setPWD (ConfigTest.class); + Config.load (testApplicationName); + Container container = new Container (); + Config.loadLocation ("Frame", container, new Point (0, 0)); + assertEquals (new Point (100, 200), container.getLocation ()); + } + + // ======================================== +} diff --git a/src/javaTest/misc/LocaleTest.java b/src/javaTest/misc/LocaleTest.java new file mode 100644 index 0000000..6d5f3ce --- /dev/null +++ b/src/javaTest/misc/LocaleTest.java @@ -0,0 +1,39 @@ +package misc; + +import java.util.ResourceBundle; +import java.util.Locale; +import java.io.FileOutputStream; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.JUnitCore; + +public class LocaleTest extends Assert { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (LocaleTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("misc.LocaleTest"); + } + + // ======================================== + @Test public void launch () { + Locale currentLocale = Locale.getDefault (); + //Locale currentLocale = new Locale ("fr", "FR"); + ResourceBundle messages = ResourceBundle.getBundle ("Misc", currentLocale, Bundle.bundleControl); + assertNotNull (messages.getString ("ActionQuit")); + } + + // ======================================== + @Test public void save () + throws java.io.IOException { + + String arg = "/tmp/RemDumpProperties"; + Bundle.load ("Misc"); + System.getProperties ().storeToXML (new FileOutputStream (arg), Bundle.getString ("RemDumpProperties", null)); + } + + // ======================================== +} diff --git a/src/javaTest/misc/TitledDialogTest.java b/src/javaTest/misc/TitledDialogTest.java new file mode 100644 index 0000000..c45b737 --- /dev/null +++ b/src/javaTest/misc/TitledDialogTest.java @@ -0,0 +1,63 @@ +package misc; + +import java.awt.Frame; +import java.util.Locale; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.JUnitCore; + +public class TitledDialogTest extends Assert { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (TitledDialogTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("misc.TitledDialogTest"); + } + + // ======================================== + static public TitledDialog titledDialog; + + @BeforeClass static public void setBundleObserver () { + Bundle.load ("BundleManager"); + titledDialog = new TitledDialog (new Frame (), "BundleEditor"); + } + + // ======================================== + @Test public void bundleReloaded () { + Bundle.load ("BundleManager", Locale.US); + String s1 = titledDialog.getTitle (); + Bundle.setLocale (Locale.FRANCE); + String s2 = titledDialog.getTitle (); + assertNotNull (s1); + assertNotNull (s2); + assertFalse (s1.equals (s2)); + } + + @Test public void setVisible () { + Config.setPWD (TitledDialogTest.class); + Config.load ("BundleManager"); + titledDialog.setVisible (true); + assertTrue (titledDialog.isVisible ()); + titledDialog.setVisible (false); + assertFalse (titledDialog.isVisible ()); + } + + @Test public void saveLocation () { + Config.setPWD (TitledDialogTest.class); + Config.load ("BundleManager"); + assertNull (Config.getString ("BundleEditor"+Config.locationPostfix, null)); + titledDialog.setVisible (true); + titledDialog.setVisible (false); + titledDialog.saveLocation (); + Object [] location = Config.coordonateFormat.parse (Config.getString ("BundleEditor"+Config.locationPostfix, null), + new java.text.ParsePosition (0)); + assertTrue (((Number) location [0]).intValue () > 0); + assertTrue (((Number) location [1]).intValue () > 0); + } + + // ======================================== +} diff --git a/src/javaTest/misc/UtilTest.java b/src/javaTest/misc/UtilTest.java new file mode 100644 index 0000000..b0b8056 --- /dev/null +++ b/src/javaTest/misc/UtilTest.java @@ -0,0 +1,147 @@ +package misc; + +import java.awt.Component; +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Arrays; +import java.util.List; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JMenuItem; +import javax.swing.JRadioButton; +import javax.swing.JMenuBar; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.JUnitCore; + +public class UtilTest extends Assert { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (UtilTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("misc.UtilTest"); + } + + // ======================================== + static public final void isCapital (String s) { + assertTrue (Character.isUpperCase (s.charAt(0))); + for (int i = 1; i < s.length (); i++) + assertTrue (Character.isLowerCase (s.charAt(i))); + } + + static public final List actions = Arrays.asList ("Open", "Save", "Quit"); + static public final ActionListener actionListener = new ActionListener () { + public void actionPerformed (ActionEvent e) { + } + }; + + @BeforeClass static public void createTestConfig () { + Bundle.load ("BundleManager"); + } + + // ======================================== + @Test public void addRadioButton () { + Container container = new Container (); + ButtonGroup group = new ButtonGroup (); + Util.addRadioButton (actions, actionListener, group, "Quit", container); + assertTrue (actions.contains (group.getSelection ().getActionCommand ())); + assertTrue (container.getComponentCount () == actions.size ()); + for (Component component : container.getComponents ()) { + JRadioButton button = (JRadioButton) component; + assertTrue (actions.contains (button.getActionCommand ())); + Arrays.asList (button.getActionListeners ()).contains (actionListener); + if ("Quit".equals (button.getActionCommand ())) { + assertEquals (group.getSelection ().getActionCommand (), button.getActionCommand ()); + assertTrue (button.isSelected ()); + } else + assertFalse (button.isSelected ()); + } + } + + @Test public void addButton () { + Container container = new Container (); + Util.addButton (actions, actionListener, container); + assertTrue (container.getComponentCount () == actions.size ()); + for (Component component : container.getComponents ()) { + JButton button = (JButton) component; + assertTrue (actions.contains (button.getActionCommand ())); + Arrays.asList (button.getActionListeners ()).contains (actionListener); + } + } + + @Test public void addIconButton () { + Container container = new Container (); + Util.addIconButton (actions, actionListener, container); + assertTrue (container.getComponentCount () == actions.size ()); + for (Component component : container.getComponents ()) { + JButton button = (JButton) component; + assertNotNull (button.getIcon ()); + assertTrue (actions.contains (button.getActionCommand ())); + Arrays.asList (button.getActionListeners ()).contains (actionListener); + } + } + + @Test public void addMenuItem () { + Container container = new Container (); + Util.addMenuItem (actions, actionListener, container); + assertTrue (container.getComponentCount () == actions.size ()); + for (Component component : container.getComponents ()) { + JMenuItem item = (JMenuItem) component; + assertTrue (actions.contains (item.getActionCommand ())); + Arrays.asList (item.getActionListeners ()).contains (actionListener); + } + } + + @Test public void addCheckMenuItem () { + Container container = new Container (); + Util.addCheckMenuItem (actions, actionListener, container); + assertTrue (container.getComponentCount () == actions.size ()); + for (Component component : container.getComponents ()) { + JMenuItem item = (JMenuItem) component; + assertTrue (actions.contains (item.getActionCommand ())); + Arrays.asList (item.getActionListeners ()).contains (actionListener); + } + } + + @Test public void addJMenu () { + JMenuBar jMenuBar = new JMenuBar (); + Util.addJMenu (jMenuBar, "Help"); + } + + @Test public void setColumnLabels () { + // YYY + } + + + @Test public void packWindow () {} + @Test public void newJFrame () {} + @Test public void WindowAdapter () {} + @Test public void windowClosing () {} + + @Test public void loadActionIcon () {} + @Test public void loadImageIcon () {} + @Test public void getDataUrl () {} + + @Test public void collectMethod () {} + @Test public void collectButtons () {} + @Test public void actionPerformed () {} + @Test public void merge () {} + + @Test public void toCapital () { + for (String s : Arrays.asList ("abcd", "AbCd", "aBcD", "ABCD")) + isCapital (Util.toCapital (s)); + } + + @Test (timeout=1500) public void sleep () { + long before = System.currentTimeMillis (); + Util.sleep (1); + long after = System.currentTimeMillis (); + long delta = after-before; + assertTrue (delta > 900 && delta < 1100); + } +} diff --git a/src/javaTest/misc/XMLTest.java b/src/javaTest/misc/XMLTest.java new file mode 100644 index 0000000..362ae67 --- /dev/null +++ b/src/javaTest/misc/XMLTest.java @@ -0,0 +1,70 @@ +package misc; + +import java.io.StringBufferInputStream; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.junit.Test; +import org.junit.Ignore; +import org.junit.runner.JUnitCore; + +import misc.XML; + +@SuppressWarnings("deprecation") + public class XMLTest { + + @Ignore ("for exemple") @Test (expected=IndexOutOfBoundsException.class, timeout=100) public void empty () { + new java.util.ArrayList ().get (0); + } + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (XMLTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("misc.XMLTest"); + } + + // ======================================== + + // XXX lecture de chaine, exriture dans une chaine différence + + String badXmlString = "Hello Word"; + String xmlString = "\n"+ + "\n"+ + " \n"+ + "\n"; + + // ======================================== + @Test (expected=java.io.IOException.class) public void badRead () + throws javax.xml.parsers.ParserConfigurationException, org.xml.sax.SAXException, java.io.IOException { + XML.readDocument (new StringBufferInputStream (badXmlString)); + } + + // ======================================== + @Test public void read () + throws javax.xml.parsers.ParserConfigurationException, org.xml.sax.SAXException, java.io.IOException { + XML.readDocument (new StringBufferInputStream (xmlString)); + } + + // ======================================== + @Test public void write () + throws javax.xml.parsers.ParserConfigurationException { + DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance ().newDocumentBuilder (); + Document doc = documentBuilder.newDocument (); + doc.setXmlStandalone (true); + Element elem = doc.createElement ("noeud1"); + doc.appendChild (elem); + elem.setAttribute ("attr1", "val 1"); + elem.appendChild (doc.createElement ("noeud2")).appendChild (doc.createElement ("noeud3")); + elem.appendChild (doc.createElement ("noeud4")); + Element elem3 = doc.createElement ("noeud5"); + elem.appendChild (elem3); + elem3.setAttribute ("attr5", "val 5&"); + XML.writeDocument (doc, System.out); + } + + // ======================================== + } diff --git a/src/javaTest/network/HTTPInputStreamTest.java b/src/javaTest/network/HTTPInputStreamTest.java new file mode 100644 index 0000000..124241f --- /dev/null +++ b/src/javaTest/network/HTTPInputStreamTest.java @@ -0,0 +1,89 @@ +package network; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.net.URLConnection; + +import org.junit.Test; +import org.junit.runner.JUnitCore; + +import misc.XML; + +public class HTTPInputStreamTest { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (HTTPInputStreamTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("network.HTTPInputStreamTest"); + } + + static { + Protocol.setIpV4 (); + } + + // ======================================== + @Test (timeout=3000) public void launch () + throws IOException { + + String xmlRequest = + "\n"+ + "\n"+ + " \n"+ + "\n"; + + final String xmlResponse = + "\n"+ + "\n"+ + " \n"+ + "\n"; + + System.err.println ("start test"); + System.err.flush (); + final ServerSocket serverSocket = new ServerSocket (8080); + serverSocket.setReuseAddress (true); + (new Thread () { + public void run () { + try { + System.err.println ("run"); + System.err.flush (); + Socket call = serverSocket.accept (); + System.err.println ("Accept call "+call); + HTTPInputStream httpInputStream = new HTTPInputStream (call.getInputStream ()); + System.err.println ("Server:main headers "+httpInputStream.headers+ + " available:"+httpInputStream.available ()); + System.err.println ("Server:main server receiveXML : "); + XML.writeDocument (XML.readDocument (httpInputStream), System.err); + System.err.flush (); + + HTTPOutputStream httpPOutputStream = new HTTPOutputStream (call.getOutputStream ()); + httpPOutputStream.setHeader ("Content-type", "application/xml"); + httpPOutputStream.write (xmlResponse.getBytes ()); + httpPOutputStream.close (); + } catch (IOException e) { + e.printStackTrace (); + } + } + }).start (); + + System.err.println ("url"); + System.err.flush (); + URLConnection urlConnection = XMLConnection.getXMLConnection (new URL ("http://localhost:8080/")); + OutputStreamWriter out = new OutputStreamWriter (urlConnection.getOutputStream ()); + out.write (xmlRequest); + out.close (); + + // attention l'ouverture de inputstream provoque la fermeture de outputstream + InputStream in = urlConnection.getInputStream (); + System.err.println ("Server:main client receiveXML : "); + XML.writeDocument (XML.readDocument (in), System.err); + System.err.flush (); + } + + // ======================================== +} diff --git a/src/javaTest/network/HTTPOutputStreamTest.java b/src/javaTest/network/HTTPOutputStreamTest.java new file mode 100644 index 0000000..bd03b01 --- /dev/null +++ b/src/javaTest/network/HTTPOutputStreamTest.java @@ -0,0 +1,52 @@ +package network; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.JUnitCore; + +public class HTTPOutputStreamTest extends Assert { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (HTTPOutputStreamTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("network.HTTPOutputStreamTest"); + } + + static { + Protocol.setIpV4 (); + } + + // ======================================== + @Test (timeout=3000) public void launch () + throws IOException { + final java.net.ServerSocket serverSocket = new java.net.ServerSocket (8080); + serverSocket.setReuseAddress (true); + (new Thread () { + public void run () { + try { + java.net.Socket call = serverSocket.accept (); + HTTPOutputStream out = new HTTPOutputStream (call.getOutputStream ()); + out.setHeader ("Content-type", "application/xml"); + out.write ("coucou\n".getBytes ()); + out.close (); + } catch (Exception e) { + fail ("Server side exception:"+e); + } + } + }).start (); + + java.net.Socket client = new java.net.Socket ("localhost", 8080); + java.io.InputStream in = client.getInputStream (); + + byte[] b = new byte [8196]; + int nb = in.read (b); + System.err.println ("Client:in:"+new String (b, 0, nb)); + System.err.flush (); + } + + // ======================================== +} diff --git a/src/javaTest/network/ServerTest.java b/src/javaTest/network/ServerTest.java new file mode 100644 index 0000000..7ae8901 --- /dev/null +++ b/src/javaTest/network/ServerTest.java @@ -0,0 +1,41 @@ +package network; + +import org.junit.Test; +import org.junit.runner.JUnitCore; + +import misc.Util; + +public class ServerTest { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (ServerTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("network.ServerTest"); + } + + // ======================================== + @Test public void launch () { + Server server = new Server (8080); + Client client = new Client ("127.0.0.1", 8080); + + server.start (); + + server.send ("service avant ecoute client"); + + Util.sleep (2); + + client.start (); + + Util.sleep (2); + + server.send ("service apres ecoute client"); + + Util.sleep (2); + + client.send ("service immediat"); + } + + // ======================================== +} diff --git a/src/javaTest/network/chat/ChatTest.java b/src/javaTest/network/chat/ChatTest.java new file mode 100644 index 0000000..779d73d --- /dev/null +++ b/src/javaTest/network/chat/ChatTest.java @@ -0,0 +1,48 @@ +package network.chat; + +import org.junit.Test; +import org.junit.runner.JUnitCore; + +import network.Client; +import network.Server; + +public class ChatTest { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (ChatTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("network.chat.ChatTest"); + } + + // ======================================== + @Test public void launch () { + Server server = new Server (8080); + Client client = new Client ("127.0.0.1", 8080); + + Chat chatServer = new Chat ("chatServer", server); + Chat chatClient = new Chat ("chatClient", client); + + ChatObserver chatObserver = new ChatObserver () { + public void talk (ChatQuote quote) { + System.err.println (Chat.dateFormat.format (quote.date)+" "+quote.speaker+" > "+quote.sentence); + } + public void renameSpeaker (String speaker) {} + public void chatModifiedChange (boolean modified) {} + public void clearChat () {} + }; + chatServer.addChatObserver (chatObserver); + chatClient.addChatObserver (chatObserver); + + server.start (); + misc.Util.sleep (1); + client.start (); + misc.Util.sleep (1); + chatServer.say ("hello"); + misc.Util.sleep (1); + chatClient.say ("world"); + } + + // ======================================== +} diff --git a/src/javaTest/network/chat/JChatTest.java b/src/javaTest/network/chat/JChatTest.java new file mode 100644 index 0000000..1c1d41a --- /dev/null +++ b/src/javaTest/network/chat/JChatTest.java @@ -0,0 +1,68 @@ +package network.chat; + +import java.awt.Image; +import org.junit.Test; +import org.junit.runner.JUnitCore; + +import misc.Bundle; +import misc.Config; +import misc.Controller; +import misc.Util; + +import network.Client; +import network.ProtocolManager; +import network.Server; +import network.login.Login; +import network.login.LoginManager; + +public class JChatTest { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (JChatTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("network.chat.JChatTest"); + } + + public class Controller2 extends Controller { + public Controller2 () { super (""); } + protected void createModel (String s) {} + public String getTitle () { return null; } + public Image getIcon () { return null; } + }; + + // ======================================== + @Test public void launch () { + Config.setPWD (JChatTest.class); + Config.load ("Chat"); + Bundle.load ("Chat"); + + Controller controller = new Controller2 (); + + ProtocolManager serverProtocolManager = new ProtocolManager (controller); + ProtocolManager clientProtocolManager = new ProtocolManager (controller); + + Server server = new Server (8080); + Client client = new Client ("localhost", 8080); + + server.start (); + client.start (); + + serverProtocolManager.setProtocol (server); + clientProtocolManager.setProtocol (client); + + Login login1 = new Login (""); + Login login2 = new Login (""); + LoginManager loginManager1 = new LoginManager (controller, login1); + LoginManager loginManager2 = new LoginManager (controller, login2); + + + Util.newJFrame ("Serge", new JChat (serverProtocolManager, loginManager1, + new ChatManager (controller, new Chat ("Serge", server))), true); + Util.newJFrame ("Clement", new JChat (clientProtocolManager, loginManager2, + new ChatManager (controller, new Chat ("Clement", client))), true); + } + + // ======================================== +} diff --git a/src/javaTest/tool/SourceIteratorModelTest.java b/src/javaTest/tool/SourceIteratorModelTest.java new file mode 100644 index 0000000..b6075b2 --- /dev/null +++ b/src/javaTest/tool/SourceIteratorModelTest.java @@ -0,0 +1,38 @@ +package tool; + +import java.io.File; +import java.util.Vector; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.JUnitCore; + +import tool.model.SourceIteratorModel; + +public class SourceIteratorModelTest extends Assert { + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (SourceIteratorModelTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("tool.SourceIteratorModelTest"); + } + + // ======================================== + @Test public void parse () { + Vector files = new Vector (); + for (SourceIteratorModel si = new SourceIteratorModel (new File ("../src")); si.hasMoreJava (); ) { + File file = si.nextJava (); + files.add (file); + assertTrue (file.isFile ()); + } + for (SourceIteratorModel si = new SourceIteratorModel (new File ("../src")); si.hasMoreString (); ) { + String next = si.nextString (); + assertTrue (files.contains (si.getCurrentFile ())); + assertTrue (next.indexOf ("\"") >= 0); + } + } + + // ======================================== +} diff --git a/src/javaTest/tool/model/BundleManagerModelTest.java b/src/javaTest/tool/model/BundleManagerModelTest.java new file mode 100644 index 0000000..0f77bfe --- /dev/null +++ b/src/javaTest/tool/model/BundleManagerModelTest.java @@ -0,0 +1,34 @@ +package tool.model; + +import java.io.File; + +import org.junit.Test; +import org.junit.Ignore; +import org.junit.runner.JUnitCore; + +import misc.Bundle; + +public class BundleManagerModelTest { + + @Ignore ("for exemple") @Test (expected=IndexOutOfBoundsException.class, timeout=100) public void empty () { + new java.util.ArrayList ().get (0); + } + + static public junit.framework.Test suite () { + return new junit.framework.JUnit4TestAdapter (BundleManagerModelTest.class); + } + + static public void main (String args[]) { + JUnitCore.main ("tool.BundleManagerModelTest"); + } + + // ======================================== + @Test public void copy () { + Bundle.load ("BundleManager"); + BundleManagerModel bmm = new BundleManagerModel (); + bmm.open (new File ("data/config"), "BundleManager", false); + bmm.saveAs (new File ("/tmp"), "BundleManager"); + } + + // ======================================== +} diff --git a/ws/bundle.sh b/ws/bundle.sh new file mode 100755 index 0000000..038314c --- /dev/null +++ b/ws/bundle.sh @@ -0,0 +1,2 @@ +cd `dirname "$0"` +ant -f ../ant/build.xml runBundleJar diff --git a/ws/bundleInclusion.sh b/ws/bundleInclusion.sh new file mode 100755 index 0000000..05e0b68 --- /dev/null +++ b/ws/bundleInclusion.sh @@ -0,0 +1,49 @@ +usage () { + echo `basename "$0"` " [-h] [-help] bundleRefDir bundleDir" + echo " -h" + echo " -help Display this help." +} + +case "$1" in + '-' ) + shift;; + '-v' ) + SVN_OPT="" + shift;; + '-h' | '-help' ) + usage + shift + exit;; +esac + +curDir=`pwd` +cd $1 +refDir=`pwd` +# cd ${curDir} +# cd $2 +# bundleDir=`pwd` + +# cd "${refDir}" +for refFile in *.properties +do + if test -f "${refFile}" -a ! -L "${refFile}" + then + cd "${refDir}" + grep -v '^#' ${refFile} | cut -d = -f 1 | sort -u | { + while read key + do + #cd "${bundleDir}" + for bundleFile in $2 + do + if test -f "${bundleFile}" -a ! -L "${bundleFile}" + then + if grep -q "^${key}=" "${bundleFile}" + then + echo "${bundleFile}: ${key} already in ${refFile}" + fi + fi + done + done + } + fi +done diff --git a/ws/chat.sh b/ws/chat.sh new file mode 100755 index 0000000..f498c2c --- /dev/null +++ b/ws/chat.sh @@ -0,0 +1,2 @@ +cd `dirname "$0"` +ant -f ../ant/build.xml runChatJar diff --git a/ws/login.sh b/ws/login.sh new file mode 100755 index 0000000..472e3a4 --- /dev/null +++ b/ws/login.sh @@ -0,0 +1,2 @@ +cd `dirname "$0"` +ant -f ../ant/build.xml runLoginJar diff --git a/ws/start_chat.sh b/ws/start_chat.sh new file mode 100755 index 0000000..76aaa3a --- /dev/null +++ b/ws/start_chat.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Title : start_dodwand.sh +# Role : script for launching the DoDWAN daemon + +# Syntax : ./start_dodwand.sh [-Dkey=value ...] + +DODWAN_VERSION=1.0.1 + +DODWAN_ROOT=${HOME}/DoDWAN + + +if [ "${HOSTNAME}" == "" ] ; then + echo "Variable HOSTNAME not defined." + exit 1 +fi + +java -Ddodwan.root=${DODWAN_ROOT} \ + -Ddodwan.host=${HOSTNAME} \ + $* -cp ./dodwan-${DODWAN_VERSION}.jar:Chat.jar -Ddodwan.announce_period=1 network.chat.LaunchChat