├── system_configs └── raspbian_buster │ ├── home │ ├── app │ │ ├── ffbbg.jpg │ │ ├── .fluxbox │ │ │ ├── menu │ │ │ ├── menu-backup │ │ │ ├── lastwallpaper │ │ │ └── init │ │ ├── ffbbg-h.jpg │ │ ├── ffbbg-v.jpg │ │ ├── onboard_reset.sh │ │ └── rotate_desktop.sh │ └── app-data │ │ ├── MUSIC │ │ └── yahVlcWasAdded │ │ ├── fbb.sh │ │ ├── ffb.sh │ │ └── FlashFreeBoardDesktop-1.0-SNAPSHOT-jar-with-dependencies │ └── etc │ └── systemd │ └── system │ ├── vlc.service │ ├── fbb.service │ ├── ffb.service │ ├── xhosthack.service │ ├── dconfUserReload.service │ ├── rotate.service │ └── keyboard.service ├── .travis.yml ├── docs └── imgs │ ├── box.png │ ├── dev.png │ ├── new.png │ ├── two.png │ ├── ball.png │ ├── campus.png │ ├── clock.png │ ├── date.png │ ├── hBar.png │ ├── menu.png │ ├── quick.png │ ├── readme.png │ ├── search.png │ ├── select.png │ ├── slow1.png │ ├── slow2.png │ ├── slow3.png │ ├── stats.png │ ├── vBar.png │ ├── whole.png │ ├── addClimb.png │ ├── navFull.png │ ├── timered.png │ ├── wholeCz.png │ ├── directJump.png │ ├── manual.cz.png │ ├── selectMulti.png │ └── wholeMarked.png ├── manifest.mf ├── tesBoards ├── bad.wall ├── test20.wall ├── moonboard.wall └── img.jpeg_Thu_Feb_21_16_30_05_CET_2019.wall ├── src └── main │ ├── resources │ └── org │ │ └── fbb │ │ └── board │ │ ├── tutorial │ │ ├── imgs │ │ │ ├── 01_1.png │ │ │ ├── 02_2.png │ │ │ ├── 03_3.png │ │ │ ├── 00_0-4.png │ │ │ ├── 04_noTop.png │ │ │ ├── 05__top.png │ │ │ ├── 06_filled.png │ │ │ └── 07_sfinx.png │ │ └── show │ │ ├── latest-release │ │ └── internals │ │ └── grades │ │ └── tabulka │ └── java │ └── org │ └── fbb │ └── board │ ├── internals │ ├── grid │ │ ├── Meassurable.java │ │ ├── HoldMarkerProvider.java │ │ ├── PhotoGrider.java │ │ ├── RelativePoint.java │ │ └── Line.java │ ├── training │ │ ├── ListSetter.java │ │ ├── TrainingWithBackends.java │ │ ├── TrainingPlaylist.java │ │ ├── BoulderCalc.java │ │ ├── Training.java │ │ └── TimeredTraining.java │ ├── FUtils.java │ ├── comm │ │ ├── ByteEater.java │ │ ├── ListAndWrite.java │ │ ├── ConnectionID.java │ │ └── wired │ │ │ └── PortWork.java │ ├── db │ │ ├── GuiExceptionHandler.java │ │ ├── ExceptionHandler.java │ │ └── Puller.java │ ├── ContentReaderListener.java │ ├── GuiLogHelper.java │ ├── ProcessResult.java │ ├── HistoryManager.java │ ├── TeeOutputStream.java │ ├── grades │ │ └── Grade.java │ ├── ContentReader.java │ └── ProcessWrapper.java │ ├── desktop │ ├── tutorial │ │ ├── StoryPart.java │ │ ├── SingleText.java │ │ ├── ImgProvider.java │ │ ├── StoryProvider.java │ │ └── awt │ │ │ ├── ImageWithTitles.java │ │ │ └── HelpWindow.java │ ├── gui │ │ ├── awtimpl │ │ │ ├── TipsListener.java │ │ │ ├── IconifierThread.java │ │ │ └── WinUtils.java │ │ ├── dialogs │ │ │ ├── parts │ │ │ │ ├── DefaultSnoozeAction.java │ │ │ │ ├── Snoozer.java │ │ │ │ └── TimeAndSnooze.java │ │ │ ├── LogView.java │ │ │ ├── SlowBoulder.java │ │ │ ├── BoxesWindow.java │ │ │ ├── ClockWindow.java │ │ │ └── CampusLikeDialog.java │ │ ├── GitAuthenticator.java │ │ ├── Authenticator.java │ │ ├── MainWindow.java │ │ ├── ArduinoWindow.java │ │ └── LoadBackgroundOrImportOrLoadWall.java │ ├── TextToSpeech.java │ └── ScreenFinder.java │ ├── Utils.java │ └── Translator.java ├── .gitignore ├── LICENSE ├── README.md └── pom.xml /system_configs/raspbian_buster/home/app/ffbbg.jpg: -------------------------------------------------------------------------------- 1 | ffbbg-h.jpg -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app-data/MUSIC/yahVlcWasAdded: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | sudo: false 5 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app/.fluxbox/menu: -------------------------------------------------------------------------------- 1 | [begin] (fluxbox) 2 | [end] 3 | -------------------------------------------------------------------------------- /docs/imgs/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/box.png -------------------------------------------------------------------------------- /docs/imgs/dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/dev.png -------------------------------------------------------------------------------- /docs/imgs/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/new.png -------------------------------------------------------------------------------- /docs/imgs/two.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/two.png -------------------------------------------------------------------------------- /docs/imgs/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/ball.png -------------------------------------------------------------------------------- /docs/imgs/campus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/campus.png -------------------------------------------------------------------------------- /docs/imgs/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/clock.png -------------------------------------------------------------------------------- /docs/imgs/date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/date.png -------------------------------------------------------------------------------- /docs/imgs/hBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/hBar.png -------------------------------------------------------------------------------- /docs/imgs/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/menu.png -------------------------------------------------------------------------------- /docs/imgs/quick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/quick.png -------------------------------------------------------------------------------- /docs/imgs/readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/readme.png -------------------------------------------------------------------------------- /docs/imgs/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/search.png -------------------------------------------------------------------------------- /docs/imgs/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/select.png -------------------------------------------------------------------------------- /docs/imgs/slow1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/slow1.png -------------------------------------------------------------------------------- /docs/imgs/slow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/slow2.png -------------------------------------------------------------------------------- /docs/imgs/slow3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/slow3.png -------------------------------------------------------------------------------- /docs/imgs/stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/stats.png -------------------------------------------------------------------------------- /docs/imgs/vBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/vBar.png -------------------------------------------------------------------------------- /docs/imgs/whole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/whole.png -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | X-COMMENT: Main-Class will be added automatically by build 3 | 4 | -------------------------------------------------------------------------------- /tesBoards/bad.wall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/tesBoards/bad.wall -------------------------------------------------------------------------------- /docs/imgs/addClimb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/addClimb.png -------------------------------------------------------------------------------- /docs/imgs/navFull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/navFull.png -------------------------------------------------------------------------------- /docs/imgs/timered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/timered.png -------------------------------------------------------------------------------- /docs/imgs/wholeCz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/wholeCz.png -------------------------------------------------------------------------------- /tesBoards/test20.wall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/tesBoards/test20.wall -------------------------------------------------------------------------------- /docs/imgs/directJump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/directJump.png -------------------------------------------------------------------------------- /docs/imgs/manual.cz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/manual.cz.png -------------------------------------------------------------------------------- /docs/imgs/selectMulti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/selectMulti.png -------------------------------------------------------------------------------- /docs/imgs/wholeMarked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/docs/imgs/wholeMarked.png -------------------------------------------------------------------------------- /tesBoards/moonboard.wall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/tesBoards/moonboard.wall -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app/.fluxbox/menu-backup: -------------------------------------------------------------------------------- 1 | [begin] (fluxbox) 2 | [include] (/etc/X11/fluxbox/fluxbox-menu) 3 | [end] 4 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app-data/fbb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | java -Xmx512m -jar /home/app-data/FlashBalknaSwing*.jar iconifyWindow 3 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app-data/ffb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | java -Xmx2g -jar /home/app-data/FlashFreeBoardDesktop-*-jar-with-dependencies.jar 3 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app/ffbbg-h.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/system_configs/raspbian_buster/home/app/ffbbg-h.jpg -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app/ffbbg-v.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/system_configs/raspbian_buster/home/app/ffbbg-v.jpg -------------------------------------------------------------------------------- /tesBoards/img.jpeg_Thu_Feb_21_16_30_05_CET_2019.wall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/tesBoards/img.jpeg_Thu_Feb_21_16_30_05_CET_2019.wall -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/tutorial/imgs/01_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/src/main/resources/org/fbb/board/tutorial/imgs/01_1.png -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/tutorial/imgs/02_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/src/main/resources/org/fbb/board/tutorial/imgs/02_2.png -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/tutorial/imgs/03_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/src/main/resources/org/fbb/board/tutorial/imgs/03_3.png -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/tutorial/imgs/00_0-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/src/main/resources/org/fbb/board/tutorial/imgs/00_0-4.png -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/tutorial/imgs/04_noTop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/src/main/resources/org/fbb/board/tutorial/imgs/04_noTop.png -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/tutorial/imgs/05__top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/src/main/resources/org/fbb/board/tutorial/imgs/05__top.png -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/tutorial/imgs/06_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/src/main/resources/org/fbb/board/tutorial/imgs/06_filled.png -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/tutorial/imgs/07_sfinx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/src/main/resources/org/fbb/board/tutorial/imgs/07_sfinx.png -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app/.fluxbox/lastwallpaper: -------------------------------------------------------------------------------- 1 | fbsetroot|-foreground rgb:10/18/20 -background rgb:30/38/40 -mod 4 4|style|:0.0 2 | $full $full|/home/app/ffbbg.jpg||:0 3 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/etc/systemd/system/vlc.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start VLC 3 | After=rotate.service 4 | 5 | [Service] 6 | User=app-data 7 | Group=app-data 8 | Environment="DISPLAY=:0" 9 | Environment="LANG=cs_CZ.UTF-8" 10 | ExecStart=vlc /home/app-data/MUSIC 11 | Restart=always 12 | RestartSec=3 13 | 14 | [Install] 15 | WantedBy=graphical.target 16 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/etc/systemd/system/fbb.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start FBB 3 | After=rotate.service 4 | 5 | [Service] 6 | User=app-data 7 | Group=app-data 8 | Environment="DISPLAY=:0" 9 | Environment="LANG=cs_CZ.UTF-8" 10 | ExecStart=bash /home/app-data/fbb.sh 11 | Restart=always 12 | RestartSec=3 13 | 14 | [Install] 15 | WantedBy=graphical.target 16 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/etc/systemd/system/ffb.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start FFB 3 | After=rotate.service 4 | 5 | [Service] 6 | User=app-data 7 | Group=app-data 8 | Environment="DISPLAY=:0" 9 | Environment="LANG=cs_CZ.UTF-8" 10 | ExecStart=/bin/bash /home/app-data/ffb.sh 11 | Restart=always 12 | RestartSec=3 13 | 14 | [Install] 15 | WantedBy=graphical.target 16 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/etc/systemd/system/xhosthack.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=enable app data 3 | After=rotate.service 4 | 5 | [Service] 6 | User=app 7 | Group=app 8 | Environment="DISPLAY=:0" 9 | Environment="LANG=cs_CZ.UTF-8" 10 | ExecStart=xhost +SI:localuser:app-data 11 | Restart=always 12 | RestartSec=3 13 | 14 | [Install] 15 | WantedBy=graphical.target 16 | 17 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/etc/systemd/system/dconfUserReload.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=reload onboard conf and the app every N secnds, to prevent misconfigurations 3 | After=ffb.service 4 | 5 | [Service] 6 | User=app 7 | Group=app 8 | Restart=always 9 | # every 10 minues 10 | RestartSec=600 11 | ExecStart=bash /home/app/onboard_reset.sh 12 | 13 | [Install] 14 | WantedBy=graphical.target 15 | 16 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/etc/systemd/system/rotate.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Rotate desktop to portrait, including touch coordinates 3 | 4 | [Service] 5 | User=app 6 | Group=app 7 | Restart=on-failure 8 | RestartSec=5 9 | Environment="DISPLAY=:0" 10 | #ExecStart=bash /home/app/rotate_desktop.sh left 11 | ExecStart=bash /home/app/rotate_desktop.sh normal 12 | 13 | [Install] 14 | WantedBy=graphical.target 15 | -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/latest-release: -------------------------------------------------------------------------------- 1 | https://github.com/judovana/FlashFreeBoardDesktop/releases/download/FlashFreeBoardDesktop-1.8/FlashFreeBoardDesktop-1.8-jar-with-dependencies.jar 2 | https://raw.githubusercontent.com/judovana/arduino/ee1feba8e3b7fe315f94554bfe9854965d2ad5bd/FreeBoard/WS2812blinks/WS2812blinks.ino 3 | https://github.com/judovana/FlashBalknaSwing/releases/download/FlashBalknaSwing-2.4/FlashBalknaSwing_2.4.jar 4 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/grid/Meassurable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.grid; 7 | 8 | /** 9 | * 10 | * @author jvanek 11 | */ 12 | interface Meassurable { 13 | public int getWidth(); 14 | public int getHeight(); 15 | } 16 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app/onboard_reset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | # have or dont have services all vars? 4 | HOME=/home/app/ 5 | orig="$HOME/onboard.default" 6 | current="$HOME/.config/dconf/user" 7 | if [ -f $orig ] ; then 8 | killall onboard || true 9 | sleep 1 #wait for dconf to save ; see keyboard.service RestartSec 10 | cp -v $orig $current 11 | exit 133 12 | else 13 | echo "$orig do not exists, creating new" 14 | cp -v $current $orig 15 | exit 122 16 | fi 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | *.so 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | /build/ 25 | /dist/ 26 | /target/ 27 | 28 | /nbproject/ 29 | .idea/ 30 | FlashFreeBoardDesktop.iml 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/training/ListSetter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.training; 7 | 8 | import org.fbb.board.internals.Filter; 9 | 10 | /** 11 | * 12 | * @author jvanek 13 | */ 14 | public abstract class ListSetter { 15 | 16 | public abstract void setUpBoulderWall(Filter f, String selected, String title); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/FUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals; 7 | 8 | import javax.swing.JLabel; 9 | import javax.swing.JPanel; 10 | 11 | /** 12 | * 13 | * @author jvanek 14 | */ 15 | public class FUtils { 16 | 17 | public static void align(int from, int to, JPanel p) { 18 | for (int i = from; i < to; i++) { 19 | p.add(new JLabel()); 20 | p.add(new JLabel()); 21 | }; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/etc/systemd/system/keyboard.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start virtual keyboards 3 | After=ffb.service 4 | 5 | [Service] 6 | User=app 7 | Group=app 8 | Restart=always 9 | RestartSec=3 10 | Environment="DISPLAY=:0" 11 | # see the onboard_reset.sh 12 | ExecStartPre=sleep 1 13 | #ExecStart=matchbox-keyboard 14 | # onboard is the only keyboard with ctrl in selections working 15 | # onboard must have docking enabled and show floating icon when hideen 16 | # onboard must have long click set to key repeat (special chars were crashing the system) 17 | # onboard must have 18 | ExecStart=onboard 19 | 20 | [Install] 21 | WantedBy=graphical.target 22 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/comm/ByteEater.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.comm; 7 | 8 | import org.fbb.board.internals.grid.Grid; 9 | 10 | /** 11 | * 12 | * @author jvanek 13 | */ 14 | public interface ByteEater { 15 | 16 | public void sendBytes(int[] b, Grid id); 17 | public void deregisterProvider(Grid id); 18 | public void reset(); 19 | public int getNumberOfRegisteredProviders(); 20 | public int getMaxOfRegisteredProviders(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/comm/ListAndWrite.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.comm; 7 | 8 | /** 9 | * 10 | * @author jvanek 11 | */ 12 | public interface ListAndWrite { 13 | 14 | public static interface LogConsole { 15 | 16 | public void print(Exception ex); 17 | } 18 | 19 | public ConnectionID[] listDevices(); 20 | 21 | public void writeToDevice(String id, byte[]... b); 22 | 23 | // public void setLogConsole(LogConsole l); 24 | //need tee stream, and as the BT and Wire are static, different architecture 25 | 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This program is offered under a commercial and under the AGPL license. 2 | For commercial licensing, contact us at via the issue tracker on this github page. For AGPL licensing, see below. 3 | 4 | AGPL licensing: 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/tutorial/StoryPart.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.tutorial; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * 13 | * @author jvanek 14 | */ 15 | public class StoryPart { 16 | 17 | private final String img; 18 | private final List lines; 19 | 20 | public StoryPart(String img) { 21 | this.img = img; 22 | this.lines = new ArrayList<>(); 23 | } 24 | 25 | public void addLine(SingleText l) { 26 | lines.add(l); 27 | } 28 | 29 | public String getImg() { 30 | return img; 31 | } 32 | 33 | public List getLines() { 34 | return lines; 35 | } 36 | 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/tutorial/SingleText.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.tutorial; 7 | 8 | /** 9 | * 10 | * @author jvanek 11 | */ 12 | public class SingleText { 13 | 14 | private final int x; 15 | private final int y; 16 | private final String key; 17 | 18 | public SingleText(int x, int y, String key) { 19 | this.x = x; 20 | this.y = y; 21 | this.key = key; 22 | } 23 | 24 | public int getX(double d) { 25 | return (int)((double)x*d); 26 | } 27 | 28 | public int getY(double d) { 29 | return (int)((double)y*d); 30 | } 31 | 32 | public String getKey() { 33 | return key; 34 | } 35 | 36 | 37 | 38 | 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/comm/ConnectionID.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.comm; 7 | 8 | /** 9 | * 10 | * @author jvanek 11 | */ 12 | public class ConnectionID { 13 | 14 | /** 15 | * id to use to be used to communicate 16 | */ 17 | private final String id; 18 | /** 19 | * human nice name for listing 20 | */ 21 | private final String name; 22 | 23 | public ConnectionID(String id, String name) { 24 | this.id = id; 25 | this.name = name; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return name + " [" + id + "]"; 31 | } 32 | 33 | public String getId() { 34 | return id; 35 | } 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/tutorial/ImgProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.tutorial; 7 | 8 | import java.awt.image.BufferedImage; 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import javax.imageio.ImageIO; 13 | 14 | /** 15 | * 16 | * @author jvanek 17 | */ 18 | public class ImgProvider { 19 | 20 | private final Map cache = new HashMap<>(15); 21 | 22 | public ImgProvider() { 23 | 24 | } 25 | 26 | public BufferedImage get(String name) throws IOException { 27 | BufferedImage bi = cache.get(name); 28 | if (bi == null) { 29 | bi = ImageIO.read((this.getClass().getClassLoader().getResourceAsStream("org/fbb/board/tutorial/imgs/" + name))); 30 | cache.put(name, bi); 31 | } 32 | return bi; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/tutorial/show: -------------------------------------------------------------------------------- 1 | 00_0-4.png 2 | 00_0-4.png 3 | 485 629 FIRST_CLICK 4 | 01_1.png 5 | 01_1.png 6 | 485 629 SECOND_CLICK 7 | 02_2.png 8 | 02_2.png 9 | 485 629 THIRD_CLICK 10 | 03_3.png 11 | 03_3.png 12 | 485 629 FOURTH_CLICK 13 | 00_0-4.png 14 | 00_0-4.png 15 | 485 629 AGAIN_NOTHING 16 | 00_0-4.png 17 | 04_noTop.png 18 | 04_noTop.png 19 | 485 629 CREATE_BOULDER 20 | 04_noTop.png 21 | 485 629 NO_TOP 22 | 04_noTop.png 23 | 05__top.png 24 | 05__top.png 25 | 485 629 ADD_TOP 26 | 05__top.png 27 | 05__top.png 28 | 99 150 MUST_FILL 29 | 05__top.png 30 | 207 123 GRADE 31 | 05__top.png 32 | 341 96 NAME 33 | 05__top.png 34 | 204 72 AUTHOR 35 | 05__top.png 36 | 06_filled.png 37 | 06_filled.png 38 | 99 150 FILLED 39 | 06_filled.png 40 | 99 150 FILLED 41 | 207 123 GRADE 42 | 06_filled.png 43 | 99 150 FILLED 44 | 207 123 GRADE 45 | 341 96 NAME 46 | 06_filled.png 47 | 99 150 FILLED 48 | 207 123 GRADE 49 | 341 96 NAME 50 | 204 72 AUTHOR 51 | 06_filled.png 52 | 165 921 PRESS_DONE 53 | 07_sfinx.png 54 | 07_sfinx.png 55 | 215 294 SFINX_Q 56 | 07_sfinx.png 57 | 215 294 SFINX_A 58 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/db/GuiExceptionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.db; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import javax.swing.JOptionPane; 11 | import org.fbb.board.Translator; 12 | import org.fbb.board.internals.GuiLogHelper; 13 | 14 | public class GuiExceptionHandler implements ExceptionHandler { 15 | 16 | private static final List remebered = new ArrayList<>(); 17 | 18 | @Override 19 | public void handleWithIssue(Throwable t) throws Throwable { 20 | throw new UnsupportedOperationException("Should not occure"); 21 | } 22 | 23 | @Override 24 | public void handleCleanly(Throwable t) { 25 | GuiLogHelper.guiLogger.loge(t); 26 | if (remebered.contains(t.getClass())) { 27 | return; 28 | } 29 | int r = JOptionPane.showConfirmDialog(null, t + "\n\n" + Translator.R("gitIssueQuestion"), Translator.R("gitIssue"), JOptionPane.YES_NO_OPTION); 30 | if (r == JOptionPane.NO_OPTION) { 31 | remebered.add(t.getClass()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/awtimpl/TipsListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.awtimpl; 7 | 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.ActionListener; 10 | import javax.swing.JOptionPane; 11 | import org.fbb.board.Translator; 12 | 13 | /** 14 | * 15 | * @author jvanek 16 | */ 17 | public class TipsListener implements ActionListener { 18 | 19 | public TipsListener() { 20 | } 21 | 22 | @Override 23 | public void actionPerformed(ActionEvent e) { 24 | JOptionPane.showMessageDialog(null, "" + "
  • " + Translator.R("tip1") + "
      " + "
    • " + escape(Translator.R("tip2")) + "
    • " + escape(Translator.R("tip3")) + "
    " + "
      " + "
    • " + escape(Translator.R("tip4")) + "
    • " + escape(Translator.R("tip5")) + "
    • " + escape(Translator.R("tip6")) + "
    " + "
  • " + escape(Translator.R("tips7")) + "
      " + "
    • " + escape(Translator.R("tips8")) + "
    • " + escape(Translator.R("tips9")) + "
    • " + escape(Translator.R("tips10")) + "
    "); 25 | } 26 | 27 | private String escape(String R) { 28 | return R.replace("<", "<").replace(">", ">"); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/awtimpl/IconifierThread.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.awtimpl; 7 | 8 | import javax.swing.JFrame; 9 | import org.fbb.board.desktop.gui.MainWindow; 10 | 11 | /** 12 | * 13 | * @author jvanek 14 | */ 15 | class IconifierThread extends Thread { 16 | JFrame target; 17 | private static final int WINDOW_COMFOR_ZONE = 5 * 60 * 1000; 18 | 19 | public IconifierThread() { 20 | super(); 21 | this.target = null; 22 | this.setDaemon(true); 23 | this.setName("iconifier"); 24 | this.start(); 25 | } 26 | 27 | public void setTarget(JFrame target) { 28 | this.target = target; 29 | } 30 | 31 | @Override 32 | public void run() { 33 | while (true) { 34 | try { 35 | Thread.sleep(WINDOW_COMFOR_ZONE); 36 | if (target != null && MainWindow.gs.isPopUpping()) { 37 | if (target.getState() == JFrame.ICONIFIED) { 38 | target.setState(JFrame.NORMAL); 39 | } 40 | } 41 | } catch (InterruptedException ex) { 42 | } 43 | ; 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/db/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.db; 7 | 8 | import org.fbb.board.internals.GuiLogHelper; 9 | 10 | /** 11 | * 12 | * @author jvanek 13 | * @param 14 | */ 15 | public interface ExceptionHandler { 16 | 17 | public void handleWithIssue(T t) throws T; 18 | 19 | public void handleCleanly(T t); 20 | 21 | class LoggingEater implements ExceptionHandler { 22 | 23 | @Override 24 | public void handleWithIssue(Throwable t) throws Throwable { 25 | throw new UnsupportedOperationException("Should not occure"); 26 | } 27 | 28 | @Override 29 | public void handleCleanly(Throwable t) { 30 | GuiLogHelper.guiLogger.loge(t); 31 | } 32 | } 33 | 34 | class Resender implements ExceptionHandler { 35 | 36 | @Override 37 | public void handleWithIssue(Throwable t) throws Throwable { 38 | throw new UnsupportedOperationException("Should not occure"); 39 | } 40 | 41 | @Override 42 | public void handleCleanly(Throwable t) { 43 | throw new RuntimeException(t); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/dialogs/parts/DefaultSnoozeAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.dialogs.parts; 7 | 8 | import org.fbb.board.desktop.gui.dialogs.CampusLikeDialog; 9 | import org.fbb.board.internals.grid.GridPane; 10 | 11 | /** 12 | * 13 | * @author jvanek 14 | */ 15 | public class DefaultSnoozeAction implements Runnable { 16 | 17 | private final GridPane gp; 18 | 19 | public DefaultSnoozeAction(GridPane gp) { 20 | this.gp = gp; 21 | } 22 | 23 | @Override 24 | public void run() { 25 | try { 26 | for (int x = 0; x < gp.getGrid().getWidth(); x++) { 27 | gp.getGrid().clean(); 28 | CampusLikeDialog.drawColumn(x, 1, gp.getGrid()); 29 | gp.repaintAndSendToKnown(); 30 | Thread.sleep(100); 31 | } 32 | for (int x = gp.getGrid().getWidth() - 1; x >= 0; x--) { 33 | gp.getGrid().clean(); 34 | CampusLikeDialog.drawColumn(x, 1, gp.getGrid()); 35 | gp.repaintAndSendToKnown(); 36 | Thread.sleep(100); 37 | } 38 | } catch (InterruptedException ex) { 39 | //unimportant 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/grid/HoldMarkerProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.grid; 7 | 8 | import java.awt.Color; 9 | 10 | /** 11 | * 12 | * @author jvanek 13 | */ 14 | public interface HoldMarkerProvider { 15 | 16 | //known styles 17 | public static final int FILL = 0; 18 | public static final int C_BIG = 1; 19 | public static final int C_SMALL = 2; 20 | public static final int C_BOTH = 3; 21 | public static final int E_BIG = 4; 22 | public static final int E_SMALL = 5; 23 | public static final int E_BOTH = 6; 24 | public static final int RECT = 7; 25 | //there is 8 of them 26 | public static int COUNT_OF_STYLES = 8; 27 | 28 | public float getStartRed(); 29 | 30 | public float getStartGreen(); 31 | 32 | public float getStartBlue(); 33 | 34 | public float getPathRed(); 35 | 36 | public float getPathGreen(); 37 | 38 | public float getPathBlue(); 39 | 40 | public float getTopRed(); 41 | 42 | public float getTopGreen(); 43 | 44 | public float getTopBlue(); 45 | 46 | public float getHoldMarkerOapcity(); 47 | 48 | public Color getGridColor(); 49 | 50 | public int getDefaultStyle(); 51 | 52 | public void setDefaultStyle(int a); 53 | 54 | public int getCurrentStyle(); 55 | 56 | public void setCurrentStyle(int a); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/tutorial/StoryProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.tutorial; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.InputStreamReader; 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | /** 16 | * 17 | * @author jvanek 18 | */ 19 | public class StoryProvider { 20 | 21 | private final List story = new ArrayList(50); 22 | 23 | public StoryProvider() throws IOException { 24 | 25 | 26 | try(BufferedReader br = new BufferedReader(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("org/fbb/board/tutorial/show"), "utf-8"))) { 27 | while (true) { 28 | String s = br.readLine(); 29 | if (s == null) { 30 | break; 31 | } 32 | if (s.startsWith(" ")||s.startsWith("\t")){ 33 | String[] parts = s.trim().split("\\s"); 34 | story.get(story.size()-1).addLine(new SingleText(Integer.valueOf(parts[0]), Integer.valueOf(parts[1]), parts[2])); 35 | }else{ 36 | story.add(new StoryPart(s.trim())); 37 | } 38 | } 39 | } 40 | } 41 | 42 | public List getStory(){ 43 | return Collections.unmodifiableList(story); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * 13 | * @author jvanek 14 | */ 15 | public class Utils { 16 | 17 | public static List bresenhamLine(int x1, int y1, int x2, int y2) { 18 | List l = new ArrayList<>(); 19 | if ((x1 == x2) && (y1 == y2)) { 20 | l.add(new int[]{x1, y1}); 21 | } else { 22 | int dx = Math.abs(x2 - x1); 23 | int dy = Math.abs(y2 - y1); 24 | int diff = dx - dy; 25 | 26 | int moveX, moveY; 27 | 28 | if (x1 < x2) { 29 | moveX = 1; 30 | } else { 31 | moveX = -1; 32 | } 33 | if (y1 < y2) { 34 | moveY = 1; 35 | } else { 36 | moveY = -1; 37 | } 38 | 39 | while ((x1 != x2) || (y1 != y2)) { 40 | 41 | int p = 2 * diff; 42 | 43 | if (p > -dy) { 44 | diff = diff - dy; 45 | x1 = x1 + moveX; 46 | } 47 | if (p < dx) { 48 | diff = diff + dx; 49 | y1 = y1 + moveY; 50 | } 51 | l.add(new int[]{x1, y1}); 52 | } 53 | } 54 | return l; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/dialogs/parts/Snoozer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.dialogs.parts; 7 | 8 | import javax.swing.JLabel; 9 | import javax.swing.JTextField; 10 | import org.fbb.board.internals.training.BoulderCalc; 11 | 12 | /** 13 | * 14 | * @author jvanek 15 | */ 16 | public class Snoozer { 17 | 18 | private final Runnable close; 19 | private long snoozeCounter; 20 | 21 | Snoozer(Runnable close) { 22 | this.close = close; 23 | } 24 | 25 | void adjustSnoozeCounter(long by) { 26 | this.snoozeCounter += by; 27 | } 28 | 29 | void setSnoozeCounter(long snoozeCounter) { 30 | this.snoozeCounter = snoozeCounter; 31 | } 32 | 33 | boolean isOnZero(double delay) { 34 | return (double) snoozeCounter / 1000d < delay && (double) snoozeCounter / 1000d > -delay; 35 | } 36 | 37 | String getTime() { 38 | return BoulderCalc.code(snoozeCounter / 1000l); 39 | } 40 | 41 | void snooze() { 42 | close.run(); 43 | } 44 | 45 | void checkAndDo(long delay, JLabel time, JTextField origin) { 46 | this.adjustSnoozeCounter(-delay); 47 | time.setText(this.getTime()); 48 | time.repaint(); 49 | if (this.isOnZero((double) delay / 1000d) 50 | && BoulderCalc.decode(origin.getText()) > 0) { 51 | this.snooze(); 52 | } 53 | time.repaint(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/grid/PhotoGrider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.grid; 7 | 8 | import java.awt.image.BufferedImage; 9 | import java.io.IOException; 10 | import javax.imageio.ImageIO; 11 | import javax.swing.JFrame; 12 | import javax.swing.SwingUtilities; 13 | import org.fbb.board.internals.GuiLogHelper; 14 | 15 | /** 16 | * 17 | * @author jvanek 18 | */ 19 | public class PhotoGrider { 20 | 21 | /** 22 | * @param args the command line arguments 23 | */ 24 | public static void main(String[] args) { 25 | SwingUtilities.invokeLater(new Runnable() { 26 | 27 | @Override 28 | public void run() { 29 | JFrame f = new JFrame("Flash Free Board"); 30 | f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 31 | f.pack(); 32 | f.setSize(800, 600); 33 | BufferedImage img; 34 | try { 35 | img = ImageIO.read(PhotoGrider.class.getResourceAsStream("mnbr.png")); 36 | //img = ImageIO.read(PhotoGrider.class.getResourceAsStream("bgr.jpg")); 37 | } catch (IOException ex) { 38 | GuiLogHelper.guiLogger.loge(ex); 39 | img = new BufferedImage(800, 600, BufferedImage.TYPE_3BYTE_BGR); 40 | } 41 | f.add(new GridPane(img, null, null)); 42 | f.setVisible(true); 43 | } 44 | }); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/db/Puller.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.db; 7 | 8 | import org.fbb.board.internals.GuiLogHelper; 9 | 10 | public class Puller implements Runnable { 11 | 12 | private boolean alive = true; 13 | private int delay = 60;//1 minute 14 | private final DB db; 15 | 16 | private Puller(int seconds, DB db) { 17 | this.delay = seconds; 18 | this.db = db; 19 | } 20 | 21 | public void cancel() { 22 | alive = false; 23 | } 24 | 25 | public void setDelay(int seconds) { 26 | delay = seconds; 27 | } 28 | 29 | @Override 30 | public void run() { 31 | while (alive) { 32 | try { 33 | if (delay <= 0) { 34 | Thread.sleep(60l * 1000l); 35 | //GuiLogHelper.guiLogger.logo("Puller doing nothing..."); 36 | } else { 37 | Thread.sleep((long) delay * 1000l); 38 | pull(); 39 | } 40 | } catch (Exception ex) { 41 | GuiLogHelper.guiLogger.loge(ex); 42 | } 43 | } 44 | GuiLogHelper.guiLogger.logo("Puller ended"); 45 | } 46 | 47 | public void pull() { 48 | //GuiLogHelper.guiLogger.logo("Auto pull!!!"); 49 | db.pullCatched(new ExceptionHandler.LoggingEater()); 50 | } 51 | 52 | public static Puller create(int seconds, DB db) { 53 | Puller p = new Puller(seconds, db); 54 | Thread t = new Thread(p); 55 | t.setDaemon(true); 56 | t.start(); 57 | return p; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/ContentReaderListener.java: -------------------------------------------------------------------------------- 1 | /* ContentReaderListener.java 2 | Copyright (C) 2011,2012 Red Hat, Inc. 3 | 4 | This file is part of IcedTea. 5 | 6 | IcedTea is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, version 2. 9 | 10 | IcedTea is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with IcedTea; see the file COPYING. If not, write to 17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18 | 02110-1301 USA. 19 | 20 | Linking this library statically or dynamically with other modules is 21 | making a combined work based on this library. Thus, the terms and 22 | conditions of the GNU General Public License cover the whole 23 | combination. 24 | 25 | As a special exception, the copyright holders of this library give you 26 | permission to link this library with independent modules to produce an 27 | executable, regardless of the license terms of these independent 28 | modules, and to copy and distribute the resulting executable under 29 | terms of your choice, provided that you also meet, for each linked 30 | independent module, the terms and conditions of the license of that 31 | module. An independent module is a module which is not derived from 32 | or based on this library. If you modify this library, you may extend 33 | this exception to your version of the library, but you are not 34 | obligated to do so. If you do not wish to do so, delete this 35 | exception statement from your version. 36 | */ 37 | 38 | package org.fbb.board.internals; 39 | 40 | public interface ContentReaderListener { 41 | 42 | public void charReaded(char ch); 43 | public void lineReaded(String s); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlashFreeBoardDesktop 2 | Implementation of moonboard-like approach, on custom indoor climbing walls - PC client. See [video](http://www.facebook.com/1403584339870419/videos/562209057939918/), from this [zip](https://github.com/FlashFreeBoard/FreeBoard/raw/Olomouc20191113/walls/Olomouc20191116.wall) 3 | 4 | User manuals: [cz](https://htmlpreview.github.io/?https://github.com/judovana/FlashFreeBoardDesktop/blob/master/docs/cs_CZ.utf8.html) 5 | 6 | This is fully moonbaord-leds compatible client, however, due to no-limits on leds shining, I strongly recomends to set up own power. 7 | 8 | Except HW, you have no limitations on wall you build. Any holds, any size, any angle. Just mak a picture of it and import it as new wall, and start doing boulders on it. The client of yours can be connected to any git repository to store boulders also on remote 9 | - backup 10 | - remote maintance 11 | - several walls with same holds 12 | - several different walls on same repo 13 | However, there do not - intentionaly - exists shared world as on moonboard. well everybody is about to have custom layout :) 14 | 15 | You can set it up also as helper on campus like devices, to highlight some parts of it. 16 | 17 | As written above, there is no limitation on number of leds or even on number of lightened-on leds, however you powersource is limited, and leds eat - a bit only, but we have a lot of them. So there is built in protection to under-power. The leds will get dimmer from some point. Set upthis protection carefully! 18 | 19 | **Although the code is open, this is not entirely free stuff! Contact us by rising issue her, or by contacting http://www.flashbb.cz/**. **You are allowed to use this for non-comercial usage, to the size of moonboaard - 11x18 max.** Any commercial or any bigger array of leds in any dimesnion, must be consulted. Wall around 20x20 leds, delivered out of the box, with holds set for your difficulty, is priced aprox 20000eur, without transfer fees. 20 | 21 | Long live great moonboard, but from some time it starts to get boring, and without posibility to set up custom holds (which kills its meaning) it is doomed. 22 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/GuiLogHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * 12 | * @author jvanek 13 | */ 14 | public class GuiLogHelper { 15 | 16 | public GuiLogHelper() { 17 | //add middleware, which catches client's application stdout/err 18 | //and will submit it into console 19 | System.setErr(new TeeOutputStream(System.err, true)); 20 | System.setOut(new TeeOutputStream(System.out, false)); 21 | //internal stdOut/Err are going throughs outLog/errLog 22 | //when console is off, those tees are not installed 23 | } 24 | 25 | public static final GuiLogHelper guiLogger = new GuiLogHelper(); 26 | 27 | StringBuffer sout = new StringBuffer(); 28 | StringBuffer serr = new StringBuffer(); 29 | 30 | public synchronized String getSerr() { 31 | return serr.toString(); 32 | } 33 | 34 | public String getSout() { 35 | return sout.toString(); 36 | } 37 | 38 | public void logo(String s) { 39 | System.out.println(s); 40 | } 41 | 42 | int limit = 1000000; 43 | 44 | public synchronized void appendInternal(String s, boolean err) { 45 | StringBuffer a = sout; 46 | if (err) { 47 | a = serr; 48 | } 49 | if (s.startsWith(" ") | s.startsWith("\t")) { 50 | a.append(s); 51 | } else { 52 | a.append(new Date().toString()).append(": ").append(s); 53 | } 54 | if (a.length() > limit) { 55 | a.delete(0, a.length() - (limit - 1)); 56 | } 57 | } 58 | 59 | public void loge(String s) { 60 | System.err.println(s); 61 | } 62 | 63 | public void loge(Throwable e) { 64 | e.printStackTrace(System.err); 65 | 66 | } 67 | 68 | public static void main(String... ar) { 69 | StringBuffer a = new StringBuffer("123456789"); 70 | System.out.println(a.delete(0, a.length() - 5)); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/training/TrainingWithBackends.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.training; 7 | 8 | import javax.swing.JCheckBox; 9 | import org.fbb.board.internals.Filter; 10 | 11 | /** 12 | * 13 | * @author jvanek 14 | */ 15 | public class TrainingWithBackends { 16 | 17 | private final BoulderCalc boulderCalc; 18 | final JCheckBox allowRandom; 19 | final JCheckBox allowRegular; 20 | final JCheckBox allowJumps; 21 | private final Training training; 22 | private final int initialDelay; 23 | private final ListSetter listSetter; 24 | private final String title; 25 | 26 | public TrainingWithBackends(BoulderCalc bc, JCheckBox random, JCheckBox regular, JCheckBox skipping, Training training, int initialDelay, String title, ListSetter ls) { 27 | this.boulderCalc = bc; 28 | this.allowRandom = random; 29 | this.allowRegular = regular; 30 | this.allowJumps = skipping; 31 | this.training = training; 32 | this.initialDelay = initialDelay; 33 | this.title = title; 34 | this.listSetter = ls; 35 | } 36 | 37 | public void setBoulderCalc() { 38 | boulderCalc.setTimeOfBoulder(training.innerSettings.tOfBoulder); 39 | boulderCalc.setTimeOfTraining(training.innerSettings.tOfTraining); 40 | boulderCalc.setNumBoulders(training.innerSettings.numOfBoulders); 41 | } 42 | 43 | public void setChecks() { 44 | allowRandom.setSelected(training.innerSettings.random); 45 | allowRegular.setSelected(training.innerSettings.regular); 46 | allowJumps.setSelected(training.innerSettings.jumps); 47 | } 48 | 49 | public void init() { 50 | listSetter.setUpBoulderWall(training.filter, training.currentName, title); 51 | } 52 | 53 | int getTotalTime() { 54 | return boulderCalc.getTotalTime(); 55 | } 56 | 57 | int getTimeOfBoulder() { 58 | return boulderCalc.getTimeOfBoulder(); 59 | } 60 | 61 | public int getInitialDelay() { 62 | return initialDelay; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/resources/org/fbb/board/internals/grades/tabulka: -------------------------------------------------------------------------------- 1 | id: UIAA france usa-boulder fountainbleu usa sasko britain austral 2 | ####################################################################################################### 3 | 0: I 1 v0--------- fb_0 5.2 I moderate 11- 4 | 1: II 2 v0-------- fb_0+ 5.3 II dificult 11 5 | 2: III 3 v0------- fb_1- 5.4 III very dificult 11+ 6 | 3: IV- 4- v0------ fb_1 5.4+ IV- 4a- 12 7 | 4: IV 4 v0----- fb_1+ 5.5 IV 4a 12+ 8 | 5: IV+ 4+ v0---- fb_2- 5.5+ IV+ 4a+ 13- 9 | 6: V- 5- v0--- fb_2 5.6 V 4b- 13 10 | 7: V 5 v0-- fb_2+ 5.7 VI 4b+ 14 11 | 8: V+ 5+ v0- fb_3 5.8 VII 4c 15 12 | 9: VI- 6a- v0 fb_4 5.9- VIIb 5a 16 13 | 10: VI 6a v0+ fb_5A 5.9 VIIb/c 5a+ 17 14 | 11: VI+ 6a+ v1 fb_5B 5.10a VIIc 5b 18 15 | 12: VII- 6b v1+ fb_5C 5.10b VIII 5b/5b+ 19 16 | 13: VII-/VII 6b/6b+ v2- fb_6A 5.10c VIIIb 5b+ 20 17 | 14: VII 6b+ v2 fb_6A+ 5.10d VIIIb/c 5b+/6a- 21 18 | 15: VII+ 6c v2/v3 fb_6B 5.11a VIIIc 6a- 22 19 | 16: VIII- 6c+ v3 fb_6B+ 5.11b IX 6a+ 23 20 | 17: VIII 7a v3/v4 fb_6C 5.11c IXb 6a+/6b- 23+ 21 | 18: VIII/VIII+ 7a+/7b v4 fb_6C+ 5.11d IXc- 6b- 24 22 | 19: VIII+ 7b v5 fb_7A 5.12a IXc+ 6b/6b+ 25 23 | 20: VIII+/IX- 7b+ v6 fb_7A+ 5.12b X 6b+ 26 24 | 21: IX- 7c v6/v7 fb_7B- 5.12c Xb- 6b+/6c 27 25 | 22: IX 7c+ v7 fb_7B 5.12d Xb+ 6c 28 26 | 23: IX+ 8a v8 fb_7B+ 5.13a Xc 6c+ 29 27 | 24: X- 8a+ v9 fb_7C 5.13c XI 7a- 30 28 | 25: X 8b v10 fb_7C+ 5.13d XIb- 7a 31 29 | 26: X+ 8c v11 fb_8A 5.14a XIb+ 7a/7b 32 30 | 27: XI- 8c+ v12 fb_8A+ 5.14b+ XIc- 7b 33 31 | 28: XI-/XI 8c+/9a- v13 fb_8B 5.14c XIc-/XIc+ 7b+ 34 32 | 29: XI 9a v14 fb_8B+ 5.14d XIc+ 7c 35 33 | 30: XI+ 9a+ v15 fb_8C 5.15a XII 8a 36 34 | 31: XII- 9b- v16 fb_9A 5.15b XII+ 8a+ 37 35 | 32: XII 9b v17 fb_9A+ 5.15c XIIb 8a+/8a- 38 36 | 33: XII+ 9b+ v16 fb_9B 5.15d XIIc 8b- 39 37 | 34: XIII- 9c- v19 fb_9B+ 5.16a XIII 8b 40 38 | 35: XIII 9c v20 fb_9C 5.16b XIIIb 8b+ 40+ -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/grid/RelativePoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.grid; 7 | 8 | import java.awt.Color; 9 | import java.awt.Graphics; 10 | import java.awt.geom.Rectangle2D; 11 | 12 | /** 13 | * 14 | * @author jvanek 15 | */ 16 | public class RelativePoint { 17 | 18 | private double x; 19 | private double y; 20 | private final Meassurable parent; 21 | private final Rectangle2D.Double bounds; 22 | 23 | public RelativePoint(double x, double y, Meassurable parent) { 24 | this(x, y, parent, new Rectangle2D.Double(0, 0, 1, 1)); 25 | } 26 | 27 | public RelativePoint(double x, double y, Meassurable parent, Rectangle2D.Double relativeBounds) { 28 | this.x = x; 29 | this.y = y; 30 | this.parent = parent; 31 | this.bounds = relativeBounds; 32 | } 33 | 34 | public double getX() { 35 | return x; 36 | } 37 | 38 | public double getY() { 39 | return y; 40 | } 41 | 42 | public int getRealX() { 43 | return (int) (x * (double) parent.getWidth()); 44 | } 45 | 46 | public int getRealY() { 47 | return (int) (y * (double) parent.getHeight()); 48 | } 49 | 50 | public boolean setX(double x) { 51 | if (x >= bounds.x && x <= bounds.x + bounds.width) { 52 | this.x = x; 53 | return true; 54 | } else { 55 | return false; 56 | } 57 | } 58 | 59 | public boolean setY(double y) { 60 | if (y >= bounds.y && y <= bounds.y + bounds.height) { 61 | this.y = y; 62 | return true; 63 | } else { 64 | return false; 65 | } 66 | } 67 | 68 | public void draw(Graphics g) { 69 | int r = 50; 70 | Color c = g.getColor(); 71 | g.setColor(new Color(this.hashCode())); 72 | g.drawOval(getRealX() - r, getRealY() - r, 2 * r, 2 * r); 73 | g.setColor(c); 74 | } 75 | 76 | public boolean isInAreaOfControl(double x, double y) { 77 | return x >= bounds.x && x <= bounds.x + bounds.width 78 | && y >= bounds.y && y <= bounds.y + bounds.height; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/grid/Line.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.grid; 7 | 8 | import java.awt.Graphics; 9 | import java.awt.Point; 10 | import java.awt.geom.Point2D; 11 | 12 | /** 13 | * 14 | * @author jvanek 15 | */ 16 | public class Line { 17 | 18 | private final Point from; 19 | private final Point to; 20 | 21 | public Line(Point from, Point to) { 22 | this.from = from; 23 | this.to = to; 24 | } 25 | 26 | public Line(int x1, int y1, int x2, int y2) { 27 | this.from = new Point(x1, y1); 28 | this.to = new Point(x2, y2); 29 | } 30 | 31 | void draw(Graphics g) { 32 | g.drawLine(from.x, from.y, to.x, to.y); 33 | } 34 | 35 | void draw(Graphics g, int xShift, int yShift) { 36 | g.drawLine(from.x+xShift, from.y+yShift, to.x+xShift, to.y+yShift); 37 | } 38 | 39 | Point cross(Line l) { 40 | //not playing with paralel or identicals - they cant be form nature of grid 41 | Point.Double p1 = new Point2D.Double(from.x, from.y); 42 | Point2D.Double v1 = new Point2D.Double(to.x - from.x, to.y - from.y); 43 | Point.Double p2 = new Point2D.Double(l.from.x, l.from.y); 44 | Point2D.Double v2 = new Point2D.Double(l.to.x - l.from.x, l.to.y - l.from.y); 45 | Point2D.Double r = calculateInterceptionPoint(p1, v1, p2, v2); 46 | if (r == null) { 47 | return null; 48 | } 49 | return new Point((int) r.x, (int) r.y); 50 | } 51 | 52 | public static Point.Double calculateInterceptionPoint(Point2D.Double p1, Point2D.Double v1, Point2D.Double p2, Point2D.Double v2) { 53 | 54 | double sNumerator = p1.y * v1.x + p2.x * v1.y - p1.x * v1.y - p2.y * v1.x; 55 | double sDenominator = v2.y * v1.x - v2.x * v1.y; 56 | 57 | // parallel ... 0 or infinite points, or one of the vectors is 0|0 58 | if (sDenominator == 0) { 59 | return null; 60 | } 61 | 62 | double s = sNumerator / sDenominator; 63 | 64 | double t; 65 | if (v1.x != 0) { 66 | t = (p2.x + s * v2.x - p1.x) / v1.x; 67 | } else { 68 | t = (p2.y + s * v2.y - p1.y) / v1.y; 69 | } 70 | 71 | Point.Double i1 = new Point.Double(p1.x + t * v1.x, p1.y + t * v1.y); 72 | 73 | return i1; 74 | 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/tutorial/awt/ImageWithTitles.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.tutorial.awt; 7 | 8 | import java.awt.Color; 9 | import java.awt.Font; 10 | import java.awt.Graphics; 11 | import java.awt.Graphics2D; 12 | import java.awt.geom.Rectangle2D; 13 | import java.awt.image.BufferedImage; 14 | import java.util.Date; 15 | import java.util.List; 16 | import javax.swing.JPanel; 17 | import org.fbb.board.Translator; 18 | import org.fbb.board.desktop.gui.BoulderCreationGui; 19 | import org.fbb.board.desktop.tutorial.SingleText; 20 | import org.fbb.board.internals.Filter; 21 | 22 | /** 23 | * 24 | * @author jvanek 25 | */ 26 | public class ImageWithTitles extends JPanel { 27 | 28 | private final List titles; 29 | private final BufferedImage bi; 30 | 31 | public ImageWithTitles(List titles, BufferedImage bi) { 32 | this.titles = titles; 33 | this.bi = bi; 34 | } 35 | 36 | @Override 37 | public void paint(Graphics g) { 38 | g.drawImage(bi, 0, 0, this.getWidth(), this.getHeight(), null); 39 | double w = (double) this.getWidth() / (double) bi.getWidth(); 40 | double h = (double) this.getHeight() / (double) bi.getHeight(); 41 | g.setFont(g.getFont().deriveFont(g.getFont().getSize2D() * 1.5f)); 42 | for (int i = 0; i < titles.size(); i++) { 43 | SingleText get = titles.get(i); 44 | String s = Translator.R(get.getKey(), BoulderCreationGui.antiChild.format(new Date())); 45 | g.setFont(g.getFont().deriveFont(Font.BOLD)); 46 | Rectangle2D rect = g.getFontMetrics().getStringBounds(s, g);//java.awt.geom.Rectangle2D$Float[x=0.0,y=-11.138672,w=71.0,h=13.96875] 47 | int fh = g.getFontMetrics().getHeight();//15 48 | g.setColor(new Color(250, 250, 250, 200)); 49 | if (get.getX(w) + rect.getWidth() > getWidth()) { 50 | g.fillRect(getWidth() - (int) rect.getWidth(), get.getY(h), (int) rect.getWidth(), fh); 51 | g.setColor(Color.black); 52 | g.drawString(s, getWidth() - (int) rect.getWidth(), get.getY(h) + fh); 53 | } else { 54 | g.fillRect(get.getX(w), get.getY(h), (int) rect.getWidth(), fh); 55 | g.setColor(Color.black); 56 | g.drawString(s, get.getX(w), get.getY(h) + fh); 57 | } 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app/rotate_desktop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # many thanks: https://gist.github.com/mildmojo/48e9025070a2ba40795c 3 | # set -eo added against original to work fine as service 4 | set -e 5 | set -o pipefail 6 | # 7 | # rotate_desktop.sh 8 | # 9 | # Rotates modern Linux desktop screen and input devices to match. Handy for 10 | # convertible notebooks. Call this script from panel launchers, keyboard 11 | # shortcuts, or touch gesture bindings (xSwipe, touchegg, etc.). 12 | # 13 | # Using transformation matrix bits taken from: 14 | # https://wiki.ubuntu.com/X/InputCoordinateTransformation 15 | # 16 | 17 | # Configure these to match your hardware (names taken from `xinput` output). 18 | #TOUCHPAD='SynPS/2 Synaptics TouchPad' 19 | #TOUCHSCREEN='Atmel Atmel maXTouch Digitizer' 20 | 21 | # adapted against original 22 | TOUCHPAD='ELAN Touchscreen' 23 | TOUCHSCREEN='Virtual core XTEST pointer' 24 | 25 | if [ -z "$1" ]; then 26 | echo "Missing orientation." 27 | echo "Usage: $0 [normal|inverted|left|right] [revert_seconds]" 28 | echo 29 | exit 1 30 | fi 31 | 32 | function do_rotate 33 | { 34 | xrandr --output $1 --rotate $2 35 | 36 | TRANSFORM='Coordinate Transformation Matrix' 37 | 38 | case "$2" in 39 | normal) 40 | [ ! -z "$TOUCHPAD" ] && xinput set-prop "$TOUCHPAD" "$TRANSFORM" 1 0 0 0 1 0 0 0 1 41 | [ ! -z "$TOUCHSCREEN" ] && xinput set-prop "$TOUCHSCREEN" "$TRANSFORM" 1 0 0 0 1 0 0 0 1 42 | ;; 43 | inverted) 44 | [ ! -z "$TOUCHPAD" ] && xinput set-prop "$TOUCHPAD" "$TRANSFORM" -1 0 1 0 -1 1 0 0 1 45 | [ ! -z "$TOUCHSCREEN" ] && xinput set-prop "$TOUCHSCREEN" "$TRANSFORM" -1 0 1 0 -1 1 0 0 1 46 | ;; 47 | left) 48 | [ ! -z "$TOUCHPAD" ] && xinput set-prop "$TOUCHPAD" "$TRANSFORM" 0 -1 1 1 0 0 0 0 1 49 | [ ! -z "$TOUCHSCREEN" ] && xinput set-prop "$TOUCHSCREEN" "$TRANSFORM" 0 -1 1 1 0 0 0 0 1 50 | ;; 51 | right) 52 | [ ! -z "$TOUCHPAD" ] && xinput set-prop "$TOUCHPAD" "$TRANSFORM" 0 1 0 -1 0 1 0 0 1 53 | [ ! -z "$TOUCHSCREEN" ] && xinput set-prop "$TOUCHSCREEN" "$TRANSFORM" 0 1 0 -1 0 1 0 0 1 54 | ;; 55 | esac 56 | } 57 | 58 | XDISPLAY=`xrandr --current | grep primary | sed -e 's/ .*//g'` 59 | XROT=`xrandr --current --verbose | grep primary | egrep -o ' (normal|left|inverted|right) '` 60 | 61 | do_rotate $XDISPLAY $1 62 | sleep 2 63 | fbsetbg /home/app/ffbbg.jpg 64 | sleep 2 65 | rm /home/app/.fluxbox/lastwallpaper 66 | sleep 2 67 | fbsetbg /home/app/ffbbg.jpg 68 | 69 | 70 | # not disabled in original, as this is service, handled rather by forced restart; maybe bad idea 71 | # but the sleep may not be enough 72 | #if [ ! -z "$2" ]; then 73 | #sleep $2 74 | # do_rotate $XDISPLAY $XROT 75 | # exit 0 76 | #fi 77 | 78 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/GitAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui; 7 | 8 | import java.awt.GridLayout; 9 | import java.nio.charset.Charset; 10 | import javax.swing.JCheckBox; 11 | import javax.swing.JLabel; 12 | import javax.swing.JOptionPane; 13 | import javax.swing.JPanel; 14 | import javax.swing.JPasswordField; 15 | import org.fbb.board.Translator; 16 | import org.fbb.board.desktop.Files; 17 | import org.fbb.board.internals.GuiLogHelper; 18 | 19 | /** 20 | * 21 | * @author jvanek 22 | */ 23 | public class GitAuthenticator { 24 | 25 | private static char[] pernament = null; 26 | 27 | public boolean isPernament() { 28 | return pernament != null; 29 | } 30 | 31 | public static void revoke() { 32 | pernament = null; 33 | } 34 | 35 | public char[] authenticate(String message) { 36 | char[] r = authenticateImpl(message); 37 | return r; 38 | 39 | } 40 | 41 | private char[] authenticateImpl(String message) { 42 | if (pernament != null) { 43 | return pernament; 44 | } 45 | if (Files.remotePass.exists()) { 46 | try { 47 | String zzz = java.nio.file.Files.readAllLines(Files.remotePass.toPath(), Charset.forName("utf-8")).get(0); 48 | String plainText = SettingsListener.textEncryptor.decrypt(zzz); 49 | return plainText.toCharArray(); 50 | } catch (Exception ex) { 51 | GuiLogHelper.guiLogger.loge(ex); 52 | } 53 | } 54 | Object[] r = new AuthoriseDialog().show(message); 55 | if (r == null || r.length != 2) { 56 | return null; 57 | } 58 | char[] s = (char[]) (r[0]); 59 | if (s == null || new String(s).trim().isEmpty()) { 60 | return null; 61 | } 62 | if ((boolean) r[1]) { 63 | pernament = s; 64 | } 65 | return s; 66 | } 67 | 68 | private static class AuthoriseDialog { 69 | 70 | public Object[] show(String message) { 71 | JPasswordField pass = new JPasswordField(5); 72 | JCheckBox save = new JCheckBox(Translator.R("keepLoged"), false); 73 | 74 | JPanel myPanel = new JPanel(new GridLayout(3, 1)); 75 | myPanel.add(new JLabel(message)); 76 | myPanel.add(pass); 77 | myPanel.add(save); 78 | int result = JOptionPane.showConfirmDialog(null, myPanel, Translator.R("autenticateGit"), JOptionPane.OK_CANCEL_OPTION); 79 | if (result == JOptionPane.OK_OPTION) { 80 | return new Object[]{pass.getPassword(), save.isSelected()}; 81 | } 82 | return null; 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/ProcessResult.java: -------------------------------------------------------------------------------- 1 | /* ProcessResult.java 2 | Copyright (C) 2011,2012 Red Hat, Inc. 3 | 4 | This file is part of IcedTea. 5 | 6 | IcedTea is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, version 2. 9 | 10 | IcedTea is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with IcedTea; see the file COPYING. If not, write to 17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18 | 02110-1301 USA. 19 | 20 | Linking this library statically or dynamically with other modules is 21 | making a combined work based on this library. Thus, the terms and 22 | conditions of the GNU General Public License cover the whole 23 | combination. 24 | 25 | As a special exception, the copyright holders of this library give you 26 | permission to link this library with independent modules to produce an 27 | executable, regardless of the license terms of these independent 28 | modules, and to copy and distribute the resulting executable under 29 | terms of your choice, provided that you also meet, for each linked 30 | independent module, the terms and conditions of the license of that 31 | module. An independent module is a module which is not derived from 32 | or based on this library. If you modify this library, you may extend 33 | this exception to your version of the library, but you are not 34 | obligated to do so. If you do not wish to do so, delete this 35 | exception statement from your version. 36 | */ 37 | 38 | package org.fbb.board.internals; 39 | 40 | /** 41 | * artefacts what are left by finished process 42 | */ 43 | public class ProcessResult { 44 | 45 | public final String stdout; 46 | public final String notFilteredStdout; 47 | public final String stderr; 48 | public final Process process; 49 | public final Integer returnValue; 50 | public final boolean wasTerminated; 51 | /* 52 | * possible exception which caused Process not to be launched 53 | */ 54 | public final Throwable deadlyException; 55 | 56 | public ProcessResult(String stdout, String stderr, Process process, boolean wasTerminated, Integer r, Throwable deadlyException) { 57 | this.notFilteredStdout = stdout; 58 | if (stdout == null) { 59 | this.stdout = null; 60 | } else { 61 | this.stdout = stdout.replaceAll("EMMA:.*\n?", ""); 62 | } 63 | this.stderr = stderr; 64 | this.process = process; 65 | this.wasTerminated = wasTerminated; 66 | this.returnValue = r; 67 | this.deadlyException = deadlyException; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app/.fluxbox/init: -------------------------------------------------------------------------------- 1 | session.screen0.iconbar.usePixmap: true 2 | session.screen0.iconbar.iconWidth: 128 3 | session.screen0.iconbar.mode: {static groups} (workspace) 4 | session.screen0.iconbar.iconTextPadding: 10 5 | session.screen0.iconbar.alignment: Relative 6 | session.screen0.toolbar.autoHide: false 7 | session.screen0.toolbar.alpha: 255 8 | session.screen0.toolbar.widthPercent: 100 9 | session.screen0.toolbar.height: 0 10 | session.screen0.toolbar.tools: prevworkspace, workspacename, nextworkspace, clock, prevwindow, nextwindow, iconbar, systemtray 11 | session.screen0.toolbar.onhead: 1 12 | session.screen0.toolbar.visible: true 13 | session.screen0.toolbar.maxOver: false 14 | session.screen0.toolbar.layer: Dock 15 | session.screen0.toolbar.placement: BottomCenter 16 | session.screen0.clientMenu.usePixmap: true 17 | session.screen0.titlebar.left: Stick 18 | session.screen0.titlebar.right: Minimize Maximize Close 19 | session.screen0.menu.alpha: 255 20 | session.screen0.tabs.usePixmap: true 21 | session.screen0.tabs.maxOver: false 22 | session.screen0.tabs.intitlebar: true 23 | session.screen0.slit.onhead: 0 24 | session.screen0.slit.alpha: 255 25 | session.screen0.slit.autoHide: false 26 | session.screen0.slit.maxOver: false 27 | session.screen0.slit.acceptKdeDockapps: true 28 | session.screen0.slit.placement: RightBottom 29 | session.screen0.slit.layer: Dock 30 | session.screen0.window.focus.alpha: 255 31 | session.screen0.window.unfocus.alpha: 255 32 | session.screen0.tab.placement: TopLeft 33 | session.screen0.tab.width: 64 34 | session.screen0.focusNewWindows: true 35 | session.screen0.fullMaximization: false 36 | session.screen0.colPlacementDirection: TopToBottom 37 | session.screen0.defaultDeco: NORMAL 38 | session.screen0.tabFocusModel: ClickToTabFocus 39 | session.screen0.opaqueMove: true 40 | session.screen0.edgeSnapThreshold: 10 41 | session.screen0.noFocusWhileTypingDelay: 0 42 | session.screen0.allowRemoteActions: false 43 | session.screen0.clickRaises: true 44 | session.screen0.rowPlacementDirection: LeftToRight 45 | session.screen0.tooltipDelay: 500 46 | session.screen0.maxDisableResize: false 47 | session.screen0.focusModel: ClickFocus 48 | session.screen0.focusSameHead: false 49 | session.screen0.autoRaise: true 50 | session.screen0.showwindowposition: false 51 | session.screen0.menuDelay: 200 52 | session.screen0.maxDisableMove: false 53 | session.screen0.windowMenu: /home/app/.fluxbox/windowmenu 54 | session.screen0.maxIgnoreIncrement: true 55 | session.screen0.workspaceNames: Workspace 1,Workspace 2, 56 | session.screen0.workspaces: 1 57 | session.screen0.strftimeFormat: %d %b, %a %02k:%M:%S 58 | session.screen0.workspacewarping: true 59 | session.screen0.windowPlacement: RowMinOverlapPlacement 60 | session.keyFile: ~/.fluxbox/keys 61 | session.appsFile: /home/app/.fluxbox/apps 62 | session.forcePseudoTransparency: false 63 | session.colorsPerChannel: 4 64 | session.configVersion: 13 65 | session.styleOverlay: /home/app/.fluxbox/overlay 66 | session.ignoreBorder: false 67 | session.cacheMax: 200 68 | session.tabsAttachArea: Window 69 | session.autoRaiseDelay: 250 70 | session.menuFile: ~/.fluxbox/menu 71 | session.styleFile: /usr/share/fluxbox/styles/Artwiz 72 | session.doubleClickInterval: 250 73 | session.cacheLife: 5 74 | session.slitlistFile: /home/app/.fluxbox/slitlist 75 | session.tabPadding: 0 76 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/TextToSpeech.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop; 7 | 8 | import java.io.IOException; 9 | import org.fbb.board.Translator; 10 | import org.fbb.board.internals.GuiLogHelper; 11 | 12 | /** 13 | * 14 | * @author jvanek 15 | */ 16 | public class TextToSpeech { 17 | 18 | public static class TextId { 19 | 20 | final private String name; 21 | final private String code; 22 | 23 | public TextId(String name, String code) { 24 | this.name = name; 25 | this.code = code; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return name; 31 | } 32 | 33 | } 34 | private static final TextId[] avail = new TextId[]{ 35 | new TextId(Translator.R("silent"), null), 36 | new TextId(("čeština"), "cs"), 37 | new TextId(("english"), "en") 38 | 39 | }; 40 | 41 | public static void change(TextId id) { 42 | try { 43 | if (id != null && id.code != null) { 44 | if (id.code.equalsIgnoreCase("cs")) { 45 | tellImpl("Změna v tréniku", id); 46 | } 47 | if (id.code.equalsIgnoreCase("en")) { 48 | tellImpl("Change in training", id); 49 | } 50 | } 51 | } catch (Exception ex) { 52 | GuiLogHelper.guiLogger.loge(ex); 53 | } 54 | } 55 | 56 | public static void pause(TextId id) { 57 | try { 58 | if (id.code.equalsIgnoreCase("cs")) { 59 | tellImpl("Pauza", id); 60 | } 61 | if (id.code.equalsIgnoreCase("en")) { 62 | tellImpl("Break", id); 63 | } 64 | } catch (Exception ex) { 65 | GuiLogHelper.guiLogger.loge(ex); 66 | } 67 | } 68 | 69 | public void tell(String s, TextId id) { 70 | try { 71 | tellImpl(s, id); 72 | } catch (Exception ex) { 73 | GuiLogHelper.guiLogger.loge(ex); 74 | } 75 | } 76 | 77 | public static void tellImpl(String s, TextId id) throws IOException { 78 | if (id.code != null) { 79 | Process p = Runtime.getRuntime().exec(new String[]{"espeak", "-v", id.code, s}); 80 | } 81 | } 82 | 83 | public static boolean isAvail() throws IOException, InterruptedException { 84 | Process p = Runtime.getRuntime().exec("espeak --version"); 85 | p.waitFor(); 86 | return (p.exitValue() == 0); 87 | } 88 | 89 | public static TextId[] getLangs() { 90 | try { 91 | if (isAvail()) { 92 | return avail; 93 | } else { 94 | return new TextId[]{ 95 | new TextId(Translator.R("noEspeak"), null) 96 | }; 97 | } 98 | } catch (Exception ex) { 99 | GuiLogHelper.guiLogger.loge(ex); 100 | return new TextId[]{ 101 | new TextId(Translator.R("noEspeak"), null) 102 | }; 103 | 104 | } 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/HistoryManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals; 7 | 8 | import org.fbb.board.internals.grid.Boulder; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Vector; 12 | 13 | /** 14 | * 15 | * @author jvanek 16 | */ 17 | public class HistoryManager { 18 | 19 | protected final List history = new ArrayList<>(); 20 | protected int historyIndex = -1; 21 | protected final int llimit; 22 | 23 | public HistoryManager(int limit) { 24 | llimit = limit; 25 | } 26 | 27 | public HistoryManager() { 28 | llimit = -1; 29 | } 30 | 31 | //returns whether we are at end or not; 32 | //return true, if index is NOT last (and thus forward button can be enabld) 33 | public Boulder getCurrentInHistory() { 34 | if (history.isEmpty() || historyIndex < 0 || historyIndex > history.size()) { 35 | return null; 36 | } 37 | return history.get(historyIndex); 38 | } 39 | 40 | public int getHistoryIndex() { 41 | return historyIndex; 42 | } 43 | 44 | 45 | 46 | public void addToBoulderHistory(Boulder b) { 47 | if (history.isEmpty()) { 48 | history.add(b); 49 | historyIndex = 0; 50 | return; 51 | } 52 | if (historyIndex >= history.size() - 1) { 53 | historyIndex++; 54 | history.add(b); 55 | limit(); 56 | return; 57 | } 58 | historyIndex++; 59 | history.add(historyIndex, b); 60 | limit(); 61 | return; 62 | } 63 | 64 | public boolean canBack() { 65 | return historyIndex > 0; 66 | } 67 | 68 | public Vector getHistory() { 69 | return new Vector(history); 70 | } 71 | 72 | public void setIndex(int index) { 73 | this.historyIndex = index; 74 | } 75 | 76 | public void setIndex(String id) { 77 | for (int i = 0; i < history.size(); i++) { 78 | Boulder get = history.get(i); 79 | if (get.getFile().getName().equals(id)) { 80 | this.historyIndex = i; 81 | break; 82 | } 83 | } 84 | 85 | } 86 | 87 | public Boulder forward() { 88 | if (history.isEmpty()) { 89 | return null; 90 | } 91 | if (canFwd()) { 92 | historyIndex++; 93 | return history.get(historyIndex); 94 | } 95 | return history.get(historyIndex); 96 | } 97 | 98 | public Boulder back() { 99 | if (history.isEmpty()) { 100 | return null; 101 | } 102 | if (canBack()) { 103 | historyIndex--; 104 | return history.get(historyIndex); 105 | } 106 | return history.get(historyIndex); 107 | } 108 | 109 | public boolean canFwd() { 110 | return historyIndex < history.size() - 1; 111 | } 112 | 113 | public void clearHistory() { 114 | historyIndex = -1; 115 | history.clear(); 116 | } 117 | 118 | protected void limit() { 119 | if (llimit > 0 && history.size() > llimit) { 120 | history.remove(0); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/dialogs/parts/TimeAndSnooze.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.dialogs.parts; 7 | 8 | import java.awt.Container; 9 | import javax.swing.JLabel; 10 | import javax.swing.JSpinner; 11 | import javax.swing.JTextField; 12 | import javax.swing.SpinnerNumberModel; 13 | import javax.swing.event.DocumentEvent; 14 | import javax.swing.event.DocumentListener; 15 | import org.fbb.board.Translator; 16 | import org.fbb.board.internals.training.BoulderCalc; 17 | 18 | /** 19 | * 20 | * @author jvanek 21 | */ 22 | public class TimeAndSnooze { 23 | 24 | private final JSpinner delay; 25 | private final JTextField snooze; 26 | private final JLabel time; 27 | private final Changer ch; 28 | private final Snoozer snoozer; 29 | private final Runnable reset; 30 | private final Runnable mainPaint; 31 | private final Thread runner; 32 | 33 | private volatile boolean alive = true; 34 | 35 | public TimeAndSnooze(Container parent, Runnable snoozAction, Runnable resetAction, Runnable paint) { 36 | parent.add(new JLabel(Translator.R("delay"))); 37 | delay = new JSpinner(new SpinnerNumberModel(1.5, 0.2, 10, 0.2)); 38 | parent.add(delay); 39 | parent.add(new JLabel(Translator.R("snooze"))); 40 | snooze = new JTextField("00:00"); 41 | ch = new Changer(); 42 | snooze.getDocument().addDocumentListener(ch); 43 | parent.add(snooze); 44 | parent.add(new JLabel(" mm:ss :")); 45 | time = new JLabel("0:0"); 46 | time.setFont(time.getFont().deriveFont(time.getFont().getSize() * 3f)); 47 | parent.add(time); 48 | snoozer = new Snoozer(new Runnable() { 49 | @Override 50 | public void run() { 51 | snoozer.setSnoozeCounter(-2000/*very aprox time of rendering below*/); 52 | snoozAction.run(); 53 | } 54 | }); 55 | reset = resetAction; 56 | mainPaint = paint; 57 | runner = new Thread(new Runnable() { 58 | @Override 59 | public void run() { 60 | reset.run(); 61 | while (alive) { 62 | try { 63 | Thread.sleep(getDelay()); 64 | snoozer.checkAndDo(getDelay(), time, snooze); 65 | mainPaint.run(); 66 | } catch (InterruptedException ex) { 67 | //nooo! 68 | } 69 | } 70 | } 71 | }); 72 | ch.work(); 73 | } 74 | 75 | public void start() { 76 | runner.start(); 77 | } 78 | 79 | public void stop() { 80 | alive = false; 81 | } 82 | 83 | private class Changer implements DocumentListener { 84 | 85 | @Override 86 | public void insertUpdate(DocumentEvent e) { 87 | work(); 88 | } 89 | 90 | @Override 91 | public void removeUpdate(DocumentEvent e) { 92 | work(); 93 | } 94 | 95 | @Override 96 | public void changedUpdate(DocumentEvent e) { 97 | work(); 98 | } 99 | 100 | private void work() { 101 | snoozer.setSnoozeCounter((long) BoulderCalc.decode(snooze.getText()) * 1000l); 102 | reset.run(); 103 | } 104 | 105 | } 106 | 107 | public void doAsOnChange() { 108 | ch.work(); 109 | } 110 | 111 | private long getDelay() { 112 | return (long) (((Double) delay.getValue()) * 1000d); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/training/TrainingPlaylist.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.training; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.IOException; 12 | import java.io.InputStreamReader; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import org.fbb.board.desktop.Files; 16 | 17 | /** 18 | * 19 | * @author jvanek 20 | */ 21 | public class TrainingPlaylist { 22 | 23 | private final List data; 24 | 25 | private TrainingPlaylist(List r) { 26 | data = r; 27 | } 28 | 29 | private static class TrainingWithStartTime { 30 | 31 | private final Training t; 32 | private final int seconds; 33 | private final String name; 34 | 35 | public TrainingWithStartTime(Training t, int seconds, String name) { 36 | this.t = t; 37 | this.seconds = seconds; 38 | this.name = name; 39 | } 40 | } 41 | 42 | public List toTrainings() { 43 | List r = new ArrayList<>(data.size()); 44 | for (TrainingWithStartTime t : data) { 45 | Training tt = new Training(t.t.innerSettings.random, t.t.innerSettings.regular, t.t.innerSettings.jumps, t.t.innerSettings.tOfBoulder, t.t.innerSettings.tOfTraining, t.t.innerSettings.numOfBoulders, t.t.filter, t.t.currentName); 46 | r.add(tt); 47 | } 48 | return r; 49 | } 50 | 51 | public int getPauseForTraing(int i) { 52 | return data.get(i).seconds; 53 | } 54 | 55 | public String getTitleForTraing(int i) { 56 | return data.get(i).name; 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | StringBuilder sb = new StringBuilder("Trainings in list: " + data.size() + "\n"); 62 | int i = 0; 63 | for (TrainingWithStartTime t : data) { 64 | i++; 65 | sb.append(i).append(")").append(t.name).append(", init delay: ").append(BoulderCalc.code(t.seconds)).append("\n"); 66 | 67 | } 68 | sb.append("\n*** Details *** \n"); 69 | i = 0; 70 | int total1 = 0; 71 | int total2 = 0; 72 | for (TrainingWithStartTime t : data) { 73 | i++; 74 | sb.append(i).append(") pause: ").append(BoulderCalc.code(t.seconds)).append(", "); 75 | sb.append(t.name).append("/").append(t.t.toString()); 76 | sb.append("\n-----------\n"); 77 | total1 += t.seconds + BoulderCalc.decode(t.t.innerSettings.tOfTraining); 78 | total2 += BoulderCalc.decode(t.t.innerSettings.tOfTraining); 79 | } 80 | sb.append("total: ").append(BoulderCalc.code(total1)).append("(").append(BoulderCalc.code(total2)).append(" of pure workload)" + "\n"); 81 | return sb.toString(); 82 | } 83 | 84 | public static TrainingPlaylist loadSavedTraining(File selectedFile) throws IOException { 85 | List r = new ArrayList<>(); 86 | try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(selectedFile), "utf-8"))) { 87 | while (true) { 88 | String s = br.readLine(); 89 | if (s == null) { 90 | break; 91 | } 92 | String[] src = s.split("/"); 93 | if (src.length == 1) { 94 | r.add(new TrainingWithStartTime(Training.loadSavedTraining(Files.getTraining(src[0])), 0, src[0])); 95 | } else { 96 | int ts = BoulderCalc.decode(src[0]); 97 | r.add(new TrainingWithStartTime(Training.loadSavedTraining(Files.getTraining(src[1])), ts, src[1])); 98 | } 99 | } 100 | } 101 | return new TrainingPlaylist(r); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/comm/wired/PortWork.java: -------------------------------------------------------------------------------- 1 | /* 2 | sudo usermod -aG dialout jvanek 3 | sudo usermod -aG uucp jvanek 4 | sudo usermod -aG lock jvanek 5 | sudo chown root:uucp /run/lock 6 | sudo chmod 775 /run/lock 7 | 8 | */ 9 | package org.fbb.board.internals.comm.wired; 10 | 11 | import com.fazecast.jSerialComm.SerialPort; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | import org.fbb.board.internals.GuiLogHelper; 15 | import org.fbb.board.internals.comm.ConnectionID; 16 | import org.fbb.board.internals.comm.ListAndWrite; 17 | 18 | /** 19 | * 20 | * @author jvanek 21 | */ 22 | public class PortWork implements ListAndWrite { 23 | 24 | @Override 25 | public void writeToDevice(String id, byte[]... b) { 26 | writeTo(id, b); 27 | } 28 | 29 | @Override 30 | public ConnectionID[] listDevices() { 31 | return list(); 32 | } 33 | 34 | //9600 is super safe 35 | private static final int BAUD_RATE = 38400; 36 | 37 | public static void main(String... s) throws Exception { 38 | ConnectionID[] a = list(); 39 | for (ConnectionID a1 : a) { 40 | GuiLogHelper.guiLogger.logo(a1.toString()); 41 | 42 | } 43 | byte[] testData = new byte[]{ 44 | (byte) 5, 0, 0, 45 | (byte) 5, 0, 0, 46 | (byte) 0, 5, 0, 47 | (byte) 0, 5, 0, 48 | (byte) 0, 0, 5, 49 | (byte) 0, 0, 5, 50 | (byte) 5, 5, 0, 51 | (byte) 5, 5, 0, 52 | (byte) 5, 0, 5, 53 | (byte) 5, 0, 5, 54 | (byte) 0, 5, 5, 55 | (byte) 0, 5, 5, 56 | (byte) 5, 5, 5 57 | }; 58 | writeTo("ttyUSB0", testData); 59 | 60 | } 61 | 62 | private static void writeTo(String port, byte[]... bytes) { 63 | int u = getUsage(port); 64 | for (int i = 0; i < u; i++) { 65 | writeToImpl(SerialPort.getCommPort(port), bytes); 66 | } 67 | } 68 | 69 | private static void writeTo(SerialPort port, byte[]... bytes) { 70 | int u = getUsage(port.getSystemPortName()); 71 | for (int i = 0; i < u; i++) { 72 | writeToImpl(port, bytes); 73 | } 74 | } 75 | 76 | private static void writeToImpl(SerialPort comPort, byte[]... b) { 77 | 78 | comPort.setBaudRate(BAUD_RATE); 79 | try { 80 | comPort.openPort(); 81 | Thread.sleep(100); 82 | for (byte[] byteArray : b) { 83 | writeByteByByte(byteArray, comPort); 84 | GuiLogHelper.guiLogger.logo("[wired]written - " + byteArray.length); 85 | } 86 | GuiLogHelper.guiLogger.logo("[wired]written - end - " + b.length); 87 | } catch (Exception e) { 88 | GuiLogHelper.guiLogger.loge(e); 89 | } 90 | comPort.closePort(); 91 | } 92 | 93 | public static void writeByteByByte(byte[] byteArray, SerialPort comPort) throws InterruptedException { 94 | for (int i = 0; i < byteArray.length; i++) { 95 | byte c = byteArray[i]; //arduino have very small buffer (so no need to send byte by byte until 64bytes, but this should serve for some 400 96 | comPort.writeBytes(new byte[]{c}, 1); 97 | Thread.sleep(2);//we sleep 1 in arduino 98 | } 99 | } 100 | 101 | private static Set usages = new HashSet<>(); 102 | 103 | //First usage of each port is lost 104 | private static int getUsage(String port) { 105 | if (usages.contains(port)) { 106 | return 1; 107 | } else { 108 | usages.add(port); 109 | return 2; 110 | } 111 | } 112 | 113 | private static ConnectionID[] list() { 114 | SerialPort[] ports = SerialPort.getCommPorts(); 115 | ConnectionID[] a = new ConnectionID[ports.length]; 116 | for (int i = 0; i < ports.length; i++) { 117 | SerialPort port = ports[i]; 118 | a[i] = new ConnectionID(port.getSystemPortName(), port.getDescriptivePortName()); 119 | } 120 | return a; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/TeeOutputStream.java: -------------------------------------------------------------------------------- 1 | /* TeeOutputStream.java 2 | Copyright (C) 2010 Red Hat 3 | 4 | This file is part of IcedTea. 5 | 6 | IcedTea is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2, or (at your option) 9 | any later version. 10 | 11 | IcedTea is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with IcedTea; see the file COPYING. If not, write to the 18 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 | 02110-1301 USA. 20 | 21 | Linking this library statically or dynamically with other modules is 22 | making a combined work based on this library. Thus, the terms and 23 | conditions of the GNU General Public License cover the whole 24 | combination. 25 | 26 | As a special exception, the copyright holders of this library give you 27 | permission to link this library with independent modules to produce an 28 | executable, regardless of the license terms of these independent 29 | modules, and to copy and distribute the resulting executable under 30 | terms of your choice, provided that you also meet, for each linked 31 | independent module, the terms and conditions of the license of that 32 | module. An independent module is a module which is not derived from 33 | or based on this library. If you modify this library, you may extend 34 | this exception to your version of the library, but you are not 35 | obligated to do so. If you do not wish to do so, delete this 36 | exception statement from your version. */ 37 | package org.fbb.board.internals; 38 | 39 | import java.io.ByteArrayOutputStream; 40 | import java.io.File; 41 | import java.io.PrintStream; 42 | 43 | /** 44 | * Behaves like the 'tee' command, sends output to both actual std stream and a 45 | * log 46 | */ 47 | public final class TeeOutputStream extends PrintStream { 48 | 49 | // Everthing written to TeeOutputStream is written to our log too 50 | private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 51 | private final boolean isError; 52 | 53 | public TeeOutputStream(PrintStream stdStream, boolean isError) { 54 | super(stdStream); 55 | this.isError = isError; 56 | } 57 | 58 | @Override 59 | public void close() { 60 | flushLog(); 61 | super.close(); 62 | } 63 | 64 | @Override 65 | public void flush() { 66 | flushLog(); 67 | super.flush(); 68 | } 69 | 70 | /* 71 | * The big ones: these do the actual writing 72 | */ 73 | @Override 74 | public synchronized void write(byte[] b, int off, int len) { 75 | if (len == 0) { 76 | return; 77 | } 78 | appendByteArray(b, off, len); 79 | super.write(b, off, len); 80 | } 81 | 82 | @Override 83 | public synchronized void write(int b) { 84 | appendByte(b); 85 | super.write(b); 86 | } 87 | 88 | private void flushLog() { 89 | String s = byteArrayOutputStream.toString(); 90 | if (s.length() > 0) { 91 | log(s); 92 | byteArrayOutputStream.reset(); 93 | } 94 | } 95 | 96 | public void log(String s) { 97 | GuiLogHelper.guiLogger.appendInternal(s, isError); 98 | } 99 | 100 | public boolean isError() { 101 | return isError; 102 | } 103 | 104 | private void appendByte(int b) { 105 | byteArrayOutputStream.write(b); 106 | String s = byteArrayOutputStream.toString(); 107 | if (s.endsWith(getLineSeparator())) { 108 | flushLog(); 109 | } 110 | } 111 | 112 | private void appendByteArray(byte[] b, int off, int len) { 113 | byteArrayOutputStream.write(b, off, len); 114 | String s = new String(b, off, len); 115 | if (s.endsWith(getLineSeparator())) { 116 | flushLog(); 117 | } 118 | } 119 | 120 | public static String getLineSeparator() { 121 | return File.separator; 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /system_configs/raspbian_buster/home/app-data/FlashFreeBoardDesktop-1.0-SNAPSHOT-jar-with-dependencies: -------------------------------------------------------------------------------- 1 | sudo apt install vim 2 | sudo apt install avahi 3 | sudo apt install avahi-utils 4 | apt-get install xorg 5 | sudo apt-get install xorg 6 | sudo apt-get install fluxbox onboard 7 | sudo mv /usr/bin/xterm /usr/bin/xterm-backup #as xterm is in default menu 8 | 9 | sudo apt install dconf #onbaords depndence 10 | sudo apt-get install matchbox-keyboard # good one 11 | sudo apt-get install espeak 12 | sudo apt-get install pulseaudio 13 | sudo apt-get install alza 14 | sudo apt-get install alsa-mixer #audio enablment 15 | sudo apt install sox # to enable fallback if application, run as systemd service fails to play directly form jdk 16 | # maybe also ad duser to audio and other groups? 17 | 18 | sudo apt install openjdk-8-jdk 19 | sudo apt install xinput #rotation depndence 20 | sudo apt install espeak 21 | sudo apt install florence #buggy 22 | sudo apt install dconf-gsettings-backend #onboard depndence 23 | sudo apt install dconf-gsettings-backend #onboard depndence 24 | sudo apt install dconf-tools #onboard depndence 25 | sudo apt install dconf-editor #onboard depndence 26 | sudo apt install dbus-x11 #onboard depndence 27 | 28 | NOPE!: sudo usermod -aG dialout app 29 | NOPE!: sudo usermod -aG uucp app 30 | NOPE!: sudo su: echo -n pass | sha256sum > /etc/FBB.auth 31 | 32 | .fluxbox/init: session.screen0.workspaces: 1 33 | /etc/lightdm/lightdm.conf: autologin-user=app 34 | in raspi-config add cs_CZ locale 35 | 36 | sudo vim /boot/cmdline.txt to add consoleblank=0 to prevent screen dimm 37 | 38 | usermod -s //usr/sbin/nologin app 39 | 40 | # add user to run the app - it is different user then the one logged into system 41 | useradd app-data 42 | mkdir /home/app-data 43 | chown app-data:app-data /home/app-data 44 | 45 | # undo: 46 | #sudo usermod -aG dialout app 47 | #sudo usermod -aG uucp app 48 | #sudo su: echo -n pass | sha256sum > /etc/FBB.auth 49 | deluser app dialout 50 | deluser app uucp 51 | rm /etc/FBB.auth 52 | # use as app-data 53 | echo -n pass | sha256sum > /home/app-data/.config/FlashBoard/FFB.auth # as here it do not need to be world readable 54 | chmod 600 /home/app-data/.config/FlashBoard/FFB.auth 55 | 56 | sudo usermod -aG dialout app-data 57 | sudo usermod -aG uucp app-data 58 | 59 | uucp:x:10:app,app-data 60 | dialout:x:20:pi,app,app-data 61 | audio:x:29:pi,pulse,app,app-data 62 | video:x:44:pi,app,app-data 63 | users:x:100:pi,app,app-data 64 | input:x:105:pi,app,app-data 65 | netdev:x:109:pi,app,app-data 66 | spi:x:999:pi,app,app-data 67 | i2c:x:998:pi,app,app-data 68 | gpio:x:997:pi,app,app-data 69 | app:x:1004: 70 | app-data:x:1005: 71 | 72 | # at the end... 73 | # usermod -s //usr/sbin/nologin app-data 74 | # usermod -s //usr/sbin/nologin app 75 | usermod -s /bin/bash app-data 76 | usermod -s /bin/bash app 77 | 78 | sudo apt install arduino # to get autoupdate for hw too 79 | 80 | 81 | # tune onboard to not mess with system 82 | #keyboard->advanced->long press action == key repeat 83 | #keyboard->advanced->touch inmpu == single touch ##ps do not set this to none!!! wills top to work on touchscreen! How did they did it? 84 | 85 | # making dconf read only led to reseting onboard aprox every three seconds :( 86 | # -r--r--r-- 1 jvanek mock 32906 Dec 16 16:57 ~/.config/dconf/user 87 | # dr-sr-sr-x. 2 jvanek mock 4096 Dec 16 16:57 ~/.config/dconf/ 88 | 89 | #so better to use the services, with timing aroun 10 minutes, or desktop icon 90 | 91 | #--------last image 92 | 93 | apt install x11vnc 94 | #? apt remove x11vnc 95 | su app 96 | x11app 97 | vncviewer freeboard.local:0 SecurityTypes=None 98 | 99 | # usbmount pkg is broken on rpi3 100 | apt install usbmount # to fetch deps 101 | # based on https://www.linuxuprising.com/2019/04/automatically-mount-usb-drives-on.html 102 | sudo apt install git 103 | git clone https://github.com/rbrito/usbmount 104 | sudo apt install debhelper build-essential 105 | cd usbmount 106 | dpkg-buildpackage -us -uc -b 107 | apt install ./*usbmount*.deb 108 | # https://raspberrypi.stackexchange.com/questions/100312/raspberry-4-usbmount-not-working 109 | vim /lib/systemd/system/systemd-udevd.service 110 | change: 111 | PrivateMounts=yes 112 | to 113 | PrivateMounts=no 114 | 115 | 116 | # no X afer raspbian update? Set manually resolution via raspi-config 117 | usermod -s //usr/sbin/nologin app-data 118 | usermod -s //usr/sbin/nologin app 119 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/Translator.java: -------------------------------------------------------------------------------- 1 | package org.fbb.board; 2 | 3 | import java.text.MessageFormat; 4 | import java.util.Locale; 5 | import java.util.MissingResourceException; 6 | import java.util.ResourceBundle; 7 | import org.fbb.board.internals.GuiLogHelper; 8 | 9 | /** 10 | * Utility class to provide simple methods to help localize messages 11 | */ 12 | public class Translator { 13 | 14 | public String getLocale() { 15 | String s = this.resources.getLocale().toString(); 16 | //GuiLogHelper.guiLogger.logo(s); 17 | return s; 18 | } 19 | 20 | private void reload(String locale) { 21 | try { 22 | if (locale == null || locale.trim().isEmpty()) { 23 | resources = ResourceBundle.getBundle(defaultBundle); 24 | } else { 25 | resources = ResourceBundle.getBundle(defaultBundle, new Locale(locale)); 26 | } 27 | } catch (Exception ex) { 28 | throw new IllegalStateException("No bundles found for Locale: " + Locale.getDefault().toString() 29 | + "and missing base resource bundle in jar:\\" + defaultBundle + "/Messages.properties"); 30 | } 31 | } 32 | 33 | private static class TranslatorHolder { 34 | 35 | //https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java 36 | //https://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom 37 | private static final Translator INSTANCE = new Translator(); 38 | 39 | private static Translator getTransaltor() { 40 | return TranslatorHolder.INSTANCE; 41 | } 42 | } 43 | 44 | /** 45 | * the localized resource strings 46 | */ 47 | private ResourceBundle resources; 48 | private static final String defaultBundle = "org.fbb.board.locales.bundles.Messages"; 49 | 50 | Translator() { 51 | this(defaultBundle); 52 | } 53 | 54 | Translator(String s) { 55 | try { 56 | resources = ResourceBundle.getBundle(s); 57 | this.reload(getLocale()); 58 | } catch (Exception ex) { 59 | GuiLogHelper.guiLogger.loge(ex); 60 | throw new IllegalStateException("No bundles found for Locale: " + Locale.getDefault().toString() 61 | + "and missing base resource bundle in jar:\\" + s + "/Messages.properties"); 62 | } 63 | } 64 | 65 | public static Translator getInstance() { 66 | return TranslatorHolder.getTransaltor(); 67 | } 68 | 69 | public static void load(String locale) { 70 | TranslatorHolder.getTransaltor().reload(locale); 71 | } 72 | 73 | /** 74 | * Return a translated (localized) version of the message 75 | * 76 | * @param message the message to translate 77 | * @return a string representing the localized message 78 | */ 79 | public static String R(String message) { 80 | return R(message, new Object[0]); 81 | } 82 | 83 | /** 84 | * @param message key to be found in properties 85 | * @param params params to be expanded to message 86 | * @return the localized string for the message 87 | */ 88 | public static String R(String message, Object... params) { 89 | return getInstance().getMessage(message, params); 90 | } 91 | 92 | /** 93 | * @return the localized resource string using the specified arguments. 94 | * @param key key to be found in properties 95 | * @param args params to be expanded to message 96 | */ 97 | protected String getMessage(String key, Object... args) { 98 | return MessageFormat.format(getMessage(key), args); 99 | } 100 | 101 | /** 102 | * Returns the localized resource string identified by the specified key. If 103 | * the message is empty, a null is returned. 104 | */ 105 | private String getMessage(String key) { 106 | try { 107 | String result = resources.getString(key); 108 | if (result.length() == 0) { 109 | return ""; 110 | } else { 111 | return result; 112 | } 113 | } catch (NullPointerException e) { 114 | return getMessage("RNoResource", new Object[]{key}); 115 | } catch (MissingResourceException e) { 116 | return multiCatchTool(key); 117 | } catch (ClassCastException ee) { 118 | return multiCatchTool(key); 119 | } 120 | } 121 | 122 | private String multiCatchTool(String key) { 123 | if ("RNoResource".equals(key)) { 124 | return "No localized text found"; 125 | } else { 126 | return getMessage("RNoResource", new Object[]{key}); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/awtimpl/WinUtils.java: -------------------------------------------------------------------------------- 1 | package org.fbb.board.desktop.gui.awtimpl; 2 | 3 | import org.fbb.board.desktop.gui.*; 4 | import java.awt.Rectangle; 5 | import java.awt.Toolkit; 6 | import java.awt.Window; 7 | import java.awt.event.ActionEvent; 8 | import java.awt.event.ActionListener; 9 | import java.awt.image.BufferedImage; 10 | import java.net.URL; 11 | import javax.swing.JDialog; 12 | import javax.swing.JEditorPane; 13 | import javax.swing.JFrame; 14 | import javax.swing.JOptionPane; 15 | import org.fbb.board.Translator; 16 | import org.fbb.board.desktop.ScreenFinder; 17 | import org.fbb.board.internals.GlobalSettings; 18 | import org.fbb.board.internals.GuiLogHelper; 19 | 20 | /** 21 | * 22 | * @author jvanek 23 | */ 24 | public class WinUtils { 25 | 26 | public static double getIdealWindowSizw(BufferedImage bis) { 27 | Rectangle size = ScreenFinder.getCurrentScreenSizeWithoutBounds(); 28 | double dw = (double) size.width / (double) bis.getWidth(); 29 | double dh = (double) size.height / (double) bis.getHeight(); 30 | double ratio = Math.min(dw, dh); 31 | ratio = ratio * MainWindow.gs.getRatio(); 32 | return ratio; 33 | } 34 | 35 | public static void setIdealWindowLocation(Window w) { 36 | GlobalSettings gs = MainWindow.gs; 37 | int he = gs.getHardcodedEdge(); 38 | if (gs.getLocation().equalsIgnoreCase("TR")) { 39 | w.setLocation( 40 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() - w.getWidth() - he), 41 | (int) (he)); 42 | } else if (gs.getLocation().equalsIgnoreCase("TL")) { 43 | w.setLocation( 44 | (int) (he), 45 | (int) (he)); 46 | } else if (gs.getLocation().equalsIgnoreCase("BR")) { 47 | w.setLocation( 48 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() - w.getWidth() - he), 49 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() - w.getHeight()) - he); 50 | } else if (gs.getLocation().equalsIgnoreCase("BL")) { 51 | w.setLocation( 52 | (int) (he), 53 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() - w.getHeight()) - he); 54 | 55 | } else if (gs.getLocation().equalsIgnoreCase("T")) { 56 | w.setLocation( 57 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() - w.getWidth()) / 2, 58 | (int) (he)); 59 | } else if (gs.getLocation().equalsIgnoreCase("B")) { 60 | w.setLocation( 61 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() - w.getWidth()) / 2, 62 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() - w.getHeight()) - he); 63 | } else if (gs.getLocation().equalsIgnoreCase("R")) { 64 | w.setLocation( 65 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() - w.getWidth() - he), 66 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() - w.getHeight()) / 2); 67 | } else if (gs.getLocation().equalsIgnoreCase("L")) { 68 | w.setLocation( 69 | (int) (he), 70 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() - w.getHeight()) / 2); 71 | } else { 72 | w.setLocation( 73 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() - w.getWidth()) / 2, 74 | (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() - w.getHeight()) / 2); 75 | } 76 | } 77 | 78 | public static String addCtrLine(String string) { 79 | return "" + string + "
    " + Translator.R("tryCtrl"); 80 | } 81 | 82 | public static class ShowWebHelp implements ActionListener { 83 | 84 | @Override 85 | public void actionPerformed(ActionEvent ae) { 86 | try { 87 | JDialog jd = new JDialog(); 88 | jd.setModal(true); 89 | jd.setSize(ScreenFinder.getCurrentScreenSizeWithoutBounds().getSize()); 90 | JEditorPane text = new JEditorPane(new URL("https://raw.githubusercontent.com/judovana/FlashFreeBoardDesktop/master/README.md")); 91 | jd.add(text); 92 | jd.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 93 | jd.setVisible(true); 94 | } catch (Exception ex) { 95 | GuiLogHelper.guiLogger.loge(ex); 96 | JOptionPane.showMessageDialog(null, ex); 97 | } 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/dialogs/LogView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.dialogs; 7 | 8 | import java.awt.BorderLayout; 9 | import java.awt.GridLayout; 10 | import java.awt.HeadlessException; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.ActionListener; 13 | import java.awt.event.WindowAdapter; 14 | import java.awt.event.WindowEvent; 15 | import javax.swing.JButton; 16 | import javax.swing.JCheckBox; 17 | import javax.swing.JDialog; 18 | import javax.swing.JFrame; 19 | import javax.swing.JPanel; 20 | import javax.swing.JScrollPane; 21 | import javax.swing.JSplitPane; 22 | import javax.swing.JTextArea; 23 | import org.fbb.board.internals.db.DB; 24 | import org.fbb.board.internals.GuiLogHelper; 25 | 26 | /** 27 | * 28 | * @author jvanek 29 | */ 30 | public class LogView extends JDialog { 31 | 32 | private final DB db; 33 | 34 | private class Autorefrsh extends Thread { 35 | 36 | private boolean alive = true; 37 | private final JTextArea o; 38 | private final JTextArea e; 39 | private final JCheckBox r; 40 | 41 | private Autorefrsh(JTextArea o, JTextArea e, JCheckBox r) { 42 | this.o = o; 43 | this.e = e; 44 | this.r = r; 45 | } 46 | 47 | @Override 48 | public void run() { 49 | while (alive) { 50 | try { 51 | if (r.isSelected()) { 52 | e.setText(GuiLogHelper.guiLogger.getSerr()); 53 | e.setCaretPosition(e.getDocument().getLength()); 54 | o.setText(GuiLogHelper.guiLogger.getSout()); 55 | o.setCaretPosition(o.getDocument().getLength()); 56 | } 57 | Thread.sleep(500); 58 | } catch (Exception ex) { 59 | GuiLogHelper.guiLogger.loge(ex); 60 | } 61 | } 62 | } 63 | 64 | } 65 | 66 | public LogView(DB ddb) throws HeadlessException { 67 | this(ddb, false); 68 | } 69 | public LogView(DB ddb, boolean modal) throws HeadlessException { 70 | this.db = ddb; 71 | this.setModal(modal); 72 | this.setLayout(new BorderLayout()); 73 | JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); 74 | JTextArea e = new JTextArea(); 75 | e.setText(GuiLogHelper.guiLogger.getSerr()); 76 | JTextArea o = new JTextArea(); 77 | o.setText(GuiLogHelper.guiLogger.getSout()); 78 | split.add(new JScrollPane(o)); 79 | split.add(new JScrollPane(e)); 80 | this.add(split); 81 | JPanel tools = new JPanel(new GridLayout(1, 3)); 82 | JButton or = new JButton("refresh"); 83 | or.addActionListener(new ActionListener() { 84 | 85 | @Override 86 | public void actionPerformed(ActionEvent x) { 87 | o.setText(GuiLogHelper.guiLogger.getSout()); 88 | } 89 | }); 90 | tools.add(or); 91 | final JCheckBox autor = new JCheckBox("autorefresh", true); 92 | tools.add(autor); 93 | JButton log = new JButton("db log"); 94 | tools.add(log); 95 | log.addActionListener(new ActionListener() { 96 | 97 | @Override 98 | public void actionPerformed(ActionEvent e) { 99 | autor.setSelected(false); 100 | o.setText(o.getText() + "\n" + db.logCatched()); 101 | } 102 | }); 103 | JButton er = new JButton("refresh"); 104 | er.addActionListener(new ActionListener() { 105 | 106 | @Override 107 | public void actionPerformed(ActionEvent x) { 108 | e.setText(GuiLogHelper.guiLogger.getSerr()); 109 | } 110 | }); 111 | tools.add(er); 112 | this.add(tools, BorderLayout.SOUTH); 113 | this.pack(); 114 | split.setDividerLocation(400); 115 | this.setSize(800, 800); 116 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 117 | final Autorefrsh autorefresh = new Autorefrsh(o, e, autor); 118 | this.addWindowListener(new WindowAdapter() { 119 | 120 | @Override 121 | public void windowClosed(WindowEvent e) { 122 | try { 123 | super.windowClosed(e); 124 | } finally { 125 | autorefresh.alive = false; 126 | } 127 | 128 | } 129 | 130 | }); 131 | autorefresh.start(); 132 | 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/Authenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui; 7 | 8 | import java.awt.GridLayout; 9 | import java.nio.charset.StandardCharsets; 10 | import java.security.MessageDigest; 11 | import java.security.NoSuchAlgorithmException; 12 | import javax.swing.JCheckBox; 13 | import javax.swing.JLabel; 14 | import javax.swing.JOptionPane; 15 | import javax.swing.JPanel; 16 | import javax.swing.JPasswordField; 17 | import org.fbb.board.Translator; 18 | import org.fbb.board.desktop.Files; 19 | 20 | /** 21 | * 22 | * @author jvanek 23 | */ 24 | public class Authenticator { 25 | 26 | private boolean pernament = false; 27 | public static final Authenticator auth = new Authenticator(); 28 | 29 | public boolean isPernament() { 30 | return pernament || Files.getAuthFileHash() == null; 31 | } 32 | 33 | public void authenticate(String message) throws AuthoriseException { 34 | try { 35 | if (!authenticateImpl(message)) { 36 | throw new AuthoriseException(); 37 | } 38 | } catch (Exception ex) { 39 | throw new AuthoriseException(ex); 40 | } 41 | } 42 | 43 | private boolean authenticateImpl(String message) throws NoSuchAlgorithmException { 44 | String hash = Files.getAuthFileHash(); 45 | if (hash == null) { 46 | //warning security by obscurity! 47 | //if the file do not exists, app is not "secured" at all 48 | //note, that all the config files are plain an readable in ~/.config anyway 49 | //to secure them, encrypt them with locekd pass 50 | //if app is run as limited user, then the security should be ok 51 | return true; 52 | } 53 | if (pernament) { 54 | return true; 55 | } 56 | Object[] r = new AuthoriseDialog().show(message); 57 | if (r == null || r.length != 2) { 58 | return false; 59 | } 60 | String s = new String((char[]) (r[0])); 61 | if (s == null || s.trim().isEmpty()) { 62 | return false; 63 | } 64 | 65 | MessageDigest md = MessageDigest.getInstance("SHA-256"); 66 | md.update(s.getBytes(StandardCharsets.UTF_8)); 67 | byte[] passHash = md.digest(); 68 | //to nice (correct) string 69 | StringBuilder sb = new StringBuilder(); 70 | for (byte b : passHash) { 71 | sb.append(String.format("%02x", b)); 72 | } 73 | if (hash.equals(sb.toString())) { 74 | if ((boolean) r[1]) { 75 | pernament = true; 76 | } 77 | return true; 78 | } 79 | return false; 80 | } 81 | 82 | public void revoke() { 83 | pernament = false; 84 | } 85 | 86 | public String[] getStatus() { 87 | if (Files.getAuthFileHash() == null) { 88 | return new String[]{ 89 | Translator.R("secNone"), 90 | "use `su " + System.getProperty("user.name") + "; echo -n your_pass | sha256sum > " + Files.localAuth, 91 | " or `su ; echo -n your_pass | sha256sum > " + Files.masterAuth+ " && chmod 600 it"}; 92 | } 93 | if (isPernament()) { 94 | return new String[]{ 95 | Translator.R("secUnlocked"), 96 | "To lock, again, use: ", 97 | Translator.R("revokePP")}; 98 | } 99 | return new String[]{Translator.R("secOk"), "", ""}; 100 | } 101 | 102 | private static class AuthoriseDialog { 103 | 104 | public static Object[] show(String message) { 105 | JPasswordField pass = new JPasswordField(5); 106 | JCheckBox save = new JCheckBox(Translator.R("keepLoged"), false); 107 | 108 | JPanel myPanel = new JPanel(new GridLayout(3, 1)); 109 | myPanel.add(new JLabel(message)); 110 | myPanel.add(pass); 111 | myPanel.add(save); 112 | int result = JOptionPane.showConfirmDialog(null, myPanel, Translator.R("autenticate"), JOptionPane.OK_CANCEL_OPTION); 113 | if (result == JOptionPane.OK_OPTION) { 114 | return new Object[]{pass.getPassword(), save.isSelected()}; 115 | } 116 | return null; 117 | } 118 | } 119 | 120 | public static class AuthoriseException extends Exception { 121 | 122 | public AuthoriseException(String s) { 123 | super(s); 124 | } 125 | 126 | public AuthoriseException() { 127 | this(Translator.R("authFail")); 128 | } 129 | 130 | private AuthoriseException(Exception ex) { 131 | super(ex); 132 | } 133 | 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/ScreenFinder.java: -------------------------------------------------------------------------------- 1 | /* ScreenFinder.java 2 | Copyright (C) 2012 Red Hat, Inc. 3 | 4 | This file is part of IcedTea. 5 | 6 | IcedTea is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2, or (at your option) 9 | any later version. 10 | 11 | IcedTea is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with IcedTea; see the file COPYING. If not, write to the 18 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 | 02110-1301 USA. 20 | 21 | Linking this library statically or dynamically with other modules is 22 | making a combined work based on this library. Thus, the terms and 23 | conditions of the GNU General Public License cover the whole 24 | combination. 25 | 26 | As a special exception, the copyright holders of this library give you 27 | permission to link this library with independent modules to produce an 28 | executable, regardless of the license terms of these independent 29 | modules, and to copy and distribute the resulting executable under 30 | terms of your choice, provided that you also meet, for each linked 31 | independent module, the terms and conditions of the license of that 32 | module. An independent module is a module which is not derived from 33 | or based on this library. If you modify this library, you may extend 34 | this exception to your version of the library, but you are not 35 | obligated to do so. If you do not wish to do so, delete this 36 | exception statement from your version. */ 37 | package org.fbb.board.desktop; 38 | 39 | import java.awt.GraphicsConfiguration; 40 | import java.awt.GraphicsDevice; 41 | import java.awt.GraphicsEnvironment; 42 | import java.awt.HeadlessException; 43 | import java.awt.Insets; 44 | import java.awt.MouseInfo; 45 | import java.awt.Point; 46 | import java.awt.Rectangle; 47 | import java.awt.Toolkit; 48 | import java.awt.Window; 49 | import org.fbb.board.internals.GuiLogHelper; 50 | 51 | public class ScreenFinder { 52 | 53 | public static GraphicsDevice getCurrentScreen() { 54 | Point p = MouseInfo.getPointerInfo().getLocation(); 55 | return getScreenOnCoords(p); 56 | 57 | } 58 | 59 | public static Rectangle getCurrentScreenSizeWithoutBounds() { 60 | try { 61 | Point p = MouseInfo.getPointerInfo().getLocation(); 62 | return getScreenOnCoordsWithoutBounds(p); 63 | } catch (HeadlessException ex) { 64 | GuiLogHelper.guiLogger.loge(ex); 65 | return new Rectangle(800, 600); 66 | } 67 | 68 | } 69 | 70 | public static void centerWindowsToCurrentScreen(Window w) { 71 | Rectangle bounds = getCurrentScreenSizeWithoutBounds(); 72 | w.setLocation(bounds.x + (bounds.width - w.getWidth()) / 2, 73 | bounds.y + (bounds.height - w.getHeight()) / 2); 74 | 75 | } 76 | 77 | public static GraphicsDevice getScreenOnCoords(Point point) { 78 | GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment(); 79 | GraphicsDevice[] devices = e.getScreenDevices(); 80 | GraphicsDevice result = null; 81 | //now get the configuration(s) for each device 82 | for (GraphicsDevice device : devices) { 83 | //GraphicsConfiguration[] configurations = device.getConfigurations(); 84 | //or? 85 | GraphicsConfiguration[] configurations = new GraphicsConfiguration[]{device.getDefaultConfiguration()}; 86 | for (GraphicsConfiguration config : configurations) { 87 | Rectangle gcBounds = config.getBounds(); 88 | if (gcBounds.contains(point)) { 89 | result = device; 90 | } 91 | } 92 | } 93 | if (result == null) { 94 | //not found, get the default display 95 | result = e.getDefaultScreenDevice(); 96 | } 97 | return result; 98 | } 99 | 100 | public static Rectangle getScreenOnCoordsWithoutBounds(Point p) { 101 | try { 102 | GraphicsDevice device = getScreenOnCoords(p); 103 | Rectangle screenSize = device.getDefaultConfiguration().getBounds(); 104 | Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(device.getDefaultConfiguration()); 105 | return new Rectangle((int) screenSize.getX() + insets.left, (int) screenSize.getY() + insets.top, (int) screenSize.getWidth() - insets.left, (int) screenSize.getHeight() - insets.bottom); 106 | } catch (HeadlessException | IllegalArgumentException ex) { 107 | GuiLogHelper.guiLogger.loge(ex); 108 | return new Rectangle(800, 600); 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/dialogs/SlowBoulder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.dialogs; 7 | 8 | import org.fbb.board.Translator; 9 | import org.fbb.board.desktop.gui.awtimpl.MainWindowImpl; 10 | import org.fbb.board.desktop.gui.dialogs.parts.DefaultSnoozeAction; 11 | import org.fbb.board.desktop.gui.dialogs.parts.TimeAndSnooze; 12 | import org.fbb.board.internals.GuiLogHelper; 13 | import org.fbb.board.internals.grid.Boulder; 14 | import org.fbb.board.internals.grid.GridPane; 15 | 16 | import javax.swing.*; 17 | import java.awt.*; 18 | import java.awt.event.WindowAdapter; 19 | import java.awt.event.WindowEvent; 20 | 21 | /** 22 | * @author jvanek 23 | */ 24 | public class SlowBoulder extends JDialog implements Runnable { 25 | 26 | private final GridPane gp; 27 | private final TimeAndSnooze tas; 28 | private final MainWindowImpl boulders; 29 | 30 | public SlowBoulder(MainWindowImpl parent, GridPane gp) { 31 | super(parent); 32 | this.boulders = parent; 33 | this.gp = gp; 34 | this.setTitle(Translator.R("slowBoulder")); 35 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 36 | this.setModalityType(ModalityType.DOCUMENT_MODAL); 37 | this.setSize(500, 400); 38 | this.setLocationRelativeTo(parent); 39 | this.add(new JTextField(Translator.R("slowBoulderHelp")), BorderLayout.SOUTH); 40 | JPanel panel = new JPanel(); 41 | this.add(panel); 42 | panel.setLayout(new GridLayout(5, 2)); 43 | tas = new TimeAndSnooze(panel, new DefaultSnoozeAction(gp), new Runnable() { 44 | @Override 45 | public void run() { 46 | SlowBoulder.this.reset(); 47 | } 48 | }, this); 49 | tas.doAsOnChange(); 50 | this.pack(); 51 | this.setSize(this.getWidth(), 350); 52 | this.setLocationRelativeTo(parent); 53 | this.addWindowListener(new WindowAdapter() { 54 | 55 | @Override 56 | public void windowClosed(WindowEvent e) { 57 | tas.stop(); 58 | } 59 | 60 | }); 61 | tas.start(); 62 | } 63 | 64 | private Boulder currentBoulder; 65 | int finalBlinks = -6; 66 | volatile int row = finalBlinks; 67 | int[] currentBoulderGrid; 68 | 69 | @Override 70 | public void run() { 71 | try { 72 | if (row <= finalBlinks) { 73 | reset(); 74 | } 75 | row--; 76 | int[] r = findRow(); 77 | if (r == null) { 78 | if (row % 2 == 0) { 79 | gp.getGrid().clean(); 80 | } else { 81 | gp.getGrid().setBouler(currentBoulder); 82 | } 83 | } else { 84 | for (int x = 0; x < r.length; x++) { 85 | gp.getGrid().set(x, row, (byte) r[x]); 86 | } 87 | } 88 | gp.repaintAndSendToKnown(); 89 | } catch (Exception ex) { 90 | GuiLogHelper.guiLogger.loge(ex); 91 | JOptionPane.showMessageDialog(SlowBoulder.this, ex); 92 | } 93 | } 94 | 95 | private int[] findRow() { 96 | int[] currentRow = new int[gp.getGrid().getWidth()]; 97 | for (int x = 0; x < currentRow.length; x++) { 98 | currentRow[x] = 0; 99 | } 100 | while (true) { 101 | if (row < 0) { 102 | return null; 103 | } 104 | for (int x = row; x < currentBoulderGrid.length; x += gp.getGrid().getHeight()) { 105 | currentRow[x / gp.getGrid().getHeight()] = currentBoulderGrid[x]; 106 | } 107 | if (isEmpty(currentRow)) { 108 | row--; 109 | } else { 110 | return currentRow; 111 | } 112 | } 113 | 114 | } 115 | 116 | private boolean isEmpty(int[] currentRow) { 117 | for (int x = 0; x < currentRow.length; x++) { 118 | if (currentRow[x] != 0) { 119 | return false; 120 | } 121 | } 122 | return true; 123 | } 124 | 125 | 126 | private void reset() { 127 | gp.getGrid().clean(); 128 | currentBoulder = boulders.list.getRandom(); 129 | currentBoulderGrid = new int[gp.getGrid().getWidth() * gp.getGrid().getHeight()]; 130 | for (int x = 0; x < currentBoulderGrid.length; x++) { 131 | currentBoulderGrid[x] = 0; 132 | } 133 | currentBoulder.apply(currentBoulderGrid, gp.getGrid().getHeight()); 134 | boulders.setNameTextAndGrade(boulders.name, currentBoulder); 135 | boulders.hm.addToBoulderHistory(currentBoulder); 136 | boulders.next.setEnabled(boulders.hm.canFwd()); 137 | boulders.previous.setEnabled(boulders.hm.canBack()); 138 | row = gp.getGrid().getHeight(); 139 | } 140 | 141 | 142 | } 143 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | org.judovana.flash 5 | FlashFreeBoardDesktop 6 | jar 7 | 1.9-SNAPSHOT 8 | FlashFreeBoardDesktop 9 | http://maven.apache.org 10 | 11 | 1.8 12 | UTF-8 13 | ${basedir}/../ 14 | org.fbb.board.desktop.gui.MainWindow 15 | 16 | 17 | 18 | junit 19 | junit 20 | 4.13.1 21 | test 22 | 23 | 24 | 25 | commons-cli 26 | commons-cli 27 | 1.4 28 | 29 | 30 | net.sf.bluecove 31 | bluecove 32 | 2.1.0 33 | 34 | 35 | net.sf.bluecove 36 | bluecove-gpl 37 | 2.1.0 38 | 39 | 40 | com.fazecast 41 | jSerialComm 42 | 2.4.1 43 | 44 | 45 | org.eclipse.jgit 46 | org.eclipse.jgit 47 | 6.10.1.202505221210-r 48 | 49 | 50 | commons-io 51 | commons-io 52 | 2.14.0 53 | 54 | 55 | org.apache.commons 56 | commons-lang3 57 | 3.18.0 58 | 59 | 60 | org.jasypt 61 | jasypt 62 | 1.9.2 63 | 64 | 65 | 66 | com.github.lgooddatepicker 67 | LGoodDatePicker 68 | 10.3.1 69 | 70 | 71 | com.fifesoft 72 | rsyntaxtextarea 73 | 3.0.4 74 | 75 | 76 | org.jfree 77 | jfreechart 78 | 1.5.0 79 | 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-compiler-plugin 86 | 3.8.0 87 | 88 | ${jdk.version} 89 | ${jdk.version} 90 | 91 | 92 | 93 | org.apache.maven.plugins 94 | maven-jar-plugin 95 | 96 | 97 | 98 | ${freeboard.desktop.main.class} 99 | 100 | 101 | 102 | 103 | 104 | maven-assembly-plugin 105 | 2.6 106 | 107 | 108 | install 109 | 110 | single 111 | 112 | 113 | 114 | jar-with-dependencies 115 | 116 | 117 | 118 | ${freeboard.desktop.main.class} 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/grades/Grade.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.grades; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Objects; 15 | import java.util.Properties; 16 | import java.util.Vector; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | import org.fbb.board.Translator; 20 | 21 | /** 22 | * 23 | * @author jvanek 24 | */ 25 | public class Grade implements Comparable { 26 | 27 | private static String[] columns; 28 | private static final List valuesPerGrade = new ArrayList<>(100); 29 | private static final Map> valuesPerColumn = new HashMap<>(); 30 | 31 | //to be used by combobox 32 | public static Vector currentGrades() { 33 | return nice(new Vector<>(valuesPerColumn.get(usedGrades))); 34 | } 35 | 36 | public static void loadConversiontable() throws IOException { 37 | InputStream s = Grade.class.getResourceAsStream("tabulka"); 38 | Properties p = new Properties(); 39 | p.load(s); 40 | //<0 == random 41 | //>=0 grades 42 | columns = parseLine(p, "id"); 43 | for (int i = 0; i < columns.length; i++) { 44 | String column = columns[i]; 45 | valuesPerColumn.put(column, new ArrayList<>(100)); 46 | } 47 | int x = -1; 48 | while (true) { 49 | x++; 50 | String[] stringsForX = parseLine(p, x + ""); 51 | if (stringsForX == null) { 52 | break; 53 | } 54 | valuesPerGrade.add(stringsForX); 55 | for (int i = 0; i < columns.length; i++) { 56 | String column = columns[i]; 57 | valuesPerColumn.get(column).add(valuesPerGrade.get(x)[i]); 58 | } 59 | } 60 | } 61 | 62 | private static String[] parseLine(Properties p, String id) { 63 | if (p.getProperty(id) == null) { 64 | return null; 65 | } 66 | String line = p.getProperty(id).trim(); 67 | return line.split("\\s+"); 68 | } 69 | 70 | private static final int RANDOM = -1; 71 | public static String usedGrades = "fountainbleu"; 72 | private final int artificialValue; 73 | 74 | public static Grade RandomBoulder() { 75 | return new Grade(RANDOM); 76 | } 77 | 78 | public boolean isRandom() { 79 | return artificialValue < 0; 80 | } 81 | 82 | public Grade(String artificialValue) { 83 | this(Integer.valueOf(artificialValue)); 84 | } 85 | 86 | public Grade(int artificialValue) { 87 | this.artificialValue = artificialValue; 88 | } 89 | 90 | public String toAllValues(String delimiter) { 91 | if (artificialValue <= RANDOM) { 92 | return Translator.R("RandomUnknown") + delimiter; 93 | } 94 | StringBuilder sbs = new StringBuilder(); 95 | for (int i = 0; i < columns.length; i++) { 96 | String column = columns[i]; 97 | sbs.append(column).append(": ").append(getValuePerGrade(i)).append(delimiter); 98 | } 99 | return sbs.toString(); 100 | } 101 | 102 | private String getValuePerGrade(int i) { 103 | return nice(valuesPerGrade.get(artificialValue)[i]); 104 | } 105 | 106 | private String getValuesPerColumn() { 107 | return nice(valuesPerColumn.get(usedGrades).get(artificialValue)); 108 | } 109 | 110 | @Override 111 | public String toString() { 112 | if (artificialValue <= RANDOM) { 113 | return Translator.R("RandomUnknown"); 114 | } 115 | return getValuesPerColumn(); 116 | } 117 | 118 | public int toNumber() { 119 | return artificialValue; 120 | } 121 | 122 | @Override 123 | public int hashCode() { 124 | return artificialValue; 125 | } 126 | 127 | @Override 128 | public boolean equals(Object obj) { 129 | return Objects.equals(((Grade) obj).toNumber(), toNumber()); 130 | } 131 | 132 | public static String getStandardTooltip(int i) { 133 | return "" + new Grade(i).toAllValues("
    ") + "
    "; 134 | } 135 | 136 | public static int getMinGrade() { 137 | return 0; 138 | } 139 | 140 | public static int getMaxGrade() { 141 | return valuesPerGrade.size() - 1; 142 | } 143 | 144 | private static String nice(String get) { 145 | return get.replace('_', ' '); 146 | } 147 | 148 | public String shorten() { 149 | return this.toString().replaceAll("fb\\s*", ""); 150 | } 151 | 152 | private static Vector nice(Vector orig) { 153 | for (int i = 0; i < orig.size(); i++) { 154 | String get = orig.get(i); 155 | orig.set(i, nice(get)); 156 | 157 | } 158 | return orig; 159 | } 160 | 161 | @Override 162 | public int compareTo(Grade t) { 163 | return t.artificialValue - this.artificialValue; 164 | } 165 | 166 | public static class ToStringGradeWrapper extends Grade { 167 | 168 | public ToStringGradeWrapper(int i) { 169 | super(i); 170 | } 171 | 172 | Pattern p = Pattern.compile(".* "); 173 | 174 | @Override 175 | public String toString() { 176 | return p.matcher(super.toString()).replaceAll(""); 177 | } 178 | 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/training/BoulderCalc.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.training; 7 | 8 | import javax.swing.JSpinner; 9 | import javax.swing.JTextField; 10 | import javax.swing.event.ChangeEvent; 11 | import javax.swing.event.DocumentEvent; 12 | import javax.swing.event.DocumentListener; 13 | import org.fbb.board.internals.GuiLogHelper; 14 | 15 | /** 16 | * 17 | * @author jvanek 18 | */ 19 | public class BoulderCalc { 20 | 21 | private final JTextField timeOfBoulder; 22 | private final JTextField timeOfTraining; 23 | private final JSpinner numBoulders; 24 | private boolean active = true; 25 | 26 | private void setActive(boolean active) { 27 | this.active = active; 28 | } 29 | 30 | public BoulderCalc(JTextField timeOfBoulder, JTextField timeOfTraining, JSpinner numBoulders) { 31 | this.timeOfBoulder = timeOfBoulder; 32 | this.timeOfTraining = timeOfTraining; 33 | this.numBoulders = numBoulders; 34 | 35 | numBoulders.addChangeListener((ChangeEvent e) -> { 36 | numOfBouldersChanged(); 37 | }); 38 | 39 | timeOfBoulder.getDocument().addDocumentListener(new DocumentListener() { 40 | 41 | @Override 42 | public void insertUpdate(DocumentEvent e) { 43 | timeOfBouldersChanged(); 44 | } 45 | 46 | @Override 47 | public void removeUpdate(DocumentEvent e) { 48 | timeOfBouldersChanged(); 49 | } 50 | 51 | @Override 52 | public void changedUpdate(DocumentEvent e) { 53 | timeOfBouldersChanged(); 54 | } 55 | }); 56 | 57 | timeOfTraining.getDocument().addDocumentListener(new DocumentListener() { 58 | 59 | @Override 60 | public void insertUpdate(DocumentEvent e) { 61 | timeOfTrainingChanged(); 62 | } 63 | 64 | @Override 65 | public void removeUpdate(DocumentEvent e) { 66 | timeOfTrainingChanged(); 67 | } 68 | 69 | @Override 70 | public void changedUpdate(DocumentEvent e) { 71 | timeOfTrainingChanged(); 72 | } 73 | }); 74 | } 75 | 76 | private void numOfBouldersChanged() { 77 | if (!active) { 78 | return; 79 | } 80 | active = false; 81 | try { 82 | if (decode(timeOfBoulder.getText()) > 0) { 83 | int nwTimeOfTraining = ((Integer) numBoulders.getValue()) * decode(timeOfBoulder.getText()); 84 | timeOfTraining.setText(code(nwTimeOfTraining)); 85 | timeOfTraining.repaint(); 86 | } 87 | } finally { 88 | active = true; 89 | } 90 | } 91 | 92 | private void timeOfBouldersChanged() { 93 | if (!active) { 94 | return; 95 | } 96 | active = false; 97 | try { 98 | if (decode(timeOfBoulder.getText()) > 0) { 99 | int nwTimeOfTraining = ((Integer) numBoulders.getValue()) * decode(timeOfBoulder.getText()); 100 | timeOfTraining.setText(code(nwTimeOfTraining)); 101 | timeOfTraining.repaint(); 102 | } 103 | } finally { 104 | active = true; 105 | } 106 | } 107 | 108 | private void timeOfTrainingChanged() { 109 | if (!active) { 110 | return; 111 | } 112 | active = false; 113 | try { 114 | int oldTimeOfTrainig = decode(timeOfTraining.getText()); 115 | if (oldTimeOfTrainig > 0) { 116 | int oldNumOfBoulders = ((Integer) numBoulders.getValue()); 117 | int nwTimeOfBoulder = oldTimeOfTrainig / oldNumOfBoulders; 118 | timeOfBoulder.setText(code(nwTimeOfBoulder)); 119 | timeOfBoulder.repaint(); 120 | } 121 | } finally { 122 | active = true; 123 | } 124 | } 125 | 126 | public static String code(long seconds) { 127 | return (seconds / 60) + ":" + (seconds % 60); 128 | 129 | } 130 | 131 | public static int decode(String s) { 132 | if (s.trim().isEmpty()) { 133 | return 0; 134 | } 135 | try { 136 | String[] ss = s.trim().split(":"); 137 | if (ss.length == 1) { 138 | return Integer.parseInt(ss[0].trim()); 139 | } 140 | int miuntes = Integer.parseInt(ss[0].trim()); 141 | int secs = Integer.parseInt(ss[1].trim()); 142 | return miuntes * 60 + secs; 143 | } catch (Exception ex) { 144 | GuiLogHelper.guiLogger.loge(ex); 145 | //JOptionPane.showMessageDialog(null, Translator.R("parseTimeIssue")); 146 | return -1; 147 | } 148 | } 149 | 150 | public int getTotalTime() { 151 | return decode(timeOfTraining.getText()); 152 | } 153 | 154 | public int getTimeOfBoulder() { 155 | return decode(timeOfBoulder.getText()); 156 | } 157 | 158 | void setTimeOfBoulder(String text) { 159 | try { 160 | setActive(false); 161 | timeOfBoulder.setText(text); 162 | } finally { 163 | setActive(true); 164 | } 165 | } 166 | 167 | void setTimeOfTraining(String text) { 168 | try { 169 | setActive(false); 170 | timeOfTraining.setText(text); 171 | } finally { 172 | setActive(true); 173 | } 174 | } 175 | 176 | void setNumBoulders(int n) { 177 | try { 178 | setActive(false); 179 | numBoulders.setValue(n); 180 | } finally { 181 | setActive(true); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/training/Training.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.training; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.File; 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.OutputStream; 14 | import java.util.Date; 15 | import java.util.Enumeration; 16 | import java.util.Properties; 17 | import java.util.zip.ZipEntry; 18 | import java.util.zip.ZipFile; 19 | import java.util.zip.ZipOutputStream; 20 | import org.fbb.board.internals.Filter; 21 | 22 | /** 23 | * 24 | * @author jvanek 25 | */ 26 | public class Training { 27 | 28 | public static class InnerSettings { 29 | 30 | public final boolean random; 31 | public final boolean regular; 32 | public final boolean jumps; 33 | public final String tOfBoulder; 34 | public final String tOfTraining; 35 | public final int numOfBoulders; 36 | 37 | public InnerSettings(boolean random, boolean regular, boolean jumps, String tOfBoulder, String tOfTraining, int numOfBoulders) { 38 | this.random = random; 39 | this.regular = regular; 40 | this.jumps = jumps; 41 | this.tOfBoulder = tOfBoulder; 42 | this.tOfTraining = tOfTraining; 43 | this.numOfBoulders = numOfBoulders; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "random/regular/jumps: " + random + "/" + regular + "/" + jumps + "\n" 49 | + "time per boulder/boulders: " + tOfTraining + "/" + numOfBoulders + "\n" 50 | + "time: " + tOfTraining; 51 | } 52 | 53 | private void save(OutputStream zos) throws IOException { 54 | Properties p = new Properties(); 55 | p.setProperty("RANDOM", "" + random); 56 | p.setProperty("REGULAR", "" + regular); 57 | p.setProperty("JUMPS", "" + jumps); 58 | p.setProperty("TIME_TRAIN", tOfTraining); 59 | p.setProperty("TIME_BOULD", tOfBoulder); 60 | p.setProperty("BOULDERS", "" + numOfBoulders); 61 | p.store(zos, "FlashFreeBoard single timered training" + new Date()); 62 | } 63 | 64 | private static InnerSettings load(InputStream zos) throws IOException { 65 | Properties p = new Properties(); 66 | p.load(zos); 67 | boolean random = Boolean.valueOf(p.getProperty("RANDOM", "true")); 68 | boolean regular = Boolean.valueOf(p.getProperty("REGULAR", "true")); 69 | boolean jumps = Boolean.valueOf(p.getProperty("JUMPS", "true")); 70 | String tOfTraining = p.getProperty("TIME_TRAIN", "5:00"); 71 | String tOfBoulder = p.getProperty("TIME_BOULD", "00:20"); 72 | int numOfBoulders = Integer.valueOf(p.getProperty("BOULDERS", "15")); 73 | return new InnerSettings(random, regular, jumps, tOfBoulder, tOfTraining, numOfBoulders); 74 | } 75 | 76 | } 77 | public final InnerSettings innerSettings; 78 | public final Filter filter; 79 | public final String currentName; 80 | 81 | @Override 82 | public String toString() { 83 | return filter.toString() + "\n" 84 | + "Start at: " + currentName + "\n" 85 | + innerSettings.toString(); 86 | 87 | } 88 | 89 | public Training(boolean random, boolean regular, boolean jumps, String tOfBoulder, String tOfTraining, int numOfBoulders, Filter filter, String currentName) { 90 | this.innerSettings = new InnerSettings(random, regular, jumps, tOfBoulder, tOfTraining, numOfBoulders); 91 | this.filter = filter; 92 | this.currentName = currentName; 93 | } 94 | 95 | private Training(InnerSettings innerSettings, Filter filter, String currentName) { 96 | this.innerSettings = innerSettings; 97 | this.filter = filter; 98 | this.currentName = currentName; 99 | } 100 | 101 | public static Training loadSavedTraining(File f) throws IOException { 102 | ZipFile zipFile = new ZipFile(f); 103 | Enumeration entries = zipFile.entries(); 104 | String current = null; 105 | InnerSettings setup = null; 106 | Filter filter = null; 107 | while (entries.hasMoreElements()) { 108 | ZipEntry entry = entries.nextElement(); 109 | InputStream stream = zipFile.getInputStream(entry); 110 | if (entry.getName().equals("last.current")) { 111 | ByteArrayOutputStream result = new ByteArrayOutputStream(); 112 | byte[] buffer = new byte[1024]; 113 | int length; 114 | while ((length = stream.read(buffer)) != -1) { 115 | result.write(buffer, 0, length); 116 | } 117 | current = result.toString("UTF-8"); 118 | } else if (entry.getName().equals("setup.prop")) { 119 | setup = InnerSettings.load(stream); 120 | } else if (entry.getName().equals("filter")) { 121 | filter = Filter.load(stream); 122 | } 123 | } 124 | return new Training(setup, filter, current); 125 | } 126 | 127 | public void saveSingleTraining(File out) throws IOException { 128 | ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(out)); 129 | zos.putNextEntry(new ZipEntry("filter")); 130 | this.filter.write(zos); 131 | zos.closeEntry(); 132 | zos.putNextEntry(new ZipEntry("last.current")); 133 | zos.write(this.currentName.getBytes("utf-8")); 134 | zos.closeEntry(); 135 | zos.putNextEntry(new ZipEntry("setup.prop")); 136 | this.innerSettings.save(zos); 137 | zos.closeEntry(); 138 | zos.flush(); 139 | zos.close(); 140 | 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/ContentReader.java: -------------------------------------------------------------------------------- 1 | /* ContentReader.java 2 | Copyright (C) 2011,2012 Red Hat, Inc. 3 | 4 | This file is part of IcedTea. 5 | 6 | IcedTea is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, version 2. 9 | 10 | IcedTea is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with IcedTea; see the file COPYING. If not, write to 17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18 | 02110-1301 USA. 19 | 20 | Linking this library statically or dynamically with other modules is 21 | making a combined work based on this library. Thus, the terms and 22 | conditions of the GNU General Public License cover the whole 23 | combination. 24 | 25 | As a special exception, the copyright holders of this library give you 26 | permission to link this library with independent modules to produce an 27 | executable, regardless of the license terms of these independent 28 | modules, and to copy and distribute the resulting executable under 29 | terms of your choice, provided that you also meet, for each linked 30 | independent module, the terms and conditions of the license of that 31 | module. An independent module is a module which is not derived from 32 | or based on this library. If you modify this library, you may extend 33 | this exception to your version of the library, but you are not 34 | obligated to do so. If you do not wish to do so, delete this 35 | exception statement from your version. 36 | */ 37 | package org.fbb.board.internals; 38 | 39 | import java.io.IOException; 40 | import java.io.InputStream; 41 | import java.io.InputStreamReader; 42 | import java.io.Reader; 43 | import java.util.ArrayList; 44 | import java.util.List; 45 | 46 | /** 47 | * Class to read content of stdout/stderr of process, and to cooperate with its 48 | * running/terminated/finished statuses. 49 | */ 50 | class ContentReader implements Runnable { 51 | 52 | StringBuilder sb = new StringBuilder(); 53 | private final InputStream is; 54 | private boolean done; 55 | final List listeners = new ArrayList(1); 56 | 57 | public String getContent() { 58 | return sb.toString(); 59 | } 60 | 61 | public ContentReader(InputStream is) throws IOException { 62 | this.is = is; 63 | } 64 | 65 | public ContentReader(InputStream is, ContentReaderListener l) throws IOException { 66 | this.is = is; 67 | if (l != null) { 68 | this.listeners.add(l); 69 | } 70 | } 71 | 72 | public ContentReader(InputStream is, List l) throws IOException { 73 | this.is = is; 74 | if (l != null) { 75 | this.listeners.addAll(l); 76 | } 77 | } 78 | 79 | public void addListener(ContentReaderListener listener) { 80 | this.listeners.add(listener); 81 | } 82 | 83 | public List getListener() { 84 | return listeners; 85 | } 86 | 87 | /** 88 | * Blocks until the copy is complete, or until the thread is interrupted 89 | */ 90 | public synchronized void waitUntilDone() throws InterruptedException { 91 | boolean interrupted = false; 92 | // poll interrupted flag, while waiting for copy to complete 93 | while (!(interrupted = Thread.interrupted()) && !done) { 94 | wait(1000); 95 | } 96 | if (interrupted) { 97 | //throw new InterruptedException(); 98 | } 99 | } 100 | 101 | @Override 102 | public void run() { 103 | try { 104 | Reader br = new InputStreamReader(is, "UTF-8"); 105 | StringBuilder line = new StringBuilder(); 106 | while (true) { 107 | int s = br.read(); 108 | if (s < 0) { 109 | if (line.length() > 0 && listeners != null) { 110 | for (ContentReaderListener listener : listeners) { 111 | if (listener != null) { 112 | listener.lineReaded(line.toString()); 113 | } 114 | } 115 | } 116 | break; 117 | } 118 | char ch = (char) s; 119 | sb.append(ch); 120 | line.append(ch); 121 | if (ch == '\n') { 122 | if (listeners != null) { 123 | for (ContentReaderListener listener : listeners) { 124 | if (listener != null) { 125 | listener.lineReaded(line.toString()); 126 | } 127 | } 128 | } 129 | line = new StringBuilder(); 130 | } 131 | if (listeners != null) { 132 | for (ContentReaderListener listener : listeners) { 133 | if (listener != null) { 134 | listener.charReaded(ch); 135 | } 136 | } 137 | } 138 | } 139 | } catch (NullPointerException ex) { 140 | ex.printStackTrace(); 141 | } 142 | //do not want to bother output with terminations 143 | //mostly compaling when assassin kill the process about StreamClosed 144 | catch (Exception ex) { 145 | // logException(ex); 146 | //ex.printStackTrace(); 147 | } finally { 148 | try { 149 | is.close(); 150 | } catch (Exception ex) { 151 | // ex.printStackTrace(); 152 | } finally { 153 | done = true; 154 | } 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/MainWindow.java: -------------------------------------------------------------------------------- 1 | package org.fbb.board.desktop.gui; 2 | 3 | import org.fbb.board.desktop.gui.awtimpl.TipsListener; 4 | import java.awt.KeyEventDispatcher; 5 | import java.awt.KeyboardFocusManager; 6 | import java.awt.event.KeyEvent; 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.util.zip.ZipInputStream; 10 | import javax.swing.JOptionPane; 11 | import org.apache.commons.cli.CommandLine; 12 | import org.apache.commons.cli.CommandLineParser; 13 | import org.apache.commons.cli.DefaultParser; 14 | import org.apache.commons.cli.Option; 15 | import org.apache.commons.cli.Options; 16 | import org.apache.commons.cli.ParseException; 17 | import org.fbb.board.Translator; 18 | import org.fbb.board.desktop.Files; 19 | import org.fbb.board.desktop.gui.awtimpl.CreateWindow; 20 | import org.fbb.board.desktop.gui.awtimpl.MainWindowImpl; 21 | import org.fbb.board.internals.grid.Boulder; 22 | import org.fbb.board.internals.db.DB; 23 | import org.fbb.board.internals.GlobalSettings; 24 | import org.fbb.board.internals.grid.Grid; 25 | import org.fbb.board.internals.grid.GridPane; 26 | import org.fbb.board.internals.GuiLogHelper; 27 | import org.fbb.board.internals.db.Puller; 28 | import org.fbb.board.internals.grades.Grade; 29 | 30 | /** 31 | * 32 | * @author jvanek 33 | */ 34 | //filters - by grade, by date, by number of holds 35 | public class MainWindow { 36 | 37 | public static final GlobalSettings gs = new GlobalSettings(); 38 | public static final DB db = new DB(gs); 39 | public static final Puller puller = Puller.create(gs.getPullerDelay() * 60, db); 40 | private static final KeyEventDispatcher f1 = new KeyEventDispatcher() { 41 | 42 | @Override 43 | public boolean dispatchKeyEvent(KeyEvent e) { 44 | if (e.getKeyCode() == KeyEvent.VK_F1 && e.getID() == KeyEvent.KEY_PRESSED) { 45 | new TipsListener().actionPerformed(null); 46 | return true; 47 | } 48 | return false; 49 | } 50 | }; 51 | 52 | public static void main(String... s) throws ParseException { 53 | Option o = new Option("w", "windows", true, "How many windows to open if all is ok, this is not guarded by internal limitation"); 54 | Options ops = new Options(); 55 | ops.addOption(o); 56 | CommandLineParser parser = new DefaultParser(); 57 | CommandLine cmd = parser.parse(ops, s); 58 | int windows = 1; 59 | if (cmd.hasOption("-w")) { 60 | String sw = cmd.getOptionValue("w"); 61 | windows = Integer.valueOf(sw); 62 | } 63 | try { 64 | KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(f1); 65 | Grid.colorProvider = gs; 66 | Grade.loadConversiontable(); 67 | if (Files.getLastBoard() != null && Files.getLastBoulder() != null) { 68 | Boulder b = Boulder.load(Files.getBoulderFile(Files.getLastBoulder())); 69 | GridPane.Preload preloaded = GridPane.preload(new ZipInputStream(new FileInputStream(Files.getWallFile(Files.getLastBoard()))), Files.getLastBoard()); 70 | //check if boards mathces 71 | if (!b.getWall().equals(preloaded.givenId)) { 72 | //if not warn, and ask user ifhe wont to keep wall or boulder. If not load wall only 73 | JOptionPane.showMessageDialog(null, Translator.R("NotMatchingBoulderWall", b.getWall(), preloaded.givenId)); 74 | File bWall = Files.getWallFile(b.getWall()); 75 | if (bWall.exists()) { 76 | //it is likely that wall had been changed intntionally, and old wall was simply not deleted, and boulder was not updated 77 | int a = JOptionPane.showConfirmDialog(null, Translator.R("PossiblyIncorrectLastBoulder")); 78 | if (a == JOptionPane.YES_OPTION) { 79 | Files.setLastBoulder((String) null);//delete alst boulder info 80 | MainWindowImpl.loadWallWithBoulder(Files.getLastBoard()); 81 | } else { 82 | preloaded = GridPane.preload(new ZipInputStream(new FileInputStream(bWall)), bWall.getName()); 83 | Files.setLastBoard(bWall.getName()); 84 | MainWindowImpl.loadWallWithBoulder(preloaded, b); 85 | } 86 | } else { 87 | Files.setLastBoulder((String) null);//delete alst boulder info 88 | MainWindowImpl.loadWallWithBoulder(Files.getLastBoard()); 89 | } 90 | } else { 91 | //if so, show last boulder on last wall 92 | for (int i = 0; i < windows; i++) { 93 | MainWindowImpl.loadWallWithBoulder(preloaded, b); 94 | } 95 | } 96 | } else if (Files.getLastBoulder() != null && Files.getLastBoard() == null) { 97 | //warn, but load last boulder on its wall, if wall does noto exists, empty(?) 98 | GuiLogHelper.guiLogger.loge("Last boulder but not lat wall!"); 99 | Boulder b = Boulder.load(Files.getBoulderFile(Files.getLastBoulder())); 100 | File bWall = Files.getWallFile(b.getWall()); 101 | if (bWall.exists()) { 102 | GridPane.Preload preloaded = GridPane.preload(new ZipInputStream(new FileInputStream(bWall)), bWall.getName()); 103 | Files.setLastBoard(bWall.getName()); 104 | MainWindowImpl.loadWallWithBoulder(preloaded, b); 105 | } else { 106 | CreateWindow.createSelectOrImportWall(LoadBackgroundOrImportOrLoadWall.getDefaultUrl()); 107 | } 108 | } else if (Files.getLastBoard() != null && Files.getLastBoulder() == null) { 109 | //load last wall, generate random bouoder 110 | MainWindowImpl.loadWallWithBoulder(Files.getLastBoard()); 111 | } else { 112 | //both null, sugest to create board 113 | CreateWindow.createSelectOrImportWall(LoadBackgroundOrImportOrLoadWall.getDefaultUrl()); 114 | } 115 | } catch (Exception ex) { 116 | //do better? 117 | GuiLogHelper.guiLogger.loge(ex); 118 | JOptionPane.showMessageDialog(null, ex); 119 | } 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/ProcessWrapper.java: -------------------------------------------------------------------------------- 1 | /* ProcessWrapper.java 2 | Copyright (C) 2011,2012 Red Hat, Inc. 3 | 4 | This file is part of IcedTea. 5 | 6 | IcedTea is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, version 2. 9 | 10 | IcedTea is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with IcedTea; see the file COPYING. If not, write to 17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18 | 02110-1301 USA. 19 | 20 | Linking this library statically or dynamically with other modules is 21 | making a combined work based on this library. Thus, the terms and 22 | conditions of the GNU General Public License cover the whole 23 | combination. 24 | 25 | As a special exception, the copyright holders of this library give you 26 | permission to link this library with independent modules to produce an 27 | executable, regardless of the license terms of these independent 28 | modules, and to copy and distribute the resulting executable under 29 | terms of your choice, provided that you also meet, for each linked 30 | independent module, the terms and conditions of the license of that 31 | module. An independent module is a module which is not derived from 32 | or based on this library. If you modify this library, you may extend 33 | this exception to your version of the library, but you are not 34 | obligated to do so. If you do not wish to do so, delete this 35 | exception statement from your version. 36 | */ 37 | 38 | package org.fbb.board.internals; 39 | 40 | import java.io.ByteArrayInputStream; 41 | import java.io.File; 42 | import java.io.InputStream; 43 | import java.io.OutputStream; 44 | import java.net.URL; 45 | import java.nio.charset.StandardCharsets; 46 | import java.util.ArrayList; 47 | import java.util.Arrays; 48 | import java.util.List; 49 | 50 | 51 | 52 | /** 53 | * This class wraps execution of ThreadedProcess. 54 | * Add listeners and allows another setters, eg of ReactingProcess 55 | * 56 | */ 57 | public class ProcessWrapper { 58 | 59 | private List args; 60 | private File dir; 61 | private final List stdoutl = new ArrayList<>(1); 62 | private final List stderrl = new ArrayList<>(1); 63 | private String[] vars; 64 | private InputStream writer; 65 | 66 | public ProcessWrapper(String... args) { 67 | List urledArgs = Arrays.asList(args); 68 | this.args = urledArgs; 69 | this.vars=null; 70 | } 71 | 72 | public final void addStdOutListener(ContentReaderListener l) { 73 | if (l == null) { 74 | return; 75 | } 76 | stdoutl.add(l); 77 | 78 | } 79 | 80 | public final void addStdErrListener(ContentReaderListener l) { 81 | if (l == null) { 82 | return; 83 | } 84 | stderrl.add(l); 85 | 86 | } 87 | 88 | public final void addStdOutListeners(List l) { 89 | if (l == null) { 90 | return; 91 | } 92 | stdoutl.addAll(l); 93 | 94 | } 95 | 96 | public final void addStdErrListeners(List l) { 97 | if (l == null) { 98 | return; 99 | } 100 | stderrl.addAll(l); 101 | 102 | } 103 | 104 | /** 105 | * @return the args 106 | */ 107 | public List getArgs() { 108 | return args; 109 | } 110 | 111 | /** 112 | * @param args the args to set 113 | */ 114 | public void setArgs(List args) { 115 | this.args = args; 116 | } 117 | 118 | /** 119 | * @return the dir 120 | */ 121 | public File getDir() { 122 | return dir; 123 | } 124 | 125 | /** 126 | * @param dir the dir to set 127 | */ 128 | public void setDir(File dir) { 129 | this.dir = dir; 130 | } 131 | 132 | /** 133 | * @return the stdoutl 134 | */ 135 | public List getStdoutListeners() { 136 | return stdoutl; 137 | } 138 | 139 | /** 140 | * @return the stderrl 141 | */ 142 | public List getStderrListeners() { 143 | return stderrl; 144 | } 145 | 146 | /** 147 | * @return the vars 148 | */ 149 | public String[] getVars() { 150 | return vars; 151 | } 152 | 153 | /** 154 | * @param vars the vars to set 155 | */ 156 | public void setVars(String[] vars) { 157 | this.vars = vars; 158 | } 159 | 160 | public ProcessResult execute() throws Exception { 161 | ThreadedProcess t = new ThreadedProcess(args, dir, vars); 162 | t.setWriter(writer); 163 | t.start(); 164 | while (t.getP() == null && t.deadlyException == null) { 165 | Thread.sleep(100); 166 | } 167 | if (t.deadlyException != null) { 168 | return new ProcessResult("", "", null, true, Integer.MIN_VALUE, t.deadlyException); 169 | } 170 | ContentReader crs = new ContentReader(t.getP().getInputStream(), stdoutl); 171 | ContentReader cre = new ContentReader(t.getP().getErrorStream(), stderrl); 172 | 173 | OutputStream out = t.getP().getOutputStream(); 174 | if (out != null) { 175 | out.close(); 176 | } 177 | 178 | new Thread(crs).start(); 179 | new Thread(cre).start(); 180 | while (t.isRunning()) { 181 | Thread.sleep(100); 182 | } 183 | 184 | while (!t.isDestoyed()) { 185 | Thread.sleep(100); 186 | } 187 | // ServerAccess.logOutputReprint(t.getP().exitValue()); when process is killed, this throws exception 188 | 189 | ProcessResult pr = new ProcessResult(crs.getContent(), cre.getContent(), t.getP(), false, t.getExitCode(), null); 190 | return pr; 191 | } 192 | 193 | 194 | private static String createConnectionMessage(ThreadedProcess t) { 195 | return "Connecting " + t.getCommandLine(); 196 | } 197 | 198 | public void setWriter(InputStream writer) { 199 | this.writer = writer; 200 | } 201 | 202 | public void setWriter(String writer) { 203 | setWriter(new ByteArrayInputStream(writer.getBytes(StandardCharsets.UTF_8))); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/dialogs/BoxesWindow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.dialogs; 7 | 8 | import java.awt.BorderLayout; 9 | import java.awt.Component; 10 | import java.awt.GridLayout; 11 | import java.awt.Window; 12 | import java.awt.event.WindowAdapter; 13 | import java.awt.event.WindowEvent; 14 | import java.util.Random; 15 | import javax.swing.JCheckBox; 16 | import javax.swing.JComboBox; 17 | import javax.swing.JDialog; 18 | import javax.swing.JFrame; 19 | import javax.swing.JLabel; 20 | import javax.swing.JOptionPane; 21 | import javax.swing.JPanel; 22 | import javax.swing.JTextField; 23 | import org.fbb.board.Translator; 24 | import org.fbb.board.desktop.gui.dialogs.parts.DefaultSnoozeAction; 25 | import org.fbb.board.desktop.gui.dialogs.parts.TimeAndSnooze; 26 | import org.fbb.board.internals.GuiLogHelper; 27 | import org.fbb.board.internals.grid.GridPane; 28 | 29 | /** 30 | * 31 | * @author jvanek 32 | */ 33 | public class BoxesWindow extends JDialog implements Runnable { 34 | 35 | private final GridPane gp; 36 | private final TimeAndSnooze tas; 37 | 38 | private final JComboBox size; 39 | 40 | private final JCheckBox up = new JCheckBox(Translator.R("uc")); 41 | private final JCheckBox bottom = new JCheckBox(Translator.R("bc")); 42 | private final JCheckBox left = new JCheckBox(Translator.R("lw")); 43 | private final JCheckBox right = new JCheckBox(Translator.R("rw")); 44 | 45 | public BoxesWindow(Window parent, GridPane gp) { 46 | super(parent); 47 | this.gp = gp; 48 | this.setTitle(Translator.R("box")); 49 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 50 | this.setModalityType(ModalityType.DOCUMENT_MODAL); 51 | this.setSize(500, 400); 52 | this.setLocationRelativeTo(parent); 53 | this.add(new JTextField(Translator.R("boxHelp")), BorderLayout.SOUTH); 54 | JPanel panel = new JPanel(); 55 | this.add(panel); 56 | panel.setLayout(new GridLayout(6, 2)); 57 | panel.add(new JLabel(Translator.R("size"))); 58 | size = new JComboBox<>(new Integer[]{1, 2, 3}); 59 | size.setSelectedIndex(1); 60 | panel.add(size); 61 | up.setSelected(false); 62 | bottom.setSelected(false); 63 | panel.add(up); 64 | panel.add(bottom); 65 | left.setSelected(true); 66 | right.setSelected(true); 67 | panel.add(left); 68 | panel.add(right); 69 | tas = new TimeAndSnooze(panel, new DefaultSnoozeAction(gp), new Runnable() { 70 | @Override 71 | public void run() { 72 | BoxesWindow.this.reset(); 73 | } 74 | }, this); 75 | this.pack(); 76 | this.setSize(this.getWidth(), 350); 77 | this.setLocationRelativeTo(parent); 78 | this.addWindowListener(new WindowAdapter() { 79 | 80 | @Override 81 | public void windowClosed(WindowEvent e) { 82 | tas.stop(); 83 | } 84 | 85 | }); 86 | reset(); 87 | tas.start(); 88 | } 89 | private final Random move = new Random(); 90 | 91 | int c1, c2, r1, r2; 92 | int t = 0; 93 | int v1, v2, v3, v4; 94 | 95 | @Override 96 | public void run() { 97 | try { 98 | if (t <= 0) { 99 | t = move.nextInt(Math.min(getGridWidth(), getgridHeight()) / 2)*2+1; 100 | v1 = move.nextInt(2) - 1; 101 | v2 = move.nextInt(2) - 1; 102 | v3 = move.nextInt(2) - 1; 103 | v4 = move.nextInt(2) - 1; 104 | } 105 | t--; 106 | c1 = c1 + v1; 107 | c2 = c2 + v2; 108 | r1 = r1 + v3; 109 | r2 = r2 + v4; 110 | int[] limited1 = limit(c1, 0, getGridWidth() / 2, v1); 111 | int[] limited2 = limit(c2, getGridWidth() / 2, getGridWidth(), v2); 112 | int[] limited3 = limit(r1, 0, getgridHeight() / 2, v3); 113 | int[] limited4 = limit(r2, getgridHeight() / 2, getgridHeight(), v4); 114 | c1 = limited1[0]; 115 | v1 = limited1[1]; 116 | c2 = limited2[0]; 117 | v2 = limited2[1]; 118 | r1 = limited3[0]; 119 | v3 = limited3[1]; 120 | r2 = limited4[0]; 121 | v4 = limited4[1]; 122 | gp.getGrid().clean(); 123 | drawSIngleGrid(c1, c2, r1, r2, 0, (byte) 3); 124 | if (size.getSelectedIndex() > 0) { 125 | drawSIngleGrid(c1, c2, r1, r2, 1, (byte) 2); 126 | if (size.getSelectedIndex() > 1) { 127 | drawSIngleGrid(c1, c2, r1, r2, 2, (byte) 1); 128 | } 129 | } 130 | gp.repaintAndSendToKnown(); 131 | } catch (Exception ex) { 132 | GuiLogHelper.guiLogger.loge(ex); 133 | JOptionPane.showMessageDialog(BoxesWindow.this, ex); 134 | } 135 | } 136 | 137 | private int getGridWidth() { 138 | return gp.getGrid().getWidth(); 139 | } 140 | 141 | private void reset() { 142 | c1 = (getGridWidth() / 4); 143 | c2 = ((getGridWidth() * 3) / 4); 144 | r1 = (getgridHeight() / 4); 145 | r2 = ((getgridHeight() * 3) / 4); 146 | 147 | } 148 | 149 | private int getgridHeight() { 150 | return gp.getGrid().getHeight(); 151 | } 152 | 153 | private void drawSIngleGrid(int c1, int c2, int r1, int r2, int i, byte c) { 154 | if (left.isSelected()) { 155 | if (c1 - i >= 0) { 156 | CampusLikeDialog.drawColumn(c1 - i, c, gp.getGrid()); 157 | } 158 | } 159 | if (up.isSelected()) { 160 | if (r1 - i >= 0) { 161 | CampusLikeDialog.drawRow(r1 - i, c, gp.getGrid()); 162 | } 163 | } 164 | if (right.isSelected()) { 165 | if (c2 + i < getGridWidth()) { 166 | CampusLikeDialog.drawColumn(c2 + i, c, gp.getGrid()); 167 | } 168 | } 169 | if (bottom.isSelected()) { 170 | if (r2 + i < getgridHeight()) { 171 | CampusLikeDialog.drawRow(r2 + i, c, gp.getGrid()); 172 | } 173 | } 174 | 175 | } 176 | 177 | private int[] limit(final int c, final int minInc, final int maxEx, final int v) { 178 | int[] r = new int[]{c, v}; 179 | if (c < minInc) { 180 | r = new int[]{minInc, v * -1}; 181 | } 182 | if (c >= maxEx) { 183 | r = new int[]{maxEx - 1, v * -1}; 184 | } 185 | return r; 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/dialogs/ClockWindow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.dialogs; 7 | 8 | import java.awt.BorderLayout; 9 | import java.awt.Component; 10 | import java.awt.GridLayout; 11 | import java.awt.Point; 12 | import java.awt.Window; 13 | import java.awt.event.WindowAdapter; 14 | import java.awt.event.WindowEvent; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Random; 18 | import javax.swing.JCheckBox; 19 | import javax.swing.JComboBox; 20 | import javax.swing.JDialog; 21 | import javax.swing.JFrame; 22 | import javax.swing.JLabel; 23 | import javax.swing.JOptionPane; 24 | import javax.swing.JPanel; 25 | import javax.swing.JTextField; 26 | import org.fbb.board.Translator; 27 | import org.fbb.board.Utils; 28 | import org.fbb.board.desktop.gui.dialogs.parts.DefaultSnoozeAction; 29 | import org.fbb.board.desktop.gui.dialogs.parts.TimeAndSnooze; 30 | import org.fbb.board.internals.GuiLogHelper; 31 | import org.fbb.board.internals.grid.GridPane; 32 | 33 | /** 34 | * 35 | * @author jvanek 36 | */ 37 | public class ClockWindow extends JDialog implements Runnable { 38 | 39 | private final GridPane gp; 40 | private final TimeAndSnooze tas; 41 | 42 | private JCheckBox clockwise; 43 | private final JComboBox size_line; 44 | private final JComboBox size_middle; 45 | private final JComboBox axes; 46 | 47 | public ClockWindow(Window parent, GridPane gp) { 48 | super(parent); 49 | this.gp = gp; 50 | this.setTitle(Translator.R("clock")); 51 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 52 | this.setModalityType(ModalityType.DOCUMENT_MODAL); 53 | this.setSize(500, 400); 54 | this.setLocationRelativeTo(parent); 55 | this.add(new JTextField(Translator.R("clockHelp")), BorderLayout.SOUTH); 56 | JPanel panel = new JPanel(); 57 | this.add(panel); 58 | panel.setLayout(new GridLayout(8, 2)); 59 | panel.add(new JLabel(Translator.R("size_l"))); 60 | size_line = new JComboBox<>(new Integer[]{1, 2, 3}); 61 | size_line.setSelectedIndex(1); 62 | panel.add(size_line); 63 | 64 | panel.add(new JLabel(Translator.R("size_m"))); 65 | size_middle = new JComboBox<>(new Integer[]{1, 2, 3}); 66 | size_middle.setSelectedIndex(1); 67 | panel.add(size_middle); 68 | panel.add(new JLabel()); 69 | clockwise = new JCheckBox(Translator.R("clockwise"), true); 70 | panel.add(clockwise); 71 | panel.add(new JLabel(Translator.R("axes"))); 72 | axes = new JComboBox<>(new Integer[]{1, 2, 3, 4, 5, 6}); 73 | axes.setSelectedIndex(0); 74 | panel.add(axes); 75 | tas = new TimeAndSnooze(panel, new DefaultSnoozeAction(gp), new Runnable() { 76 | @Override 77 | public void run() { 78 | ClockWindow.this.reset(); 79 | } 80 | }, this); 81 | this.pack(); 82 | this.setSize(this.getWidth(), 350); 83 | this.setLocationRelativeTo(parent); 84 | this.addWindowListener(new WindowAdapter() { 85 | 86 | @Override 87 | public void windowClosed(WindowEvent e) { 88 | tas.stop(); 89 | } 90 | 91 | }); 92 | tas.start(); 93 | } 94 | private Random move = new Random(); 95 | int degrees = 0; 96 | int jump = 15; 97 | 98 | @Override 99 | public void run() { 100 | try { 101 | gp.getGrid().clean(); 102 | Point center = new Point(gp.getGrid().getWidth() / 2, gp.getGrid().getHeight() / 2); 103 | int r = Math.max(gp.getGrid().getWidth() / 2, gp.getGrid().getHeight() / 2); 104 | int astep = 360 / (axes.getSelectedIndex() + 1); 105 | BallWindow.drawBall(gp.getGrid(), size_middle.getSelectedIndex(), center, (byte) 3, (byte) 2, (byte) 1); 106 | for (int i = 0; i <= axes.getSelectedIndex(); i++) { 107 | Double a = Math.toRadians(degrees + (i * astep)); 108 | int x = (int) ((double) r * Math.cos(a) - r * Math.sin(a)); 109 | int y = (int) ((double) r * Math.sin(a) + r * Math.cos(a)); 110 | Point dest = new Point(x + center.x, y + center.y); 111 | //by width, create three lines 112 | //moved in corect way to suggest direction 113 | int ymove = 0; 114 | int xmove = 0; 115 | if (x <= 0 && y <= 0) { 116 | ymove = 1; 117 | } 118 | if (x >= 0 && y <= 0) { 119 | xmove = -1; 120 | } 121 | if (x <= 0 && y >= 0) { 122 | xmove = 1; 123 | } 124 | if (x >= 0 && y >= 0) { 125 | ymove = -1; 126 | } 127 | if (!clockwise.isSelected()) { 128 | xmove *= -1; 129 | ymove *= -1; 130 | } 131 | List l1 = Utils.bresenhamLine(center.x, center.y, dest.x, dest.y); 132 | //I keep drawing the line in two way, to get some more pixels in the most ignored ides.. not much sucess 133 | if (size_line.getSelectedIndex() > 1) { 134 | List l3 = Utils.bresenhamLine(center.x + 2 * xmove, center.y + 2 * ymove, dest.x + 2 * xmove, dest.y + 2 * ymove); 135 | gp.getGrid().set(l3, (byte) 1); 136 | l3 = new ArrayList(l1.size()); 137 | for (int[] is : l1) { 138 | l3.add(new int[]{is[0] + 2 * xmove, is[1] + 2 * ymove}); 139 | } 140 | gp.getGrid().set(l3, (byte) 1); 141 | } 142 | if (size_line.getSelectedIndex() > 0) { 143 | List l2 = Utils.bresenhamLine(center.x + xmove, center.y + ymove, dest.x + xmove, dest.y + ymove); 144 | gp.getGrid().set(l2, (byte) 2); 145 | l2 = new ArrayList(l1.size()); 146 | for (int[] is : l1) { 147 | l2.add(new int[]{is[0] + xmove, is[1] + ymove}); 148 | } 149 | gp.getGrid().set(l2, (byte) 2); 150 | } 151 | gp.getGrid().set(l1, (byte) 3); 152 | gp.repaintAndSendToKnown(); 153 | } 154 | if (clockwise.isSelected()) { 155 | degrees = degrees + jump; 156 | } else { 157 | degrees = degrees - jump; 158 | } 159 | } catch (Exception ex) { 160 | GuiLogHelper.guiLogger.loge(ex); 161 | JOptionPane.showMessageDialog(ClockWindow.this, ex); 162 | } 163 | } 164 | 165 | private void reset() { 166 | degrees = 0; 167 | 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/dialogs/CampusLikeDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui.dialogs; 7 | 8 | import java.awt.BorderLayout; 9 | import java.awt.Component; 10 | import java.awt.GridLayout; 11 | import java.awt.Window; 12 | import javax.swing.JDialog; 13 | import javax.swing.JFrame; 14 | import javax.swing.JLabel; 15 | import javax.swing.JOptionPane; 16 | import javax.swing.JPanel; 17 | import javax.swing.JTextField; 18 | import javax.swing.event.DocumentEvent; 19 | import javax.swing.event.DocumentListener; 20 | import org.fbb.board.Translator; 21 | import org.fbb.board.internals.GuiLogHelper; 22 | import org.fbb.board.internals.grid.Grid; 23 | import org.fbb.board.internals.grid.GridPane; 24 | 25 | /** 26 | * 27 | * @author jvanek 28 | */ 29 | public class CampusLikeDialog extends JDialog { 30 | 31 | private final GridPane gp; 32 | final JTextField redLines; 33 | final JTextField greenLines; 34 | final JTextField blueLines; 35 | 36 | final JTextField redColumns; 37 | final JTextField greenColumns; 38 | final JTextField blueColumns; 39 | 40 | public CampusLikeDialog(Window parent, GridPane gp) { 41 | super(parent); 42 | this.gp = gp; 43 | this.setTitle(Translator.R("campus")); 44 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 45 | this.setModalityType(ModalityType.DOCUMENT_MODAL); 46 | this.setSize(500, 400); 47 | this.setLocationRelativeTo(parent); 48 | this.add(new JTextField(Translator.R("campusHelp", gp.getGrid().getHeight() - 1, gp.getGrid().getWidth() - 1)), BorderLayout.SOUTH); 49 | JPanel panel = new JPanel(); 50 | this.add(panel); 51 | panel.setLayout(new GridLayout(6, 2)); 52 | panel.add(new JLabel(Translator.R("rln"))); 53 | redLines = new JTextField("" + (gp.getGrid().getHeight() / 2) + " " + (1 * gp.getGrid().getHeight()) / 4); 54 | panel.add(redLines); 55 | panel.add(new JLabel(Translator.R("gln"))); 56 | greenLines = new JTextField(""); 57 | panel.add(greenLines); 58 | panel.add(new JLabel(Translator.R("bln"))); 59 | blueLines = new JTextField(""); 60 | panel.add(blueLines); 61 | panel.add(new JLabel(Translator.R("rnc"))); 62 | redColumns = new JTextField("" + (gp.getGrid().getWidth() / 3) + " " + (2 * gp.getGrid().getWidth() / 3)); 63 | panel.add(redColumns); 64 | panel.add(new JLabel(Translator.R("gnc"))); 65 | greenColumns = new JTextField(""); 66 | panel.add(greenColumns); 67 | panel.add(new JLabel(Translator.R("bnc"))); 68 | blueColumns = new JTextField(""); 69 | panel.add(blueColumns); 70 | Changer ch = new Changer(); 71 | redLines.getDocument().addDocumentListener(ch); 72 | greenLines.getDocument().addDocumentListener(ch); 73 | blueLines.getDocument().addDocumentListener(ch); 74 | redColumns.getDocument().addDocumentListener(ch); 75 | greenColumns.getDocument().addDocumentListener(ch); 76 | blueColumns.getDocument().addDocumentListener(ch); 77 | ch.work(); 78 | this.pack(); 79 | this.setSize(this.getWidth(), 400); 80 | this.setLocationRelativeTo(parent); 81 | } 82 | 83 | private class Changer implements DocumentListener { 84 | 85 | @Override 86 | public void insertUpdate(DocumentEvent e) { 87 | work(); 88 | } 89 | 90 | @Override 91 | public void removeUpdate(DocumentEvent e) { 92 | work(); 93 | } 94 | 95 | @Override 96 | public void changedUpdate(DocumentEvent e) { 97 | work(); 98 | } 99 | 100 | private void work() { 101 | try { 102 | String[] rl = redLines.getText().split("[^\\d]+"); 103 | String[] gl = greenLines.getText().split("[^\\d]+"); 104 | String[] bl = blueLines.getText().split("[^\\d]+"); 105 | String[] rc = redColumns.getText().split("[^\\d]+"); 106 | String[] gc = greenColumns.getText().split("[^\\d]+"); 107 | String[] bc = blueColumns.getText().split("[^\\d]+"); 108 | int totall = countNonEmpty(rl) + countNonEmpty(gl) + countNonEmpty(bl); 109 | int totalc = countNonEmpty(rc) + countNonEmpty(gc) + countNonEmpty(bc); 110 | 111 | if (totalc > gp.getGrid().getWidth() / 2 || totall > gp.getGrid().getHeight() / 2) { 112 | throw new RuntimeException(Translator.R("outOfBonds2", (gp.getGrid().getWidth() / 2), (gp.getGrid().getHeight() / 2))); 113 | } 114 | 115 | gp.getGrid().clean(); 116 | drawColumns(rc, 3); 117 | drawColumns(gc, 2); 118 | drawColumns(bc, 1); 119 | drawRows(rl, 3); 120 | drawRows(gl, 2); 121 | drawRows(bl, 1); 122 | gp.repaintAndSendToKnown(); 123 | } catch (Exception ex) { 124 | GuiLogHelper.guiLogger.loge(ex); 125 | JOptionPane.showMessageDialog(CampusLikeDialog.this, ex); 126 | } 127 | } 128 | 129 | public void drawColumns(String[] cc, int color) throws NumberFormatException, IndexOutOfBoundsException { 130 | CampusLikeDialog.drawColumns(cc, color, gp.getGrid()); 131 | } 132 | 133 | public void drawRows(String[] rr, int color) throws NumberFormatException, IndexOutOfBoundsException { 134 | CampusLikeDialog.drawRows(rr, color, gp.getGrid()); 135 | } 136 | 137 | private int countNonEmpty(String[] s) { 138 | int r = 0; 139 | for (String s1 : s) { 140 | if (!s1.trim().isEmpty()) { 141 | r++; 142 | }; 143 | } 144 | return r; 145 | } 146 | 147 | } 148 | 149 | public static void drawColumn(int x, int color, Grid gp) throws NumberFormatException, IndexOutOfBoundsException { 150 | for (int i = 0; i < gp.getHeight(); i++) { 151 | gp.set(x, i, (byte) color); 152 | } 153 | } 154 | 155 | public static void drawColumns(String[] cc, int color, Grid gp) throws NumberFormatException, IndexOutOfBoundsException { 156 | for (String c : cc) { 157 | if (!c.trim().isEmpty()) { 158 | drawColumn(Integer.valueOf(c), color, gp); 159 | } 160 | } 161 | } 162 | 163 | public static void drawRow(int y, int color, Grid gp) throws NumberFormatException, IndexOutOfBoundsException { 164 | for (int i = 0; i < gp.getWidth(); i++) { 165 | gp.set(i, y, (byte) color); 166 | } 167 | } 168 | 169 | public static void drawRows(String[] rr, int color, Grid gp) throws NumberFormatException, IndexOutOfBoundsException { 170 | for (String c : rr) { 171 | if (!c.trim().isEmpty()) { 172 | drawRow(Integer.valueOf(c), color, gp); 173 | } 174 | } 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/internals/training/TimeredTraining.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.internals.training; 7 | 8 | import java.awt.event.ActionEvent; 9 | import org.fbb.board.internals.grid.Boulder; 10 | import java.awt.event.ActionListener; 11 | import java.util.List; 12 | import java.util.Random; 13 | import javax.swing.JLabel; 14 | import org.fbb.board.desktop.TextToSpeech; 15 | import org.fbb.board.desktop.gui.awtimpl.MainWindowImpl; 16 | import org.fbb.board.internals.GuiLogHelper; 17 | 18 | /** 19 | * 20 | * @author jvanek 21 | */ 22 | public class TimeredTraining implements Runnable { 23 | 24 | private final ActionListener next, prev, nextRandom, nextGenerateRandom; 25 | private boolean running = true; 26 | private boolean paused = false; 27 | private int counter; 28 | private final JLabel output; 29 | private TextToSpeech.TextId speak; 30 | private final List ts; 31 | private int currentInList = 0; 32 | private final ActionListener clear; 33 | private final MainWindowImpl parent; 34 | 35 | public void setJumpingAllowed(boolean jumpingAllowed) { 36 | ts.get(currentInList).allowJumps.setSelected(jumpingAllowed); 37 | } 38 | 39 | public void setRandomAllowed(boolean randomAllowed) { 40 | ts.get(currentInList).allowRandom.setSelected(randomAllowed); 41 | } 42 | 43 | public void setRegularAllowed(boolean regularAllowed) { 44 | ts.get(currentInList).allowRegular.setSelected(regularAllowed); 45 | } 46 | 47 | public boolean getJumpingAllowed() { 48 | return ts.get(currentInList).allowJumps.isSelected(); 49 | } 50 | 51 | public boolean getRandomAllowed() { 52 | return ts.get(currentInList).allowRandom.isSelected(); 53 | } 54 | 55 | public boolean getRegularAllowed() { 56 | return ts.get(currentInList).allowRegular.isSelected(); 57 | } 58 | 59 | public boolean isPaused() { 60 | return paused; 61 | } 62 | 63 | public void setPaused(boolean paused) { 64 | this.paused = paused; 65 | } 66 | 67 | public void stop() { 68 | this.running = false; 69 | } 70 | 71 | public boolean isRunning() { 72 | return running; 73 | } 74 | 75 | public void setSpeak(TextToSpeech.TextId speak) { 76 | this.speak = speak; 77 | } 78 | 79 | public TimeredTraining(MainWindowImpl parent, ActionListener next, ActionListener prev, ActionListener nextRandom, ActionListener nextGenerateRandom, ActionListener clear, List ts, JLabel output, TextToSpeech.TextId speak) { 80 | this.parent = parent; 81 | this.next = next; 82 | this.prev = prev; 83 | this.nextRandom = nextRandom; 84 | this.nextGenerateRandom = nextGenerateRandom; 85 | this.clear=clear; 86 | this.output = output; 87 | this.speak = speak; 88 | this.ts = ts; 89 | counter = ts.get(currentInList).getTotalTime() + 1; 90 | } 91 | 92 | @Override 93 | public void run() { 94 | Random r = new Random(); 95 | try { 96 | while (consultList() && running && counter > 0) { 97 | if (!paused) { 98 | counter--; 99 | if (counter == ts.get(currentInList).getTotalTime()) { 100 | if (ts.get(currentInList).getInitialDelay() > 0) { 101 | TextToSpeech.pause(speak); 102 | int delay = ts.get(currentInList).getInitialDelay(); 103 | clear.actionPerformed(null); 104 | while (delay > 0) { 105 | if (!paused) { 106 | delay--; 107 | output.setText("Brak! " + delay / 60 + ":" + delay % 60); 108 | output.repaint(); 109 | Thread.sleep(1000); 110 | } 111 | } 112 | TextToSpeech.change(speak); 113 | } 114 | } 115 | output.setText(counter / 60 + ":" + counter % 60); 116 | if (ts.size() > 1) { 117 | output.setText(output.getText() + "(" + (currentInList + 1) + "/" + ts.size() + ")"); 118 | } 119 | output.repaint(); 120 | if (counter % ts.get(currentInList).getTimeOfBoulder() == 0) { 121 | if (!getRegularAllowed() && !getRandomAllowed()) { 122 | continue; 123 | } 124 | boolean choice1 = r.nextBoolean(); 125 | if (!getRegularAllowed() || (getRandomAllowed() && choice1)) { 126 | nextGenerateRandom.actionPerformed(null); 127 | } 128 | if (!getRandomAllowed() || (getRandomAllowed() && !choice1)) { 129 | if (getJumpingAllowed() && r.nextBoolean()) { 130 | int i = r.nextInt(parent.list.getSize()); 131 | i = i--; 132 | parent.list.setIndex(i); 133 | next.actionPerformed(new ActionEvent(this, 0, null)); 134 | } else { 135 | if (parent.list.canFwd()) { 136 | next.actionPerformed(new ActionEvent(this, 0, null)); 137 | } else { 138 | parent.list.setIndex(-1); 139 | next.actionPerformed(new ActionEvent(this, 0, null)); 140 | } 141 | } 142 | } 143 | Boulder b = parent.hm.getCurrentInHistory(); 144 | if (b.getGrade().isRandom()) { 145 | TextToSpeech.tellImpl(b.getGrade().toString(), speak); 146 | } else { 147 | TextToSpeech.tellImpl(b.getAuthor()+" "+b.getGrade().shorten()+" "+b.getName(), speak); 148 | } 149 | } 150 | } 151 | Thread.sleep(1000); 152 | } 153 | } catch (Exception ex) { 154 | GuiLogHelper.guiLogger.loge(ex); 155 | } 156 | } 157 | 158 | private boolean consultList() { 159 | if (running && counter <= 0) { 160 | currentInList++; 161 | if (currentInList >= ts.size()) { 162 | return false; 163 | } 164 | //init filter and aply initial delay 165 | int delay = ts.get(currentInList).getInitialDelay(); 166 | if (delay == 0) { 167 | TextToSpeech.change(speak); 168 | } 169 | counter = ts.get(currentInList).getTotalTime() + 1; 170 | TrainingWithBackends q = ts.get(currentInList); 171 | q.setBoulderCalc(); 172 | q.setChecks(); 173 | q.init(); 174 | } 175 | return true; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/tutorial/awt/HelpWindow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.tutorial.awt; 7 | 8 | import java.awt.BorderLayout; 9 | import java.awt.Color; 10 | import java.awt.Component; 11 | import java.awt.GridLayout; 12 | import java.awt.Rectangle; 13 | import java.awt.Window; 14 | import java.awt.event.ActionEvent; 15 | import java.awt.event.ActionListener; 16 | import java.awt.event.WindowAdapter; 17 | import java.awt.event.WindowEvent; 18 | import java.io.IOException; 19 | import java.util.concurrent.atomic.AtomicInteger; 20 | import javax.swing.JButton; 21 | import javax.swing.JDialog; 22 | import javax.swing.JLabel; 23 | import javax.swing.JPanel; 24 | import javax.swing.SwingUtilities; 25 | import org.fbb.board.Translator; 26 | import org.fbb.board.desktop.ScreenFinder; 27 | import org.fbb.board.desktop.tutorial.ImgProvider; 28 | import org.fbb.board.desktop.tutorial.StoryPart; 29 | import org.fbb.board.desktop.tutorial.StoryProvider; 30 | import org.fbb.board.internals.GuiLogHelper; 31 | 32 | /** 33 | * 34 | * @author jvanek 35 | */ 36 | public class HelpWindow extends JDialog { 37 | 38 | private final ImgProvider ip; 39 | private final JLabel welcome; 40 | private boolean running = true; 41 | private AtomicInteger index = new AtomicInteger(); 42 | private final StoryProvider sp; 43 | private ImageWithTitles iwt; 44 | private boolean isAlive = true; 45 | 46 | public HelpWindow() throws IOException { 47 | this.setTitle(Translator.R("HELP")); 48 | this.setModal(true); 49 | this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); 50 | this.setSize(900,1000);//from images 51 | setRelativeSize(this); 52 | sp = new StoryProvider(); 53 | ip = new ImgProvider(); 54 | welcome = new JLabel(Translator.R("nwBoulderWelcome")); 55 | welcome.setOpaque(true); 56 | this.add(welcome, BorderLayout.NORTH); 57 | final Color wc = welcome.getBackground(); 58 | setImg(); 59 | JPanel tools = new JPanel(new GridLayout(1, 3)); 60 | final JButton backb = new JButton("<<"); 61 | tools.add(backb); 62 | final JButton pauseb = new JButton("II"); 63 | tools.add(pauseb); 64 | final JButton fwdb = new JButton(">>"); 65 | tools.add(fwdb); 66 | this.add(tools, BorderLayout.SOUTH); 67 | pauseb.addActionListener(new ActionListener() { 68 | @Override 69 | public void actionPerformed(ActionEvent ae) { 70 | running = !running; 71 | if (running) { 72 | pauseb.setText("II"); 73 | } else { 74 | pauseb.setText(">"); 75 | } 76 | } 77 | }); 78 | fwdb.addActionListener(new ActionListener() { 79 | @Override 80 | public void actionPerformed(ActionEvent ae) { 81 | 82 | try { 83 | index.addAndGet(1); 84 | if (index.get() >= sp.getStory().size()) { 85 | index.set(sp.getStory().size() - 1); 86 | } 87 | setImg(); 88 | } catch (Exception ex) { 89 | GuiLogHelper.guiLogger.loge(ex); 90 | } 91 | } 92 | }); 93 | backb.addActionListener(new ActionListener() { 94 | @Override 95 | public void actionPerformed(ActionEvent ae) { 96 | try { 97 | index.addAndGet(-1); 98 | if (index.get() < 0) { 99 | index.set(0); 100 | } 101 | setImg(); 102 | } catch (Exception ex) { 103 | GuiLogHelper.guiLogger.loge(ex); 104 | } 105 | } 106 | }); 107 | Thread t1 = new Thread(new Runnable() { 108 | @Override 109 | public void run() { 110 | try { 111 | Thread.sleep(500); 112 | for (int i = 0; i < 6; i++) { 113 | Thread.sleep(200); 114 | if (i % 2 == 0) { 115 | welcome.setBackground(Color.yellow); 116 | } else { 117 | welcome.setBackground(wc); 118 | } 119 | welcome.repaint(); 120 | } 121 | } catch (Exception ex) { 122 | GuiLogHelper.guiLogger.loge(ex); 123 | } 124 | } 125 | }); 126 | t1.setDaemon(true); 127 | t1.start(); 128 | Thread t2 = new Thread(new Runner()); 129 | t2.setDaemon(true); 130 | t2.start(); 131 | 132 | this.addWindowListener(new WindowAdapter() { 133 | @Override 134 | public void windowClosed(WindowEvent we) { 135 | isAlive = false; 136 | } 137 | 138 | }); 139 | } 140 | 141 | private void setImg() throws IOException { 142 | if (index.get() < 0 || index.get() >= sp.getStory().size()) { 143 | return; 144 | } 145 | StoryPart start = sp.getStory().get(index.get()); 146 | if (iwt != null) { 147 | this.remove(iwt); 148 | } 149 | iwt = new ImageWithTitles(start.getLines(), ip.get(start.getImg())); 150 | this.add(iwt); 151 | this.validate(); 152 | } 153 | 154 | private void setRelativeSize(Window bis) { 155 | Rectangle size = ScreenFinder.getCurrentScreenSizeWithoutBounds(); 156 | double dw = (double) size.width / (double) bis.getWidth(); 157 | double dh = (double) size.height / (double) bis.getHeight(); 158 | double ratio = Math.min(dw, dh); 159 | ratio = ratio * 0.9d; // APROX 4/5 of screen 160 | double nwW = ratio * bis.getWidth(); 161 | double nwH = ratio * bis.getHeight(); 162 | bis.setSize((int)nwW, (int)nwH); 163 | } 164 | 165 | private class Runner implements Runnable { 166 | 167 | @Override 168 | public void run() { 169 | try { 170 | Thread.sleep(1000); 171 | } catch (Exception ex) { 172 | GuiLogHelper.guiLogger.loge(ex); 173 | } 174 | while (isAlive) { 175 | try { 176 | Thread.sleep(3000); 177 | if (running) { 178 | index.addAndGet(1); 179 | if (index.get() >= sp.getStory().size()) { 180 | index.set(0); 181 | } 182 | setImg(); 183 | } 184 | } catch (Exception ex) { 185 | GuiLogHelper.guiLogger.loge(ex); 186 | } 187 | } 188 | } 189 | } 190 | 191 | public static void show(final Component c) { 192 | SwingUtilities.invokeLater(new Runnable() { 193 | @Override 194 | public void run() { 195 | try { 196 | HelpWindow h = new HelpWindow(); 197 | h.setLocationRelativeTo(c); 198 | h.setVisible(true); 199 | } catch (IOException ex) { 200 | ex.printStackTrace(); 201 | } 202 | } 203 | }); 204 | 205 | } 206 | 207 | public static void main(String... args) { 208 | show(null); 209 | } 210 | 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/ArduinoWindow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui; 7 | 8 | import java.awt.BorderLayout; 9 | import java.awt.Dimension; 10 | import java.awt.GridLayout; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.ActionListener; 13 | import java.io.ByteArrayOutputStream; 14 | import java.io.File; 15 | import java.io.IOException; 16 | import java.io.PrintStream; 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.stream.Collectors; 20 | import javax.swing.JButton; 21 | import javax.swing.JComboBox; 22 | import javax.swing.JDialog; 23 | import javax.swing.JFrame; 24 | import javax.swing.JOptionPane; 25 | import javax.swing.JPanel; 26 | import javax.swing.JTabbedPane; 27 | import javax.swing.JTextField; 28 | import org.fbb.board.desktop.ScreenFinder; 29 | import org.fbb.board.internals.ContentReaderListener; 30 | import org.fbb.board.internals.GuiLogHelper; 31 | import org.fbb.board.internals.ProcessResult; 32 | import org.fbb.board.internals.ProcessWrapper; 33 | import org.fbb.board.internals.comm.ConnectionID; 34 | import org.fbb.board.internals.comm.wired.PortWork; 35 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 36 | import org.fife.ui.rsyntaxtextarea.SyntaxConstants; 37 | import org.fife.ui.rtextarea.RTextScrollPane; 38 | 39 | /** 40 | * 41 | * @author jvanek 42 | */ 43 | public class ArduinoWindow extends JDialog { 44 | 45 | public static void main(String... a) throws IOException { 46 | new ArduinoWindow(File.createTempFile("aaa", "bbb"), "aaa", "zz").setVisible(true); 47 | } 48 | 49 | private final File file; 50 | private final Object lock = new Object(); 51 | 52 | public ArduinoWindow(final File f, String content, String currentPort) { 53 | this.file = f; 54 | this.setModal(true); 55 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 56 | Dimension s = ScreenFinder.getCurrentScreenSizeWithoutBounds().getSize(); 57 | s.width = s.width / 6 * 5; 58 | s.height = s.height / 6 * 5; 59 | this.setSize(s); 60 | final RSyntaxTextArea readOnlySourceCode = new RSyntaxTextArea(content); 61 | readOnlySourceCode.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_C); 62 | readOnlySourceCode.setEditable(false); 63 | 64 | final RSyntaxTextArea readOnlyLog = new RSyntaxTextArea(""); 65 | readOnlyLog.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_C); 66 | readOnlyLog.setEditable(false); 67 | 68 | final JTabbedPane pane = new JTabbedPane(); 69 | 70 | pane.add(new RTextScrollPane(readOnlySourceCode)); 71 | pane.add(new RTextScrollPane(readOnlyLog)); 72 | pane.setTitleAt(0, "code"); 73 | pane.setTitleAt(1, "log"); 74 | this.add(pane); 75 | JPanel tools = new JPanel(new GridLayout(1, 3)); 76 | ConnectionID[] ports = new PortWork().listDevices(); 77 | if (ports.length == 0) { 78 | JOptionPane.showMessageDialog(null, "No ports! Maybe you are on bluetooh only? Do NOT execute run!"); 79 | } 80 | final JComboBox port = new JComboBox(ports); 81 | boolean found = false; 82 | for (int i = 0; i < ports.length; i++) { 83 | ConnectionID p = ports[i]; 84 | if (p.getId().equals(currentPort)) { 85 | port.setSelectedIndex(i); 86 | found = true; 87 | break; 88 | } 89 | } 90 | if (!found) { 91 | JOptionPane.showMessageDialog(null, "Warning, set port of `" + currentPort + "` not found in currently seen ports: `" + Arrays.toString(ports) + "`"); 92 | if (ports.length > 0) { 93 | JOptionPane.showMessageDialog(null, "maybe you are on bluetooth? Do NOT execute run!"); 94 | } 95 | } 96 | tools.add(port); 97 | final JTextField tf = new JTextField("arduino --upload {1} -v --port {2} #--board package:arch:board[:parameters]]"); 98 | tf.setEditable(false); 99 | tools.add(tf); 100 | JButton run = new JButton("run"); 101 | run.addActionListener(new ActionListener() { 102 | 103 | @Override 104 | public void actionPerformed(ActionEvent e) { 105 | pane.setSelectedIndex(1); 106 | new Thread() { 107 | public void run() { 108 | new ProcessRun().work(readOnlyLog, createCommand(tf.getText(), f, (ConnectionID) port.getSelectedItem())); 109 | } 110 | }.start(); 111 | } 112 | }); 113 | tools.add(run); 114 | this.add(tools, BorderLayout.SOUTH); 115 | } 116 | 117 | private String createCommand(String template, File f, ConnectionID port) { 118 | String s = template.replaceAll("#.*", ""); 119 | if (file == null) { 120 | s = s.replace("--upload", "").replace("{1}", ""); 121 | } else { 122 | s = s.replace("{1}", f.getAbsolutePath()); 123 | } 124 | if (port == null) { 125 | s = s.replace("--port", "").replace("{2}", ""); 126 | } else { 127 | //in wired work we work with connection directly, here we must translate manually 128 | String pp = port.getId(); 129 | if (!pp.startsWith("/dev/")) { 130 | pp = "/dev/" + pp; 131 | } 132 | s = s.replace("{2}", pp); 133 | } 134 | return s; 135 | } 136 | 137 | private class ProcessRun { 138 | 139 | public void work(final RSyntaxTextArea t, String cmd) { 140 | t.setText("Executing: " + cmd + "\n"); 141 | try { 142 | ProcessWrapper pw = new ProcessWrapper(cmd.split("\\s+")); 143 | pw.addStdErrListener(new ContentReaderListener() { 144 | @Override 145 | public void charReaded(char ch) { 146 | 147 | } 148 | 149 | @Override 150 | public void lineReaded(String s) { 151 | synchronized (lock) { 152 | t.setText(t.getText() + "# " + s); 153 | } 154 | } 155 | }); 156 | pw.addStdOutListener(new ContentReaderListener() { 157 | @Override 158 | public void charReaded(char ch) { 159 | 160 | } 161 | 162 | @Override 163 | public void lineReaded(String s) { 164 | synchronized (lock) { 165 | t.setText(t.getText() + "$ " + s); 166 | } 167 | } 168 | }); 169 | ProcessResult pr = pw.execute(); 170 | if (pr.deadlyException != null) { 171 | t.setText(t.getText() + etoText(pr.deadlyException)); 172 | } 173 | t.setText(t.getText() + "\n EXIT: " + pr.returnValue); 174 | } catch (Exception e) { 175 | GuiLogHelper.guiLogger.loge(e); 176 | String s = etoText(e); 177 | t.setText(t.getText() + "\n" + e.getMessage() + "\n" + s); 178 | } 179 | } 180 | 181 | private String etoText(Throwable e) { 182 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 183 | e.printStackTrace(new PrintStream(out)); 184 | String str = new String(out.toByteArray()); 185 | return str; 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/org/fbb/board/desktop/gui/LoadBackgroundOrImportOrLoadWall.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.fbb.board.desktop.gui; 7 | 8 | import java.awt.Color; 9 | import java.awt.GridLayout; 10 | import java.awt.event.ActionEvent; 11 | import java.awt.event.ActionListener; 12 | import java.io.File; 13 | import java.net.MalformedURLException; 14 | import java.net.URL; 15 | import javax.swing.JButton; 16 | import javax.swing.JFileChooser; 17 | import javax.swing.JLabel; 18 | import javax.swing.JPanel; 19 | import javax.swing.JTextField; 20 | import javax.swing.event.DocumentEvent; 21 | import javax.swing.event.DocumentListener; 22 | import org.fbb.board.Translator; 23 | import org.fbb.board.desktop.Files; 24 | import org.fbb.board.desktop.gui.awtimpl.WinUtils; 25 | import org.fbb.board.internals.GlobalSettings; 26 | import org.fbb.board.internals.GuiLogHelper; 27 | import org.fbb.board.internals.db.DB; 28 | import org.fbb.board.internals.db.Puller; 29 | 30 | /** 31 | * 32 | * @author jvanek 33 | */ 34 | public class LoadBackgroundOrImportOrLoadWall extends JPanel { 35 | 36 | private JTextField input; 37 | private JLabel info; 38 | private JLabel valid; 39 | private JButton select; 40 | private JButton ok; 41 | private JButton settings; 42 | 43 | private final DB db; 44 | private final GlobalSettings gs; 45 | private final Authenticator auth; 46 | private final Puller puller; 47 | 48 | public static String getDefaultUrl() { 49 | try { 50 | return Files.wallsDir.toURI().toURL().toString(); 51 | } catch (MalformedURLException ex) { 52 | GuiLogHelper.guiLogger.loge(ex); 53 | return Files.wallsDir.getAbsolutePath(); 54 | } 55 | } 56 | 57 | public void setOkAction(ActionListener l) { 58 | ok.addActionListener(l); 59 | } 60 | 61 | public LoadBackgroundOrImportOrLoadWall(String defaultPath, Authenticator auth, DB db, GlobalSettings gs, Puller puller) { 62 | this.auth = auth; 63 | this.db = db; 64 | this.gs = gs; 65 | this.puller = puller; 66 | 67 | input = new JTextField(defaultPath); 68 | valid = new JLabel(); 69 | input.getDocument().addDocumentListener(new DocumentListener() { 70 | @Override 71 | public void insertUpdate(DocumentEvent e) { 72 | validateUrl(); 73 | } 74 | 75 | @Override 76 | public void removeUpdate(DocumentEvent e) { 77 | validateUrl(); 78 | } 79 | 80 | @Override 81 | public void changedUpdate(DocumentEvent e) { 82 | validateUrl(); 83 | } 84 | 85 | }); 86 | validateUrl(); 87 | //password? 88 | info = new JLabel(Translator.R("MainWindowSetWallInfo")); 89 | ok = new JButton(Translator.R("Bfinish")); 90 | if (Files.repoGit.exists()) { 91 | settings = new JButton(Translator.R("Settings")); 92 | } else { 93 | settings = new JButton(Translator.R("BinitialInit")); 94 | } 95 | settings.addActionListener(new SettingsListener(null, auth, gs, puller, db, 3, null)); 96 | select = new JButton(Translator.R("Bselect")); 97 | select.addActionListener((ActionEvent e) -> { 98 | JFileChooser jf; 99 | try { 100 | URL u = new URL(input.getText()); 101 | jf = new JFileChooser(u.getPath()); 102 | } catch (MalformedURLException ex) { 103 | GuiLogHelper.guiLogger.loge(ex); 104 | jf = new JFileChooser(input.getText()); 105 | } 106 | jf.setDialogType(JFileChooser.OPEN_DIALOG); 107 | int returnVal = jf.showDialog(LoadBackgroundOrImportOrLoadWall.this, Translator.R("Bok")); 108 | 109 | if (returnVal == JFileChooser.APPROVE_OPTION) { 110 | try { 111 | input.setText(jf.getSelectedFile().toURI().toURL().toExternalForm()); 112 | } catch (MalformedURLException ex) { 113 | GuiLogHelper.guiLogger.loge(ex); 114 | } 115 | } 116 | }); 117 | this.setLayout(new GridLayout(4, 1)); 118 | JPanel p = new JPanel(new GridLayout(1, 3)); 119 | this.add(info); 120 | this.add(input); 121 | this.add(valid); 122 | p.add(ok); 123 | p.add(select); 124 | p.add(settings); 125 | this.add(p); 126 | } 127 | 128 | private void validateUrl() { 129 | int i = validateImp(); 130 | switch (i) { 131 | case 0: 132 | valid.setForeground(Color.red); 133 | valid.setText(Translator.R("EmptyUrl")); 134 | break; 135 | case 1: 136 | valid.setForeground(Color.green); 137 | valid.setText(WinUtils.addCtrLine(Translator.R("ExistsFile"))); 138 | break; 139 | case 11: 140 | valid.setForeground(Color.red); 141 | valid.setText(Translator.R("ExistsDir")); 142 | break; 143 | case 2: 144 | valid.setForeground(Color.orange); 145 | valid.setText(WinUtils.addCtrLine(Translator.R("NotExistsFile"))); 146 | break; 147 | case 3: 148 | valid.setForeground(Color.green); 149 | valid.setText(WinUtils.addCtrLine(Translator.R("OkRemoteUrl"))); 150 | break; 151 | default: 152 | valid.setForeground(Color.red); 153 | valid.setText("?????"); 154 | break; 155 | } 156 | } 157 | 158 | private int validateImp() { 159 | String candidate = input.getText(); 160 | if (candidate == null || candidate.trim().isEmpty()) { 161 | //empty 162 | return 0; 163 | } 164 | URL u; 165 | try { 166 | u = new URL(candidate); 167 | if (u.getProtocol().toLowerCase().equals("file")) { 168 | String fileUrl = u.getPath(); 169 | if (new File(fileUrl).exists()) { 170 | if (new File(fileUrl).isDirectory()) { 171 | return 11; 172 | } else { 173 | //existing fiel url 174 | return 1; 175 | } 176 | } else { 177 | //non existing file url 178 | return 2; 179 | } 180 | } else { 181 | //valid remote url 182 | return 3; 183 | } 184 | } catch (MalformedURLException ex) { 185 | GuiLogHelper.guiLogger.loge(ex); 186 | if (new File(candidate).exists()) { 187 | if (new File(candidate).isDirectory()) { 188 | return 11; 189 | } else { 190 | //existing fiel url 191 | return 1; 192 | } 193 | } else { 194 | return 2; 195 | } 196 | } 197 | 198 | } 199 | 200 | public URL getResult() { 201 | try { 202 | URL u = new URL(input.getText()); 203 | return u; 204 | } catch (MalformedURLException ex) { 205 | GuiLogHelper.guiLogger.loge(ex); 206 | try { 207 | return new File(info.getText()).toURI().toURL(); 208 | } catch (MalformedURLException eex) { 209 | throw new RuntimeException(eex); 210 | } 211 | } 212 | } 213 | } 214 | --------------------------------------------------------------------------------