Blimp v.0.2 (2004-11-10)
Christian Heimke

Christian Heimke commited on 2011-07-15 09:15:31
Showing 41 changed files, with 5186 additions and 0 deletions.

... ...
@@ -0,0 +1,21 @@
1
+<html>
2
+  <head>
3
+    <title>Blimp - BlinkenLightsInteractiveMovieProgram</title>
4
+  </head>
5
+  <script language="JavaScript">
6
+    function Resize( )
7
+    {
8
+      var applet, height, width;
9
+      height = document.body.clientHeight - 20;
10
+      width = document.body.clientWidth - 20
11
+      applet = document.Blimp;
12
+      applet.width = width;
13
+      applet.height = height;
14
+      applet.setSize( width, height );
15
+    }
16
+  </script>
17
+  <body onLoad="Resize( );" onResize="Resize( );">
18
+    <applet archive="Blimp.jar" code="Blimp.class" height="420" name="Blimp" width="560">
19
+    </applet>
20
+  </body>
21
+</html>
... ...
@@ -0,0 +1,1612 @@
1
+/* BlinkenLightsInteractiveMovieProgram
2
+ * version 0.2 date 2004-11-10
3
+ * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ * powered by eventphone.de
7
+ */
8
+
9
+import java.applet.*;
10
+import java.awt.*;
11
+import java.awt.event.*;
12
+import java.awt.image.*;
13
+import javax.swing.*;
14
+import javax.swing.border.*;
15
+import javax.swing.event.*;
16
+import java.io.*;
17
+import java.util.*;
18
+import java.util.regex.*;
19
+import java.net.*;
20
+
21
+public class Blimp extends JApplet
22
+             implements Runnable, WindowListener, ActionListener,
23
+                        AdjustmentListener, ChangeListener, FocusListener,
24
+                        DocumentListener, BlinkenFrameEditorListener
25
+{
26
+  //configuration constants
27
+  static final int constColorCntX = 2, constColorCntY = 4;
28
+  static final int constColorCnt = constColorCntX * constColorCntY;
29
+
30
+  //configuration variables
31
+  boolean isFullApp = false; //if running as full application
32
+
33
+  //GUI elements
34
+  JFrame frame; //main window (if running as full application)
35
+  Component dialogParent; //parent to use for dialogs
36
+  JMenuBar menubar; //menubar in main window
37
+  JMenu menuFile, menuInfo, menuEdit, menuPlay, menuHelp; //menus
38
+  JMenuItem menuFileNew, menuFileLoad, menuFileSave, menuFileSaveAs, menuFileQuit;
39
+  JMenuItem menuInfoShow, menuInfoAdd, menuInfoDelete;
40
+  JMenuItem menuEditResize, menuEditInsertFrame, menuEditDuplicateFrame, menuEditDeleteFrame;
41
+  JMenuItem menuPlayStart, menuPlayStop;
42
+  JCheckBoxMenuItem menuPlayBegin, menuPlayLoop;
43
+  JMenuItem menuHelpAbout;
44
+  JPanel panel, panelStatus, panelMain, panelFrames, panelOuterFrame; //panels of in window
45
+  JPanel panelMiddleFrame, panelFrame, panelDuration, panelColors;
46
+  JLabel labelStatus, labelFrames, labelFrameInfo, labelDuration;
47
+  JScrollBar scrollFrames;
48
+  JSlider sliderFrameZoom;
49
+  BlinkenFrameEditor frameEditor;
50
+  JScrollPane scrollpaneFrame;
51
+  JTextField textDuration;
52
+  JPanel panelOuterTools, panelMiddleTools, panelTools, panelActions;
53
+  JToggleButton buttonToolsNone, buttonToolsColorPicker, buttonToolsDot, buttonToolsLine;
54
+  JToggleButton buttonToolsRect, buttonToolsFilledRect, buttonToolsCircle, buttonToolsFilledCircle;
55
+  JToggleButton buttonToolsCopy, buttonToolsPaste;
56
+  ButtonGroup groupTools;
57
+  JButton buttonActionsInvert, buttonActionsRotate90, buttonActionsRotate180, buttonActionsRotate270;
58
+  JButton buttonActionsMirrorHor, buttonActionsMirrorVer, buttonActionsMirrorDiag, buttonActionsMirrorDiag2;
59
+  JButton buttonActionsRollLeft, buttonActionsRollRight, buttonActionsRollUp, buttonActionsRollDown;
60
+  JButton buttonActionsUndo, buttonActionsRedo;
61
+  JPanel panelColorsChoose, panelColorsSettings, panelColorsColor, panelColorsAlpha;
62
+  JToggleButton buttonsColor[];
63
+  ButtonGroup groupColor;
64
+  JLabel labelColorsColor, labelColorsAlpha;
65
+  JButton buttonColorsColor;
66
+  JSlider sliderColorsAlpha;
67
+  JTextField textColorsColor, textColorsAlpha;
68
+
69
+  //other variables
70
+  int colorIdx; //index of selected color
71
+  Color colors[]; //current colors
72
+  ImageIcon iconsColor[], iconColorsColor; //color icons shown in color panel 
73
+  javax.swing.Timer timerPlay; //timer used for playing movies
74
+
75
+  //file, movie, frame
76
+  File curDir = null, curFile = null; //current directory and file
77
+  BlinkenMovie curMovie = null; //current movie
78
+  boolean curMovieChanged = false; //if changes have been made to current movie
79
+  BlinkenFrame curFrame = null; //current frame
80
+
81
+  //constructor for applet
82
+  public Blimp( )
83
+  {
84
+    this.isFullApp = false;
85
+  }
86
+
87
+  //constructor for full application (isFullApp == true) or applet (isFullAll == false)
88
+  public Blimp( boolean isFullApp )
89
+  {
90
+    this.isFullApp = isFullApp;
91
+  }
92
+
93
+  //load an image
94
+  private ImageIcon loadImageIcon( String name )
95
+  {
96
+    URL url = Blimp.class.getResource( "images/" + name );
97
+    if( url != null )
98
+      return new ImageIcon( url );
99
+    else
100
+      return new ImageIcon( new BufferedImage( 1, 1, BufferedImage.TYPE_INT_RGB ) );
101
+  }
102
+
103
+  //perhaps ask if to save changes and perhaps do it
104
+  //return true on cancel
105
+  private boolean askSaveChanges( )
106
+  {
107
+    int retVal;
108
+
109
+    //ask only when changes were made
110
+    if( curMovieChanged )
111
+    {
112
+      //ask if to save changes
113
+      retVal = JOptionPane.showConfirmDialog( dialogParent,
114
+                                              "Do You want to save the changes?",
115
+                                              "Blimp - Save changes?",
116
+                                              JOptionPane.YES_NO_CANCEL_OPTION,
117
+                                              JOptionPane.QUESTION_MESSAGE );
118
+      //cancelled
119
+      if( retVal == JOptionPane.CANCEL_OPTION )
120
+        return true;
121
+      //save
122
+      if( retVal == JOptionPane.YES_OPTION )
123
+        actionFileSave( );
124
+    }
125
+
126
+    //not cancelled
127
+    return false;
128
+  }
129
+
130
+  //"File New" was chosen from menu
131
+  private void actionFileNew( )
132
+  {
133
+    //ask if to save changes
134
+    if( askSaveChanges( ) ) //returns true on cancel
135
+      return;
136
+
137
+    //create a new movie
138
+    if( frame != null )
139
+      frame.setTitle( "Blimp" );
140
+    labelStatus.setText( "new movie..." );
141
+    curFile = null;
142
+    curMovie = new BlinkenMovie( 0, 0, 0, 0 );
143
+    curMovie.insertInfo( 0, "creator", "Blimp (version 0.2 date 2004-11-10)" );
144
+    curMovieChanged = false;
145
+
146
+    //update controls
147
+    updateFrames( 0 );
148
+  }
149
+
150
+  //"File Load" was chosen from menu
151
+  private void actionFileLoad( )
152
+  {
153
+    JFileChooser fileChooser;
154
+
155
+    //ask if to save changes
156
+    if( askSaveChanges( ) ) //returns true on cancel
157
+      return;
158
+
159
+    //show file select dialog
160
+    fileChooser = new JFileChooser( );
161
+    fileChooser.setDialogTitle( "Blimp - Load..." );
162
+    fileChooser.setFileFilter( new BlinkenFileFilter( ) );
163
+    if( curDir != null )
164
+      fileChooser.setCurrentDirectory( curDir );
165
+    if( fileChooser.showOpenDialog( dialogParent ) == JFileChooser.APPROVE_OPTION )
166
+    {
167
+      //save current directory and current file
168
+      curDir = fileChooser.getCurrentDirectory( );
169
+      curFile = fileChooser.getSelectedFile( );
170
+      //load file
171
+      if( curMovie.load( curFile.getPath( ) ) )
172
+      {
173
+        //success
174
+        if( frame != null )
175
+          frame.setTitle( "Blimp - " + curFile.getPath( ) );
176
+        labelStatus.setText( "movie \"" + curFile.getPath( ) +  "\" was loaded successfully..." );
177
+        curMovieChanged = false;
178
+      }
179
+      else
180
+      {
181
+        //some error
182
+        if( frame != null )
183
+          frame.setTitle( "Blimp" );
184
+        labelStatus.setText( "movie \"" + curFile.getPath( ) +  "\" could not be loaded..." );
185
+        curFile = null;
186
+        curMovieChanged = false;
187
+      }
188
+    }
189
+
190
+    //update controls
191
+    updateFrames( 0 );
192
+  }
193
+
194
+  //"File Save" was chosen from menu
195
+  private void actionFileSave( )
196
+  {
197
+    //just call "File Save as" if no current file
198
+    if( curFile == null )
199
+    {
200
+      actionFileSaveAs( );
201
+      return;
202
+    }
203
+    //save file
204
+    if( curMovie.save( curFile.getPath( ) ) )
205
+    {
206
+      //success
207
+      labelStatus.setText( "movie \"" + curFile.getPath( ) +  "\" was saved successfully..." );
208
+      curMovieChanged = false;
209
+    }
210
+    else
211
+    {
212
+      //some error
213
+      labelStatus.setText( "movie \"" + curFile.getPath( ) +  "\" could not be saved..." );
214
+    }
215
+  }
216
+
217
+  //"File Save as" was chosen from menu
218
+  private void actionFileSaveAs( )
219
+  {
220
+    JFileChooser fileChooser;
221
+
222
+    //show file select dialog
223
+    fileChooser = new JFileChooser( );
224
+    fileChooser.setDialogTitle( "Blimp - Save as..." );
225
+    fileChooser.setFileFilter( new BlinkenFileFilter( ) );
226
+    if( curDir != null )
227
+      fileChooser.setCurrentDirectory( curDir );
228
+    if( curFile != null )
229
+      fileChooser.setSelectedFile( curFile );
230
+    if( fileChooser.showSaveDialog( dialogParent ) == JFileChooser.APPROVE_OPTION )
231
+    {
232
+      //save current directory and file
233
+      curDir = fileChooser.getCurrentDirectory( );
234
+      curFile = fileChooser.getSelectedFile( );
235
+      if( frame != null )
236
+        frame.setTitle( "Blimp - " + curFile.getPath( ) );
237
+      //just call "File Save" to do the work
238
+      actionFileSave( );
239
+    }
240
+  }
241
+
242
+  //"File Quit" was chosen from menu
243
+  private void actionFileQuit( )
244
+  {
245
+    JFileChooser fileChooser;
246
+
247
+    //ask if to save changes
248
+    if( askSaveChanges( ) ) //returns true on cancel
249
+      return;
250
+
251
+    //only end program if runnning as full application
252
+    if( isFullApp )
253
+      System.exit( 0 );
254
+  }
255
+  
256
+  //"Information Show..." was chosen from menu
257
+  private void actionInfoShow( )
258
+  {
259
+    int i, cnt;
260
+    String info;
261
+
262
+    //get information about movie
263
+    info = "";
264
+    cnt = curMovie.getInfoCnt( );
265
+    for( i = 0; i < cnt; i++ )
266
+      info += "\n" + curMovie.getInfoType( i ) +
267
+              ": " + curMovie.getInfoData( i );
268
+
269
+    //show information
270
+    JOptionPane.showMessageDialog( dialogParent,
271
+                                   "Information about movie:\n" + info,
272
+                                   "Blimp - Show Information...",
273
+                                   JOptionPane.INFORMATION_MESSAGE );
274
+  }
275
+
276
+  //"Information Add..." was chosen from menu
277
+  private void actionInfoAdd( )
278
+  {
279
+    Pattern infoPattern;
280
+    Object info;
281
+    Matcher infoMatcher;
282
+
283
+    //initialize info pattern
284
+    infoPattern = Pattern.compile( "^([A-Za-z0-9]+)(?: *= *|: *)(.*)$" );
285
+
286
+    //ask for information to add
287
+    info = JOptionPane.showInputDialog( dialogParent,
288
+                                        "Please enter the information to add:\n\n" +
289
+                                        "The format is:   <info-type>: <info-text>\n" + 
290
+                                        "     title: <title of movie>\n" + 
291
+                                        "     description: <short description of movie content>\n" + 
292
+                                        "     creator: <program this movie was created with>\n" + 
293
+                                        "     author: <name of author(s)>\n" + 
294
+                                        "     email: <email address of author>\n" + 
295
+                                        "     url: <homepage of author or of this movie>", 
296
+                                        "Blimp - Add Information...",
297
+                                        JOptionPane.QUESTION_MESSAGE,
298
+                                        null, null, "" );
299
+    //dialog was cancelled
300
+    if( info == null )
301
+      return;
302
+
303
+    //add info
304
+    if( (infoMatcher = infoPattern.matcher( info.toString( ) )).find( ) )
305
+      curMovie.insertInfo( curMovie.getInfoCnt( ), infoMatcher.group( 1 ), infoMatcher.group( 2 ) );
306
+    else
307
+      curMovie.insertInfo( curMovie.getInfoCnt( ), "description", info.toString( ) );
308
+    curMovieChanged = true;
309
+  }
310
+
311
+  //"Information Delete..." was chosen from menu
312
+  private void actionInfoDelete( )
313
+  {
314
+    int i, cnt;
315
+    String info[];
316
+    Object selected;
317
+
318
+    //get information about movie
319
+    cnt = curMovie.getInfoCnt( );
320
+    info = new String[cnt];
321
+    for( i = 0; i < cnt; i++ )
322
+      info[i] = curMovie.getInfoType( i ) + ": " + 
323
+                curMovie.getInfoData( i );
324
+
325
+    //ask for new size
326
+    selected = JOptionPane.showInputDialog( dialogParent,
327
+                                            "Select information to delete:",
328
+                                            "Blimp - Delete Information...",
329
+                                            JOptionPane.QUESTION_MESSAGE,
330
+                                            null, info, null );
331
+    //dialog was cancelled
332
+    if( selected == null )
333
+      return;
334
+
335
+    //delete sected information
336
+    for( i = 0; i < cnt; i++ )
337
+      if( info[i] == selected )
338
+        break;
339
+    if( i < cnt )
340
+    {
341
+      curMovie.deleteInfo( i );
342
+      curMovieChanged = true;
343
+    }
344
+  }
345
+
346
+  //"Edit Resize Movie..." was chosen from menu
347
+  private void actionEditResize( )
348
+  {
349
+    Pattern sizePattern;
350
+    String curSize;
351
+    Object size;
352
+    Matcher sizeMatcher;
353
+
354
+    //initialize size pattern
355
+    sizePattern = Pattern.compile( "^([0-9]+)x([0-9]+)-([0-9]+)/([0-9]+)$" );
356
+
357
+    //get string with current movie size
358
+    curSize = curMovie.getWidth( ) + "x" +
359
+              curMovie.getHeight( ) + "-" + 
360
+              curMovie.getChannels( ) + "/" + 
361
+              (curMovie.getMaxval( ) + 1);
362
+
363
+    //ask until cancel or answer is valid
364
+    size = curSize;
365
+    do
366
+    {
367
+      //ask for new size
368
+      size = JOptionPane.showInputDialog( dialogParent,
369
+                                          "Current movie size is:   " + curSize + "\n\n" +
370
+                                          "The format is:   <width>x<height>-<channels>/<colors>\n" + 
371
+                                          "     18x8-1/2     (Blinkenlights)\n" + 
372
+                                          "     18x8-1/16     (Blinkenlights Reloaded)\n" + 
373
+                                          "     26x20-1/16     (Blinkenlights Arcade)\n" + 
374
+                                          "     104x32-1/128     (TROIA big walls)\n" + 
375
+                                          "     80x32-1/128     (TROIA small walls)\n" + 
376
+                                          "     104x80-1/128     (TROIA floor + ceiling)\n\n" + 
377
+                                          "Please enter the new movie size:",
378
+                                          "Blimp - Resize Movie...",
379
+                                          JOptionPane.QUESTION_MESSAGE,
380
+                                          null, null, size );
381
+      //dialog was cancelled
382
+      if( size == null )
383
+        return;
384
+    }
385
+    while( ! (sizeMatcher = sizePattern.matcher( size.toString( ) )).find( ) ); //repeat question if answer not valid
386
+
387
+    //resize movie
388
+    curMovie.resize( Integer.parseInt( sizeMatcher.group( 2 ) ),
389
+                     Integer.parseInt( sizeMatcher.group( 1 ) ),
390
+                     Integer.parseInt( sizeMatcher.group( 3 ) ),
391
+                     Integer.parseInt( sizeMatcher.group( 4 ) ) - 1 );
392
+    curMovieChanged = true;
393
+
394
+    //update controls
395
+    updateFrames( scrollFrames.getValue( ) );
396
+
397
+    //update status
398
+    labelStatus.setText( "movie resized successfully to " + size.toString( ) + "..." );
399
+  }
400
+
401
+  //"Edit Insert Frame" was chosen from menu
402
+  private void actionEditInsertFrame( )
403
+  {
404
+    BlinkenFrame frame;
405
+    int frameCnt, frameNo;
406
+
407
+    //create new empty frame
408
+    frame = new BlinkenFrame( curMovie.getHeight( ), curMovie.getWidth( ),
409
+                              curMovie.getChannels( ), curMovie.getMaxval( ), 100 );
410
+    frame.clear( );
411
+
412
+    //insert frame behind current position
413
+    frameCnt = curMovie.getFrameCnt( );
414
+    frameNo = scrollFrames.getValue( ) + 1;
415
+    if( frameNo < 0 )
416
+      frameNo = 0;
417
+    if( frameNo > frameCnt )
418
+      frameNo = frameCnt;
419
+    curMovie.insertFrame( frameNo, frame );
420
+    curMovieChanged = true;
421
+
422
+    //update controls
423
+    updateFrames( frameNo );
424
+  }
425
+
426
+  //"Edit Duplicate Frame" was chosen from menu
427
+  private void actionEditDuplicateFrame( )
428
+  {
429
+    BlinkenFrame frame;
430
+    int frameCnt, frameNo;
431
+
432
+    //do nothing if there is no current frame
433
+    if( curFrame == null )
434
+      return;
435
+
436
+    //duplicate current frame
437
+    frame = new BlinkenFrame( curFrame );
438
+
439
+    //insert frame behind current position
440
+    frameCnt = curMovie.getFrameCnt( );
441
+    frameNo = scrollFrames.getValue( ) + 1;
442
+    if( frameNo < 0 )
443
+      frameNo = 0;
444
+    if( frameNo > frameCnt )
445
+      frameNo = frameCnt;
446
+    curMovie.insertFrame( frameNo, frame );
447
+    curMovieChanged = true;
448
+
449
+    //update controls
450
+    updateFrames( frameNo );
451
+  }
452
+
453
+  //"Edit Delete Frame" was chosen from menu
454
+  private void actionEditDeleteFrame( )
455
+  {
456
+    int frameNo;
457
+
458
+    //do nothing if there is no current frame
459
+    if( curFrame == null )
460
+      return;
461
+
462
+    //delete current frame
463
+    frameNo = scrollFrames.getValue( );
464
+    curMovie.deleteFrame( frameNo );
465
+    frameNo--;
466
+    curMovieChanged = true;
467
+
468
+    //update controls
469
+    updateFrames( frameNo );
470
+  }
471
+
472
+  //"Play Start" was chosen from menu
473
+  private void actionPlayStart( )
474
+  {
475
+    //select no tool
476
+    buttonToolsNone.setSelected( true );
477
+    frameEditor.setTool( BlinkenFrameEditor.toolNone );
478
+
479
+    //disable start, enable stop
480
+    menuPlayStart.setEnabled( false );
481
+    menuPlayStop.setEnabled( true );
482
+
483
+    //stop old play timer
484
+    timerPlay.stop( );
485
+
486
+    //if play shall start from beginning
487
+    if( menuPlayBegin.getState( ) )
488
+    {
489
+      //show first frame
490
+      if( scrollFrames.getValue( ) != 0 ) //value changes
491
+        scrollFrames.setValue( 0 ); //play timer will be started again when frame is being shown by scrollbar callback
492
+      else //value does not change
493
+        stateFrames( ); //value does not change, no event will be sent, execute callback by hand
494
+    }
495
+
496
+    //start play timer
497
+    if( curFrame == null )
498
+      timerPlay.setInitialDelay( 100 ); //use 100ms as default
499
+    else
500
+      timerPlay.setInitialDelay( curFrame.getDuration( ) );
501
+    timerPlay.restart( );
502
+  }
503
+
504
+  //"Play Stop" was chosen from menu
505
+  private void actionPlayStop( )
506
+  {
507
+    //stop play timer
508
+    timerPlay.stop( );
509
+
510
+    //enable start, disable stop
511
+    menuPlayStart.setEnabled( true );
512
+    menuPlayStop.setEnabled( false );
513
+  }
514
+
515
+  //play timer elapsed
516
+  private void actionPlayTimer( )
517
+  {
518
+    int frameCnt, frameNoOld, frameNoNew;
519
+
520
+    //stop play timer
521
+    timerPlay.stop( );
522
+
523
+    //get number of next frame
524
+    frameCnt = curMovie.getFrameCnt( );
525
+    frameNoOld = scrollFrames.getValue( );
526
+    frameNoNew = frameNoOld + 1;
527
+    if( frameNoNew >= frameCnt )
528
+    {
529
+      frameNoNew = 0;
530
+      //stop playing if looping is not requested
531
+      if( ! menuPlayLoop.getState( ) )
532
+      {
533
+        //enable start, disable stop
534
+        menuPlayStart.setEnabled( true );
535
+        menuPlayStop.setEnabled( false );
536
+        return;
537
+      }
538
+    }
539
+
540
+    //show next frame
541
+    if( frameNoNew != frameNoOld ) //value changes
542
+      scrollFrames.setValue( frameNoNew ); //play timer will be started again when frame is being shown by scrollbar callback
543
+    else //value does not change
544
+      stateFrames( ); //value does not change, no event will be sent, execute callback by hand
545
+  }
546
+
547
+  //"Help About" was chosen from menu
548
+  private void actionHelpAbout( )
549
+  {
550
+    JOptionPane.showMessageDialog( dialogParent,
551
+                                   "BlinkenLightsInteractiveMovieProgram\n" +
552
+                                   "version 0.2 date 2004-11-10\n" +
553
+                                   "Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>\n" +
554
+                                   "Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html\n" +
555
+                                   "a blinkenarea.org project\n" +
556
+                                   "powered by eventphone.de",
557
+                                   "Blimp - About...",
558
+                                   JOptionPane.INFORMATION_MESSAGE );
559
+  }
560
+
561
+  //update frames controls (and go to certaint frame)
562
+  private void updateFrames( int frameNo )
563
+  {
564
+    int frameCnt, frameInc;
565
+
566
+    //update frames scrollbar range
567
+    frameCnt = curMovie.getFrameCnt( );
568
+    if( frameCnt <= 0 )
569
+    {
570
+      scrollFrames.setValues( 0, 0, 0, 0 );
571
+      scrollFrames.setBlockIncrement( 1 );
572
+    }
573
+    else
574
+    {
575
+      if( frameNo < 0 )
576
+        frameNo = 0;
577
+      if( frameNo >= frameCnt )
578
+        frameNo = frameCnt - 1;
579
+      scrollFrames.setValues( frameNo, 1, 0, frameCnt );
580
+      frameInc = (int)Math.sqrt( frameCnt );
581
+      if( frameInc < 1 )
582
+        frameInc = 1;
583
+      scrollFrames.setBlockIncrement( frameInc );
584
+    }
585
+
586
+    //simulate frames scrollbar change to propagate update
587
+    stateFrames( );
588
+
589
+    //enable disable some menu commands which need a current frame
590
+    menuEditDuplicateFrame.setEnabled( frameCnt > 0 );
591
+    menuEditDeleteFrame.setEnabled( frameCnt > 0 );
592
+  }
593
+
594
+  //frames scrollbar changed
595
+  private void stateFrames( )
596
+  {
597
+    int frameCnt, frameNo;
598
+
599
+    //update frames scrollbar label
600
+    //get current frame
601
+    frameCnt = curMovie.getFrameCnt( );
602
+    if( frameCnt <= 0 )
603
+    {
604
+      labelFrames.setText( "frame: -/0" );
605
+      curFrame = null;
606
+    }
607
+    else
608
+    {
609
+      frameNo = scrollFrames.getValue( );
610
+      labelFrames.setText( "frame: " + (frameNo + 1) + "/" + frameCnt );
611
+      curFrame = curMovie.getFrame( frameNo );
612
+    }
613
+
614
+    //update frame
615
+    frameEditor.setFrame( curFrame );
616
+
617
+    //show duration
618
+    showDuration( );
619
+
620
+    //if currently playing
621
+    if( ! menuPlayStart.isEnabled( ) )
622
+    {
623
+      //stop play timer
624
+      timerPlay.stop( );
625
+      //start play timer
626
+      if( curFrame == null )
627
+        timerPlay.setInitialDelay( 100 ); //use 100ms as default
628
+      else
629
+        timerPlay.setInitialDelay( curFrame.getDuration( ) );
630
+      timerPlay.restart( );
631
+    }
632
+  }
633
+
634
+  //frame zoom changed
635
+  private void stateFrameZoom( )
636
+  {
637
+    //update frame
638
+    frameEditor.setZoom( sliderFrameZoom.getValue( ) );
639
+  }
640
+
641
+  //show duration
642
+  private void showDuration( )
643
+  {
644
+    if( curFrame == null )
645
+    {
646
+      textDuration.setEnabled( false );
647
+      textDuration.setText( "" );
648
+    }
649
+    else
650
+    {
651
+      textDuration.setEnabled( true );
652
+      textDuration.setText( "" + curFrame.getDuration( ) );
653
+    }
654
+  }
655
+
656
+  //new frame duration is being entered
657
+  private void changeDuration( )
658
+  {
659
+    int duration;
660
+
661
+    try
662
+    {
663
+      //get new frame duration
664
+      duration = Integer.parseInt( textDuration.getText( ) );
665
+
666
+      //write new duration into frame (if it really changed)
667
+      if( curFrame != null && curFrame.getDuration( ) != duration )
668
+      {
669
+        curFrame.setDuration( duration );
670
+        curMovieChanged = true;
671
+      }
672
+    }
673
+    catch( NumberFormatException e ) { }
674
+  }
675
+
676
+  //new frame duration was entered
677
+  private void validateDuration( )
678
+  {
679
+    //process changes made to duration
680
+    changeDuration( );
681
+
682
+    //redisplay new duration
683
+    showDuration( );
684
+  }
685
+
686
+  //generate a color icon from a color
687
+  private void iconFromColor( ImageIcon icon, Color color )
688
+  {
689
+    int height, width, y, x;
690
+    boolean yy, xx;
691
+    Graphics graphics;
692
+
693
+    //get size
694
+    height = icon.getIconHeight( );
695
+    width = icon.getIconWidth( );
696
+
697
+    //get graphics context of icon's image
698
+    graphics = icon.getImage( ).getGraphics( );
699
+
700
+    //draw background
701
+    graphics.setColor( new Color( color.getRed( ), color.getGreen( ), color.getBlue( ) ) );
702
+    graphics.fillRect( 0, 0, width / 2, height );
703
+    for( y = 0, yy = false; y < height; y += height / 4, yy = ! yy )
704
+    {
705
+      for( x = width / 2, xx = yy; x < width; x += width / 6, xx = ! xx )
706
+      {
707
+        if( xx )
708
+          graphics.setColor( Color.white );
709
+        else
710
+          graphics.setColor( Color.black );
711
+        graphics.fillRect( x, y, width / 6, height / 4 );
712
+      }
713
+    }
714
+
715
+    //draw foreground in specified color
716
+    graphics.setColor( color );
717
+    graphics.fillRect( 0, 0, width, height );
718
+  }
719
+
720
+  //a color was chosen
721
+  private void actionColorIdx( int idx )
722
+  {
723
+    //click on active color
724
+    if( idx == colorIdx )
725
+    {
726
+      //act as if color color select button was pressed
727
+      actionColorsColor( );
728
+      return;
729
+    }
730
+
731
+    //set active color index
732
+    colorIdx = idx;
733
+
734
+    //update color settings
735
+    showColorsColor( );
736
+    showColorsAlpha( );
737
+
738
+    //set color of frame editor to new color
739
+    frameEditor.setColor( colors[colorIdx] );
740
+  }
741
+
742
+  //show color
743
+  private void showColorsColor( )
744
+  {
745
+    int red, green, blue;
746
+    String hex;
747
+
748
+    //get color components
749
+    red = colors[colorIdx].getRed( );
750
+    green = colors[colorIdx].getGreen( );
751
+    blue = colors[colorIdx].getBlue( );
752
+
753
+    //color button
754
+    iconFromColor( iconColorsColor, new Color( red, green, blue ) );
755
+    buttonColorsColor.repaint( );
756
+
757
+    //color text
758
+    if( red < 0x10 )
759
+      hex = "0" + Integer.toHexString( red );
760
+    else
761
+      hex = Integer.toHexString( red );
762
+    if( green < 0x10 )
763
+      hex += "0" + Integer.toHexString( green );
764
+    else
765
+      hex += Integer.toHexString( green );
766
+    if( blue < 0x10 )
767
+      hex += "0" + Integer.toHexString( blue );
768
+    else
769
+      hex += Integer.toHexString( blue );
770
+    textColorsColor.setText( hex.toUpperCase( ) );
771
+  }
772
+
773
+  //color select button was pressed
774
+  private void actionColorsColor( )
775
+  {
776
+    Color color;
777
+
778
+    //get current color with full alpha
779
+    color = new Color( colors[colorIdx].getRed( ),
780
+                       colors[colorIdx].getGreen( ),
781
+                       colors[colorIdx].getBlue( ) );
782
+    
783
+    //show color select dialog
784
+    color = JColorChooser.showDialog( dialogParent,
785
+                                      "Blimp - Choose Color...",
786
+                                      color );
787
+    if( color == null ) //dialog was cancelled
788
+      return;
789
+
790
+    //save new color
791
+    colors[colorIdx] = new Color( color.getRed( ),
792
+                                  color.getGreen( ),
793
+                                  color.getBlue( ),
794
+                                  colors[colorIdx].getAlpha( ) );
795
+
796
+    //redisplay new color
797
+    showColorsColor( );
798
+
799
+    //update color icon of active color
800
+    iconFromColor( iconsColor[colorIdx], colors[colorIdx] );
801
+    buttonsColor[colorIdx].repaint( );
802
+
803
+    //set color of frame editor to new color
804
+    frameEditor.setColor( colors[colorIdx] );
805
+  }
806
+
807
+  //new color text is being entered
808
+  private void changeColorsColor( )
809
+  {
810
+    String txt;
811
+    int red, green, blue;
812
+
813
+    //get color text
814
+    txt = textColorsColor.getText( );
815
+
816
+    //standard color is black
817
+    red = 0;
818
+    green = 0;
819
+    blue = 0;
820
+
821
+    //get new color
822
+    try
823
+    {
824
+      if( txt.length( ) >= 2 )
825
+        red = Integer.parseInt( txt.substring( 0, 2 ), 0x10 );
826
+      if( txt.length( ) >= 4 )
827
+        green = Integer.parseInt( txt.substring( 2, 4 ), 0x10 );
828
+      if( txt.length( ) >= 6 )
829
+        blue = Integer.parseInt( txt.substring( 4, 6 ), 0x10 );
830
+    }
831
+    catch( NumberFormatException e ) { }
832
+
833
+    //save new color
834
+    colors[colorIdx] = new Color( red, green, blue, colors[colorIdx].getAlpha( ) );
835
+
836
+    //set color of frame editor to new color
837
+    frameEditor.setColor( colors[colorIdx] );
838
+  }
839
+
840
+  //new color text was entered
841
+  private void validateColorsColor( )
842
+  {
843
+    //process changes
844
+    changeColorsColor( );
845
+
846
+    //redisplay new color
847
+    showColorsColor( );
848
+
849
+    //update color icon of active color
850
+    iconFromColor( iconsColor[colorIdx], colors[colorIdx] );
851
+    buttonsColor[colorIdx].repaint( );
852
+  }
853
+
854
+  //show color's alpha value
855
+  private void showColorsAlpha( )
856
+  {
857
+    int alpha;
858
+    String hex;
859
+
860
+    //get alpha value
861
+    alpha = colors[colorIdx].getAlpha( );
862
+
863
+    //alpha slider
864
+    sliderColorsAlpha.setValue( alpha );
865
+
866
+    //alpha text
867
+    if( alpha < 0x10 )
868
+      hex = "0" + Integer.toHexString( alpha );
869
+    else
870
+      hex = Integer.toHexString( alpha );
871
+    textColorsAlpha.setText( hex.toUpperCase( ) );
872
+  }
873
+
874
+  //color's alpha value changed
875
+  private void stateColorsAlpha( )
876
+  {
877
+    int alpha;
878
+    String hex;
879
+
880
+    //get new alpha value
881
+    alpha = sliderColorsAlpha.getValue( );
882
+
883
+    //update active color
884
+    colors[colorIdx] = new Color( colors[colorIdx].getRed( ),
885
+                                  colors[colorIdx].getGreen( ),
886
+                                  colors[colorIdx].getBlue( ),
887
+                                  alpha );
888
+
889
+    //update alpha text
890
+    if( alpha < 0x10 )
891
+      hex = "0" + Integer.toHexString( alpha );
892
+    else
893
+      hex = Integer.toHexString( alpha );
894
+    textColorsAlpha.setText( hex.toUpperCase( ) );
895
+
896
+    //update color icon of active color
897
+    iconFromColor( iconsColor[colorIdx], colors[colorIdx] );
898
+    buttonsColor[colorIdx].repaint( );
899
+
900
+    //set color of frame editor to new color
901
+    frameEditor.setColor( colors[colorIdx] );
902
+  }
903
+
904
+  //new alpha text is being entered
905
+  private void changeColorsAlpha( )
906
+  {
907
+    String txt;
908
+    int alpha;
909
+
910
+    //get alpha text
911
+    txt = textColorsAlpha.getText( );
912
+
913
+    //standard alpha is full
914
+    alpha = 255;
915
+
916
+    //get new alpha
917
+    try
918
+    {
919
+      if( txt.length( ) >= 2 )
920
+        alpha = Integer.parseInt( txt.substring( 0, 2 ), 0x10 );
921
+    }
922
+    catch( NumberFormatException e ) { }
923
+
924
+    //save new alpha
925
+    colors[colorIdx] = new Color( colors[colorIdx].getRed( ),
926
+                                  colors[colorIdx].getGreen( ),
927
+                                  colors[colorIdx].getBlue( ),
928
+                                  alpha );
929
+
930
+    //set color of frame editor to new color
931
+    frameEditor.setColor( colors[colorIdx] );
932
+  }
933
+
934
+  //new alpha text was entered
935
+  private void validateColorsAlpha( )
936
+  {
937
+    //process changes
938
+    changeColorsAlpha( );
939
+
940
+    //redisplay new alpha value
941
+    showColorsAlpha( );
942
+
943
+    //update color icon of active color
944
+    iconFromColor( iconsColor[colorIdx], colors[colorIdx] );
945
+    buttonsColor[colorIdx].repaint( );
946
+  }
947
+
948
+  public void windowActivated( WindowEvent e )
949
+  {
950
+  }
951
+
952
+  public void windowDeactivated( WindowEvent e )
953
+  {
954
+  }
955
+
956
+  public void windowOpened( WindowEvent e )
957
+  {
958
+  }
959
+
960
+  public void windowClosing( WindowEvent e )
961
+  {
962
+    actionFileQuit( ); //act as "File Quit"
963
+  }
964
+
965
+  public void windowClosed( WindowEvent e )
966
+  { 
967
+  }
968
+
969
+  public void windowIconified( WindowEvent e )
970
+  {
971
+  }
972
+
973
+  public void windowDeiconified( WindowEvent e )
974
+  {
975
+  }
976
+
977
+  //some GUI action was perfomed
978
+  public void actionPerformed( ActionEvent e )
979
+  {
980
+    int i;
981
+
982
+    if( e.getSource( ) == menuFileNew )
983
+      actionFileNew( );
984
+    else if( e.getSource( ) == menuFileLoad )
985
+      actionFileLoad( );
986
+    else if( e.getSource( ) == menuFileSave )
987
+      actionFileSave( );
988
+    else if( e.getSource( ) == menuFileSaveAs )
989
+      actionFileSaveAs( );
990
+    else if( e.getSource( ) == menuFileQuit )
991
+      actionFileQuit( );
992
+    else if( e.getSource( ) == menuInfoShow )
993
+      actionInfoShow( );
994
+    else if( e.getSource( ) == menuInfoAdd )
995
+      actionInfoAdd( );
996
+    else if( e.getSource( ) == menuInfoDelete )
997
+      actionInfoDelete( );
998
+    else if( e.getSource( ) == menuEditResize )
999
+      actionEditResize( );
1000
+    else if( e.getSource( ) == menuEditInsertFrame )
1001
+      actionEditInsertFrame( );
1002
+    else if( e.getSource( ) == menuEditDuplicateFrame )
1003
+      actionEditDuplicateFrame( );
1004
+    else if( e.getSource( ) == menuEditDeleteFrame )
1005
+      actionEditDeleteFrame( );
1006
+    else if( e.getSource( ) == menuPlayStart )
1007
+      actionPlayStart( );
1008
+    else if( e.getSource( ) == menuPlayStop )
1009
+      actionPlayStop( );
1010
+    else if( e.getSource( ) == timerPlay )
1011
+      actionPlayTimer( );
1012
+    else if( e.getSource( ) == menuHelpAbout )
1013
+      actionHelpAbout( );
1014
+    else if( e.getSource( ) == textDuration )
1015
+      validateDuration( );
1016
+    else if( e.getSource( ) == buttonToolsNone )
1017
+      frameEditor.setTool( BlinkenFrameEditor.toolNone );
1018
+    else if( e.getSource( ) == buttonToolsColorPicker )
1019
+      frameEditor.setTool( BlinkenFrameEditor.toolColorPicker );
1020
+    else if( e.getSource( ) == buttonToolsDot )
1021
+      frameEditor.setTool( BlinkenFrameEditor.toolDot );
1022
+    else if( e.getSource( ) == buttonToolsLine )
1023
+      frameEditor.setTool( BlinkenFrameEditor.toolLine );
1024
+    else if( e.getSource( ) == buttonToolsRect )
1025
+      frameEditor.setTool( BlinkenFrameEditor.toolRect );
1026
+    else if( e.getSource( ) == buttonToolsFilledRect )
1027
+      frameEditor.setTool( BlinkenFrameEditor.toolFilledRect );
1028
+    else if( e.getSource( ) == buttonToolsCircle )
1029
+      frameEditor.setTool( BlinkenFrameEditor.toolCircle );
1030
+    else if( e.getSource( ) == buttonToolsFilledCircle )
1031
+      frameEditor.setTool( BlinkenFrameEditor.toolFilledCircle );
1032
+    else if( e.getSource( ) == buttonToolsCopy )
1033
+      frameEditor.setTool( BlinkenFrameEditor.toolCopy );
1034
+    else if( e.getSource( ) == buttonToolsPaste )
1035
+      frameEditor.setTool( BlinkenFrameEditor.toolPaste );
1036
+    else if( e.getSource( ) == buttonActionsInvert )
1037
+      frameEditor.actionInvert( );
1038
+    else if( e.getSource( ) == buttonActionsRotate90 )
1039
+      frameEditor.actionRotate90( );
1040
+    else if( e.getSource( ) == buttonActionsRotate180 )
1041
+      frameEditor.actionRotate180( );
1042
+    else if( e.getSource( ) == buttonActionsRotate270 )
1043
+      frameEditor.actionRotate270( );
1044
+    else if( e.getSource( ) == buttonActionsMirrorHor )
1045
+      frameEditor.actionMirrorHor( );
1046
+    else if( e.getSource( ) == buttonActionsMirrorVer )
1047
+      frameEditor.actionMirrorVer( );
1048
+    else if( e.getSource( ) == buttonActionsMirrorDiag )
1049
+      frameEditor.actionMirrorDiag( );
1050
+    else if( e.getSource( ) == buttonActionsMirrorDiag2 )
1051
+      frameEditor.actionMirrorDiag2( );
1052
+    else if( e.getSource( ) == buttonActionsRollLeft )
1053
+      frameEditor.actionRollLeft( );
1054
+    else if( e.getSource( ) == buttonActionsRollRight )
1055
+      frameEditor.actionRollRight( );
1056
+    else if( e.getSource( ) == buttonActionsRollUp )
1057
+      frameEditor.actionRollUp( );
1058
+    else if( e.getSource( ) == buttonActionsRollDown )
1059
+      frameEditor.actionRollDown( );
1060
+    else if( e.getSource( ) == buttonActionsUndo )
1061
+      frameEditor.actionUndo( );
1062
+    else if( e.getSource( ) == buttonActionsRedo )
1063
+      frameEditor.actionRedo( );
1064
+    else if( e.getSource( ) == buttonColorsColor )
1065
+      actionColorsColor( );
1066
+    else if( e.getSource( ) == textColorsColor )
1067
+      validateColorsColor( );
1068
+    else if( e.getSource( ) == textColorsAlpha )
1069
+      validateColorsAlpha( );
1070
+    else
1071
+    {
1072
+      for( i = 0; i < constColorCnt; i++ )
1073
+        if( e.getSource( ) == buttonsColor[i] )
1074
+          break;
1075
+      if( i < constColorCnt )
1076
+        actionColorIdx( i );
1077
+    }
1078
+  }
1079
+
1080
+  //some GUI value was adjusted
1081
+  public void adjustmentValueChanged( AdjustmentEvent e )
1082
+  {
1083
+    if( e.getSource( ) == scrollFrames )
1084
+      stateFrames( );
1085
+  }
1086
+
1087
+  //some GUI state changed
1088
+  public void stateChanged( ChangeEvent e )
1089
+  {
1090
+    if( e.getSource( ) == sliderFrameZoom )
1091
+      stateFrameZoom( );
1092
+    else if( e.getSource( ) == sliderColorsAlpha )
1093
+      stateColorsAlpha( );
1094
+  }
1095
+
1096
+  //a control got the focus
1097
+  public void focusGained( FocusEvent e )
1098
+  {
1099
+  }
1100
+
1101
+  //a control lost the focus
1102
+  public void focusLost( FocusEvent e )
1103
+  {
1104
+    if( e.getSource( ) == textDuration )
1105
+      validateDuration( );
1106
+    else if( e.getSource( ) == textColorsColor )
1107
+      validateColorsColor( );
1108
+    else if( e.getSource( ) == textColorsAlpha )
1109
+      validateColorsAlpha( );
1110
+  }
1111
+
1112
+  //something was changed in a document
1113
+  public void changedUpdate( DocumentEvent e )
1114
+  {
1115
+    if( e.getDocument( ) == textDuration.getDocument( ) )
1116
+      changeDuration( );
1117
+    else if( e.getDocument( ) == textColorsColor.getDocument( ) )
1118
+      changeColorsColor( );
1119
+    else if( e.getDocument( ) == textColorsAlpha.getDocument( ) )
1120
+      changeColorsAlpha( );
1121
+  }
1122
+
1123
+  //something was inserted into a document
1124
+  public void insertUpdate( DocumentEvent e )
1125
+  {
1126
+    if( e.getDocument( ) == textDuration.getDocument( ) )
1127
+      changeDuration( );
1128
+  }
1129
+
1130
+  //something was removed from a document
1131
+  public void removeUpdate( DocumentEvent e )
1132
+  {
1133
+    if( e.getDocument( ) == textDuration.getDocument( ) )
1134
+      changeDuration( );
1135
+  }
1136
+
1137
+  //info text of frame editor changed
1138
+  public void blinkenFrameEditorInfo( String info )
1139
+  {
1140
+    labelFrameInfo.setText( info );
1141
+  }
1142
+
1143
+  //a color was picked in the frame editor
1144
+  public void blinkenFrameEditorColorPicked( Color color )
1145
+  {
1146
+    //save new color
1147
+    colors[colorIdx] = color;
1148
+
1149
+    //redisplay new color (incl. alpha)
1150
+    showColorsColor( );
1151
+    showColorsAlpha( );
1152
+
1153
+    //update color icon of active color
1154
+    iconFromColor( iconsColor[colorIdx], colors[colorIdx] );
1155
+    buttonsColor[colorIdx].repaint( );
1156
+
1157
+    //set color of frame editor to new color
1158
+    frameEditor.setColor( colors[colorIdx] );
1159
+  }
1160
+
1161
+  //the current frame was changed in the frame editor
1162
+  public void blinkenFrameEditorFrameChanged( )
1163
+  {
1164
+    curMovieChanged = true;
1165
+  }
1166
+
1167
+  //the possibility to perfon an undo or redo operation changed
1168
+  public void blinkenFrameEditorCanUndoRedo( boolean canUndo, boolean canRedo )
1169
+  {
1170
+    buttonActionsUndo.setEnabled( canUndo );
1171
+    buttonActionsRedo.setEnabled( canRedo );
1172
+  }
1173
+
1174
+  //entry point of main thread
1175
+  public void run( )
1176
+  {
1177
+    int i, val;
1178
+    Dimension size;
1179
+    Insets smallMargin;
1180
+
1181
+    //initialize current movie, frame
1182
+    curDir = new File( "." );
1183
+    curMovie = new BlinkenMovie( 0, 0, 0, 0 );
1184
+    curMovie.insertInfo( 0, "creator", "Blimp (version 0.2 date 2004-11-10)" );
1185
+    curFrame = null;
1186
+
1187
+    //runnning as full application
1188
+    if( isFullApp )
1189
+    {
1190
+      //create main window
1191
+      JFrame.setDefaultLookAndFeelDecorated( true );
1192
+      frame = new JFrame( "Blimp" );
1193
+      frame.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
1194
+      frame.addWindowListener( this );
1195
+      //create menu bar
1196
+      menubar = new JMenuBar( );
1197
+      frame.setJMenuBar( menubar );
1198
+      //create main panel
1199
+      panel = new JPanel( new BorderLayout( 5, 5 ) );
1200
+      frame.getContentPane( ).add( panel );
1201
+      //use main window as parent for dialogs
1202
+      dialogParent = frame;
1203
+    }
1204
+    //runnning as applet
1205
+    else
1206
+    {
1207
+      //no main window - applet is main window
1208
+      frame = null;
1209
+      //create menu bar
1210
+      menubar = new JMenuBar( );
1211
+      setJMenuBar( menubar );
1212
+      //create main panel
1213
+      panel = new JPanel( new BorderLayout( 5, 5 ) );
1214
+      getContentPane( ).add( panel );
1215
+      //use applet as parent for dialogs
1216
+      dialogParent = this;
1217
+    }
1218
+
1219
+    //create menus
1220
+    //file menu
1221
+    menuFile = new JMenu( "File" );
1222
+    menubar.add( menuFile );
1223
+    menuFileNew = new JMenuItem( "New" );
1224
+    menuFileNew.addActionListener( this );
1225
+    menuFile.add( menuFileNew );
1226
+    menuFileLoad = new JMenuItem( "Load..." );
1227
+    menuFileLoad.addActionListener( this );
1228
+    menuFile.add( menuFileLoad );
1229
+    menuFileSave = new JMenuItem( "Save" );
1230
+    menuFileSave.addActionListener( this );
1231
+    menuFile.add( menuFileSave );
1232
+    menuFileSaveAs = new JMenuItem( "Save as..." );
1233
+    menuFileSaveAs.addActionListener( this );
1234
+    menuFile.add( menuFileSaveAs );
1235
+    if( isFullApp )
1236
+      menuFile.addSeparator( );
1237
+    menuFileQuit = new JMenuItem( "Quit" );
1238
+    menuFileQuit.addActionListener( this );
1239
+    if( isFullApp )
1240
+      menuFile.add( menuFileQuit );
1241
+    //information menu
1242
+    menuInfo = new JMenu( "Information" );
1243
+    menubar.add( menuInfo );
1244
+    menuInfoShow = new JMenuItem( "Show..." );
1245
+    menuInfoShow.addActionListener( this );
1246
+    menuInfo.add( menuInfoShow );
1247
+    menuInfoAdd = new JMenuItem( "Add..." );
1248
+    menuInfoAdd.addActionListener( this );
1249
+    menuInfo.add( menuInfoAdd );
1250
+    menuInfoDelete = new JMenuItem( "Delete..." );
1251
+    menuInfoDelete.addActionListener( this );
1252
+    menuInfo.add( menuInfoDelete );
1253
+    //edit menu
1254
+    menuEdit = new JMenu( "Edit" );
1255
+    menubar.add( menuEdit );
1256
+    menuEditResize = new JMenuItem( "Resize Movie..." );
1257
+    menuEditResize.addActionListener( this );
1258
+    menuEdit.add( menuEditResize );
1259
+    menuEdit.addSeparator( );
1260
+    menuEditInsertFrame = new JMenuItem( "Insert Frame" );
1261
+    menuEditInsertFrame.addActionListener( this );
1262
+    menuEdit.add( menuEditInsertFrame );
1263
+    menuEditDuplicateFrame = new JMenuItem( "Duplicate Frame" );
1264
+    menuEditDuplicateFrame.setEnabled( false );
1265
+    menuEditDuplicateFrame.addActionListener( this );
1266
+    menuEdit.add( menuEditDuplicateFrame );
1267
+    menuEditDeleteFrame = new JMenuItem( "Delete Frame" );
1268
+    menuEditDeleteFrame.setEnabled( false );
1269
+    menuEditDeleteFrame.addActionListener( this );
1270
+    menuEdit.add( menuEditDeleteFrame );
1271
+    //play menu
1272
+    menuPlay = new JMenu( "Play" );
1273
+    menubar.add( menuPlay );
1274
+    menuPlayStart = new JMenuItem( "Start" );
1275
+    menuPlayStart.addActionListener( this );
1276
+    menuPlay.add( menuPlayStart );
1277
+    menuPlayStop = new JMenuItem( "Stop" );
1278
+    menuPlayStop.setEnabled( false );
1279
+    menuPlayStop.addActionListener( this );
1280
+    menuPlay.add( menuPlayStop );
1281
+    menuPlay.addSeparator( );
1282
+    menuPlayBegin = new JCheckBoxMenuItem( "From Begin", false );
1283
+    menuPlayBegin.addActionListener( this );
1284
+    menuPlay.add( menuPlayBegin );
1285
+    menuPlayLoop = new JCheckBoxMenuItem( "Looped", false );
1286
+    menuPlayLoop.addActionListener( this );
1287
+    menuPlay.add( menuPlayLoop );
1288
+    //help menu
1289
+    menuHelp = new JMenu( "Help" );
1290
+    menubar.add( menuHelp );
1291
+    menuHelpAbout = new JMenuItem( "About..." );
1292
+    menuHelpAbout.addActionListener( this );
1293
+    menuHelp.add( menuHelpAbout );
1294
+
1295
+    //create controls
1296
+    smallMargin = new Insets( 1, 1, 1, 1 );
1297
+    panel.setBorder( new EmptyBorder( 5, 5, 5, 5 ) );
1298
+    //status bar
1299
+    panelStatus = new JPanel( new BorderLayout( 5, 5 ) );
1300
+    panel.add( panelStatus, BorderLayout.SOUTH );
1301
+    panelStatus.add( new JSeparator( JSeparator.HORIZONTAL ), BorderLayout.NORTH );
1302
+    labelStatus = new JLabel( "ready..." );
1303
+    panelStatus.add( labelStatus, BorderLayout.CENTER );
1304
+    //main panel
1305
+    panelMain = new JPanel( new BorderLayout( 5, 5 ) );
1306
+    panel.add( panelMain, BorderLayout.CENTER );
1307
+    //frames panel
1308
+    panelFrames = new JPanel( new BorderLayout( 5, 5 ) );
1309
+    panelMain.add( panelFrames, BorderLayout.SOUTH );
1310
+    scrollFrames = new JScrollBar( SwingConstants.HORIZONTAL, 0, 0, 0, 0 );
1311
+    scrollFrames.setBlockIncrement( 1 );
1312
+    scrollFrames.addAdjustmentListener( this );
1313
+    panelFrames.add( scrollFrames, BorderLayout.CENTER );
1314
+    labelFrames = new JLabel( "frame: -/0" );
1315
+    labelFrames.setLabelFor( scrollFrames );
1316
+    panelFrames.add( labelFrames, BorderLayout.WEST );
1317
+    //outer and middle frame panel
1318
+    panelOuterFrame = new JPanel( new BorderLayout( 5, 5 ) );
1319
+    panelMain.add( panelOuterFrame, BorderLayout.CENTER );
1320
+    panelOuterFrame.add( new JSeparator( JSeparator.HORIZONTAL ), BorderLayout.SOUTH );
1321
+    panelMiddleFrame = new JPanel( new BorderLayout( 5, 5 ) );
1322
+    panelOuterFrame.add( panelMiddleFrame, BorderLayout.CENTER );
1323
+    panelMiddleFrame.add( new JSeparator( JSeparator.VERTICAL ), BorderLayout.WEST );
1324
+    panelMiddleFrame.add( new JSeparator( JSeparator.VERTICAL ), BorderLayout.EAST );
1325
+    //frame panel
1326
+    panelFrame = new JPanel( new BorderLayout( 5, 5 ) );
1327
+    panelMiddleFrame.add( panelFrame, BorderLayout.CENTER );
1328
+    sliderFrameZoom = new JSlider( JSlider.VERTICAL, 0, 6, 3 );
1329
+    sliderFrameZoom.setSnapToTicks( true );
1330
+    sliderFrameZoom.setInverted( true );
1331
+    sliderFrameZoom.addChangeListener( this );
1332
+    sliderFrameZoom.setToolTipText( "Zoom" );
1333
+    panelFrame.add( sliderFrameZoom, BorderLayout.EAST );
1334
+    frameEditor = new BlinkenFrameEditor( );
1335
+    frameEditor.setZoom( sliderFrameZoom.getValue( ) );
1336
+    scrollpaneFrame = new JScrollPane( frameEditor );
1337
+    panelFrame.add( scrollpaneFrame, BorderLayout.CENTER );
1338
+    labelFrameInfo = new JLabel( "", JLabel.CENTER );
1339
+    labelFrameInfo.setLabelFor( frameEditor );
1340
+    panelFrame.add( labelFrameInfo, BorderLayout.NORTH );
1341
+    frameEditor.setEditorListener( this );
1342
+    panelDuration = new JPanel( new FlowLayout( FlowLayout.CENTER, 5, 5 ) );
1343
+    panelFrame.add( panelDuration, BorderLayout.SOUTH );
1344
+    textDuration = new JTextField( 5 );
1345
+    textDuration.setHorizontalAlignment( JTextField.CENTER );
1346
+    textDuration.setEnabled( false );
1347
+    textDuration.getDocument( ).addDocumentListener( this );
1348
+    textDuration.addActionListener( this );
1349
+    textDuration.addFocusListener( this );
1350
+    labelDuration = new JLabel( "duration (ms): " );
1351
+    labelDuration.setLabelFor( textDuration );
1352
+    panelDuration.add( labelDuration );
1353
+    panelDuration.add( textDuration );
1354
+    //tool and action panels
1355
+    panelOuterTools = new JPanel( new GridLayout( 2, 1, 5, 5 ) );
1356
+    panelOuterFrame.add( panelOuterTools, BorderLayout.WEST );
1357
+    panelMiddleTools = new JPanel( new BorderLayout( 5, 5 ) );
1358
+    panelOuterTools.add( panelMiddleTools );
1359
+    panelTools = new JPanel( new GridLayout( 4, 3, 5, 5 ) );
1360
+    panelMiddleTools.add( panelTools, BorderLayout.CENTER );
1361
+    panelMiddleTools.add( new JSeparator( JSeparator.HORIZONTAL ), BorderLayout.SOUTH );
1362
+    panelActions = new JPanel( new GridLayout( 5, 3, 5, 5 ) );
1363
+    panelOuterTools.add( panelActions );
1364
+    //tool buttons
1365
+    groupTools = new ButtonGroup( );
1366
+    buttonToolsNone = new JToggleButton( );
1367
+    buttonToolsNone.setMargin( smallMargin );
1368
+    buttonToolsNone.setToolTipText( "no tool" );
1369
+    buttonToolsNone.addActionListener( this );
1370
+    groupTools.add( buttonToolsNone );
1371
+    panelTools.add( buttonToolsNone );
1372
+    buttonToolsColorPicker = new JToggleButton( loadImageIcon( "ColorPicker.png" ) );
1373
+    buttonToolsColorPicker.setMargin( smallMargin );
1374
+    buttonToolsColorPicker.setToolTipText( "Color Picker" );
1375
+    buttonToolsColorPicker.addActionListener( this );
1376
+    groupTools.add( buttonToolsColorPicker );
1377
+    panelTools.add( buttonToolsColorPicker );
1378
+    buttonToolsDot = new JToggleButton( loadImageIcon( "Dot.png" ) );
1379
+    buttonToolsDot.setMargin( smallMargin );
1380
+    buttonToolsDot.setToolTipText( "Dot" );
1381
+    buttonToolsDot.addActionListener( this );
1382
+    groupTools.add( buttonToolsDot );
1383
+    panelTools.add( buttonToolsDot );
1384
+    buttonToolsLine = new JToggleButton( loadImageIcon( "Line.png" ) );
1385
+    buttonToolsLine.setMargin( smallMargin );
1386
+    buttonToolsLine.setToolTipText( "Line" );
1387
+    buttonToolsLine.addActionListener( this );
1388
+    groupTools.add( buttonToolsLine );
1389
+    panelTools.add( buttonToolsLine );
1390
+    buttonToolsRect = new JToggleButton( loadImageIcon( "Rectangle.png" ) );
1391
+    buttonToolsRect.setMargin( smallMargin );
1392
+    buttonToolsRect.setToolTipText( "Rectangle" );
1393
+    buttonToolsRect.addActionListener( this );
1394
+    groupTools.add( buttonToolsRect );
1395
+    panelTools.add( buttonToolsRect );
1396
+    buttonToolsFilledRect = new JToggleButton( loadImageIcon( "FilledRectangle.png" ) );
1397
+    buttonToolsFilledRect.setMargin( smallMargin );
1398
+    buttonToolsFilledRect.setToolTipText( "Filled Rectangle" );
1399
+    buttonToolsFilledRect.addActionListener( this );
1400
+    groupTools.add( buttonToolsFilledRect );
1401
+    panelTools.add( buttonToolsFilledRect );
1402
+    panelTools.add( new JLabel( ) );
1403
+    buttonToolsCircle = new JToggleButton( loadImageIcon( "Circle.png" ) );
1404
+    buttonToolsCircle.setMargin( smallMargin );
1405
+    buttonToolsCircle.setToolTipText( "Circle" );
1406
+    buttonToolsCircle.addActionListener( this );
1407
+    groupTools.add( buttonToolsCircle );
1408
+    panelTools.add( buttonToolsCircle );
1409
+    buttonToolsFilledCircle = new JToggleButton( loadImageIcon( "FilledCircle.png" ) );
1410
+    buttonToolsFilledCircle.setMargin( smallMargin );
1411
+    buttonToolsFilledCircle.setToolTipText( "Filled Circle" );
1412
+    buttonToolsFilledCircle.addActionListener( this );
1413
+    groupTools.add( buttonToolsFilledCircle );
1414
+    panelTools.add( buttonToolsFilledCircle );
1415
+    panelTools.add( new JLabel( ) );
1416
+    buttonToolsCopy = new JToggleButton( loadImageIcon( "Copy.png" ) );
1417
+    buttonToolsCopy.setMargin( smallMargin );
1418
+    buttonToolsCopy.setToolTipText( "Copy" );
1419
+    buttonToolsCopy.addActionListener( this );
1420
+    groupTools.add( buttonToolsCopy );
1421
+    panelTools.add( buttonToolsCopy );
1422
+    buttonToolsPaste = new JToggleButton( loadImageIcon( "Paste.png" ) );
1423
+    buttonToolsPaste.setMargin( smallMargin );
1424
+    buttonToolsPaste.setToolTipText( "Paste" );
1425
+    buttonToolsPaste.addActionListener( this );
1426
+    groupTools.add( buttonToolsPaste );
1427
+    panelTools.add( buttonToolsPaste );
1428
+    buttonToolsNone.setSelected( true );
1429
+    frameEditor.setTool( BlinkenFrameEditor.toolNone );
1430
+    //action buttons
1431
+    buttonActionsInvert = new JButton( loadImageIcon( "Invert.png" ) );
1432
+    buttonActionsInvert.setMargin( smallMargin );
1433
+    buttonActionsInvert.setToolTipText( "Invert" );
1434
+    buttonActionsInvert.addActionListener( this );
1435
+    panelActions.add( buttonActionsInvert );
1436
+    buttonActionsMirrorHor = new JButton( loadImageIcon( "MirrorHor.png" ) );
1437
+    buttonActionsMirrorHor.setMargin( smallMargin );
1438
+    buttonActionsMirrorHor.setToolTipText( "Mirror Horizontally" );
1439
+    buttonActionsMirrorHor.addActionListener( this );
1440
+    panelActions.add( buttonActionsMirrorHor );
1441
+    buttonActionsRollLeft = new JButton( loadImageIcon( "RollLeft.png" ) );
1442
+    buttonActionsRollLeft.setMargin( smallMargin );
1443
+    buttonActionsRollLeft.setToolTipText( "Roll Left" );
1444
+    buttonActionsRollLeft.addActionListener( this );
1445
+    panelActions.add( buttonActionsRollLeft );
1446
+    buttonActionsRotate90 = new JButton( loadImageIcon( "Rotate90.png" ) );
1447
+    buttonActionsRotate90.setMargin( smallMargin );
1448
+    buttonActionsRotate90.setToolTipText( "Rotate 90 Degrees" );
1449
+    buttonActionsRotate90.addActionListener( this );
1450
+    panelActions.add( buttonActionsRotate90 );
1451
+    buttonActionsMirrorVer = new JButton( loadImageIcon( "MirrorVer.png" ) );
1452
+    buttonActionsMirrorVer.setMargin( smallMargin );
1453
+    buttonActionsMirrorVer.setToolTipText( "Mirror Vertically" );
1454
+    buttonActionsMirrorVer.addActionListener( this );
1455
+    panelActions.add( buttonActionsMirrorVer );
1456
+    buttonActionsRollRight = new JButton( loadImageIcon( "RollRight.png" ) );
1457
+    buttonActionsRollRight.setMargin( smallMargin );
1458
+    buttonActionsRollRight.setToolTipText( "Roll Right" );
1459
+    buttonActionsRollRight.addActionListener( this );
1460
+    panelActions.add( buttonActionsRollRight );
1461
+    buttonActionsRotate180 = new JButton( loadImageIcon( "Rotate180.png" ) );
1462
+    buttonActionsRotate180.setMargin( smallMargin );
1463
+    buttonActionsRotate180.setToolTipText( "Rotate 180 Degrees" );
1464
+    buttonActionsRotate180.addActionListener( this );
1465
+    panelActions.add( buttonActionsRotate180 );
1466
+    buttonActionsMirrorDiag = new JButton( loadImageIcon( "MirrorDiag.png" ) );
1467
+    buttonActionsMirrorDiag.setMargin( smallMargin );
1468
+    buttonActionsMirrorDiag.setToolTipText( "Mirror Diagonally (\\)" );
1469
+    buttonActionsMirrorDiag.addActionListener( this );
1470
+    panelActions.add( buttonActionsMirrorDiag );
1471
+    buttonActionsRollUp = new JButton( loadImageIcon( "RollUp.png" ) );
1472
+    buttonActionsRollUp.setMargin( smallMargin );
1473
+    buttonActionsRollUp.setToolTipText( "Roll Up" );
1474
+    buttonActionsRollUp.addActionListener( this );
1475
+    panelActions.add( buttonActionsRollUp );
1476
+    buttonActionsRotate270 = new JButton( loadImageIcon( "Rotate270.png" ) );
1477
+    buttonActionsRotate270.setMargin( smallMargin );
1478
+    buttonActionsRotate270.setToolTipText( "Rotate 270 Degrees" );
1479
+    buttonActionsRotate270.addActionListener( this );
1480
+    panelActions.add( buttonActionsRotate270 );
1481
+    buttonActionsMirrorDiag2 = new JButton( loadImageIcon( "MirrorDiag2.png" ) );
1482
+    buttonActionsMirrorDiag2.setMargin( smallMargin );
1483
+    buttonActionsMirrorDiag2.setToolTipText( "Mirror Diagonally (/)" );
1484
+    buttonActionsMirrorDiag2.addActionListener( this );
1485
+    panelActions.add( buttonActionsMirrorDiag2 );
1486
+    buttonActionsRollDown = new JButton( loadImageIcon( "RollDown.png" ) );
1487
+    buttonActionsRollDown.setMargin( smallMargin );
1488
+    buttonActionsRollDown.setToolTipText( "Roll Down" );
1489
+    buttonActionsRollDown.addActionListener( this );
1490
+    panelActions.add( buttonActionsRollDown );
1491
+    panelActions.add( new JLabel( ) );
1492
+    buttonActionsUndo = new JButton( loadImageIcon( "Undo.png" ) );
1493
+    buttonActionsUndo.setMargin( smallMargin );
1494
+    buttonActionsUndo.setToolTipText( "Undo" );
1495
+    buttonActionsUndo.setEnabled( false );
1496
+    buttonActionsUndo.addActionListener( this );
1497
+    panelActions.add( buttonActionsUndo );
1498
+    buttonActionsRedo = new JButton( loadImageIcon( "Redo.png" ) );
1499
+    buttonActionsRedo.setMargin( smallMargin );
1500
+    buttonActionsRedo.setToolTipText( "Redo" );
1501
+    buttonActionsRedo.setEnabled( false );
1502
+    buttonActionsRedo.addActionListener( this );
1503
+    panelActions.add( buttonActionsRedo );
1504
+    //color panel
1505
+    panelColors = new JPanel( new GridLayout( 2, 1, 5, 5 ) );
1506
+    panelOuterFrame.add( panelColors, BorderLayout.EAST );
1507
+    panelColorsChoose = new JPanel( new GridLayout( constColorCntY, constColorCntX, 5, 5 ) );
1508
+    panelColors.add( panelColorsChoose );
1509
+    buttonsColor = new JToggleButton[constColorCnt];
1510
+    groupColor = new ButtonGroup( );
1511
+    for( i = 0; i < constColorCnt; i++ )
1512
+    {
1513
+      buttonsColor[i] = new JToggleButton( );
1514
+      buttonsColor[i].setMargin( smallMargin );
1515
+      buttonsColor[i].addActionListener( this );
1516
+      groupColor.add( buttonsColor[i] );
1517
+      panelColorsChoose.add( buttonsColor[i] );
1518
+    }
1519
+    //color panel - settings
1520
+    panelColorsSettings = new JPanel( new GridLayout( 4, 1, 5, 0 ) );
1521
+    panelColors.add( panelColorsSettings );
1522
+    labelColorsColor = new JLabel( "color:" );
1523
+    labelColorsColor.setVerticalAlignment( JLabel.BOTTOM );
1524
+    panelColorsSettings.add( labelColorsColor );
1525
+    panelColorsColor = new JPanel( new FlowLayout( FlowLayout.CENTER, 5, 3 ) );
1526
+    panelColorsSettings.add( panelColorsColor );
1527
+    buttonColorsColor = new JButton( );
1528
+    buttonColorsColor.setMargin( smallMargin );
1529
+    buttonColorsColor.addActionListener( this );
1530
+    panelColorsColor.add( buttonColorsColor );
1531
+    textColorsColor = new JTextField( "FFFFFF", 6 );
1532
+    textColorsColor.setHorizontalAlignment( JTextField.CENTER );
1533
+    textColorsColor.addActionListener( this );
1534
+    textColorsColor.addFocusListener( this );
1535
+    panelColorsColor.add( textColorsColor );
1536
+    labelColorsColor.setLabelFor( panelColorsColor );
1537
+    labelColorsAlpha = new JLabel( "alpha:" );
1538
+    labelColorsAlpha.setVerticalAlignment( JLabel.BOTTOM );
1539
+    panelColorsSettings.add( labelColorsAlpha );
1540
+    panelColorsAlpha = new JPanel( new FlowLayout( FlowLayout.CENTER, 5, 3 ) );
1541
+    panelColorsSettings.add( panelColorsAlpha );
1542
+    sliderColorsAlpha = new JSlider( JSlider.HORIZONTAL, 0, 255, 255 );
1543
+    size = sliderColorsAlpha.getPreferredSize( );
1544
+    size.width = size.width * 2 / 5;
1545
+    sliderColorsAlpha.setPreferredSize( size );
1546
+    sliderColorsAlpha.setSnapToTicks( true );
1547
+    sliderColorsAlpha.addChangeListener( this );
1548
+    panelColorsAlpha.add( sliderColorsAlpha );
1549
+    textColorsAlpha = new JTextField( "FF", 2 );
1550
+    textColorsAlpha.setHorizontalAlignment( JTextField.CENTER );
1551
+    textColorsAlpha.addActionListener( this );
1552
+    textColorsAlpha.addFocusListener( this );
1553
+    panelColorsAlpha.add( textColorsAlpha );
1554
+    labelColorsAlpha.setLabelFor( panelColorsAlpha );
1555
+    //color panel - color icons
1556
+    colorIdx = 0;
1557
+    colors = new Color[constColorCnt];
1558
+    size = textColorsAlpha.getPreferredSize( );
1559
+    iconsColor = new ImageIcon[constColorCnt];
1560
+    for( i = 0; i < constColorCnt; i++ )
1561
+    {
1562
+      iconsColor[i] = new ImageIcon( new BufferedImage( size.width, size.height, BufferedImage.TYPE_INT_RGB ) );
1563
+      val = (constColorCnt - 1 - i) * 255 / (constColorCnt - 1);
1564
+      colors[i] = new Color( val, val, val );
1565
+      iconFromColor( iconsColor[i], colors[i] );
1566
+      buttonsColor[i].setIcon( iconsColor[i] );
1567
+    }
1568
+    iconColorsColor = new ImageIcon( new BufferedImage( size.width, size.height, BufferedImage.TYPE_INT_RGB ) );
1569
+    iconFromColor( iconColorsColor, colors[colorIdx] );
1570
+    buttonColorsColor.setIcon( iconColorsColor );
1571
+    buttonsColor[colorIdx].setSelected( true );
1572
+    frameEditor.setColor( colors[colorIdx] );
1573
+
1574
+    //create play timer
1575
+    timerPlay = new javax.swing.Timer( 100, this );
1576
+    timerPlay.setRepeats( false );
1577
+    timerPlay.stop( );
1578
+
1579
+    //update controls
1580
+    updateFrames( 0 );
1581
+
1582
+    //running as full application
1583
+    if( isFullApp )
1584
+    {
1585
+      //calculate size for main window, menus and controls
1586
+      frame.pack( );
1587
+      //show main window
1588
+      frame.setVisible( true );
1589
+    }
1590
+    //running as applet
1591
+    else
1592
+    {
1593
+      //arrange menus and controls
1594
+      size = getSize( );
1595
+      resize( 1, 1 );
1596
+      resize( size );
1597
+    }
1598
+  }
1599
+
1600
+  //entry point for applet
1601
+  public void init( )
1602
+  {
1603
+    javax.swing.SwingUtilities.invokeLater( this );
1604
+  }
1605
+
1606
+  //entry point for full application
1607
+  public static void main( String[] args )
1608
+  {
1609
+    javax.swing.SwingUtilities.invokeLater( new Blimp( true ) );
1610
+  }
1611
+
1612
+} //public class Blimp
... ...
@@ -0,0 +1 @@
1
+Main-Class: Blimp
... ...
@@ -0,0 +1,28 @@
1
+/* BlinkenLightsInteractiveMovieProgram
2
+ * version 0.2 date 2004-11-10
3
+ * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ * powered by eventphone.de
7
+ */
8
+
9
+import java.io.*;
10
+
11
+public class BlinkenFileFilter extends javax.swing.filechooser.FileFilter
12
+{
13
+  public boolean accept( File file )
14
+  {
15
+    if( file.isDirectory( ) )
16
+      return true;
17
+    String fileName = file.getPath( );
18
+    return fileName.endsWith( ".blm" ) ||
19
+           fileName.endsWith( ".bmm" ) ||
20
+           fileName.endsWith( ".bml" ) ||
21
+           fileName.endsWith( ".bbm" );
22
+  }
23
+
24
+  public String getDescription( )
25
+  {
26
+    return "BlinkenLights movie files (*.blm, *.bmm, *.bml, *.bbm)";
27
+  }
28
+}
... ...
@@ -0,0 +1,267 @@
1
+/* BlinkenLightsInteractiveMovieProgram
2
+ * version 0.2 date 2004-11-10
3
+ * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ * powered by eventphone.de
7
+ */
8
+
9
+import java.awt.*;
10
+
11
+public class BlinkenFrame
12
+{
13
+
14
+  private int height;
15
+  private int width;
16
+  private int channels;
17
+  private int maxval;
18
+  private int duration;
19
+  private byte[][][] data;
20
+
21
+  BlinkenFrame( int height, int width, int channels, int maxval, int duration )
22
+  {
23
+    if( height < 1 ) height = 1;
24
+    if( height > 1024 ) height = 1024;
25
+    if( width < 1 ) width = 1;
26
+    if( width > 1024 ) width = 1024;
27
+    if( channels < 1 ) channels = 1;
28
+    if( channels > 16 ) channels = 16;
29
+    if( maxval < 1 ) maxval = 1;
30
+    if( maxval > 255 ) maxval = 255;
31
+    if( duration < 1 ) duration = 1;
32
+    if( duration > 65535 ) duration = 65535;
33
+
34
+    this.height = height;
35
+    this.width = width;
36
+    this.channels = channels;
37
+    this.maxval = maxval;
38
+    this.duration = duration;
39
+    data = new byte[height][width][channels];
40
+  }
41
+
42
+  BlinkenFrame( BlinkenFrame frame )
43
+  {
44
+    int y, x, c;
45
+    height = frame.height;
46
+    width = frame.width;
47
+    channels = frame.channels;
48
+    maxval = frame.maxval;
49
+    duration = frame.duration;
50
+    data = new byte[height][width][channels];
51
+    for( y = 0; y < height; y++ )
52
+      for( x = 0; x < width; x++ )
53
+        for( c = 0; c < channels; c++ )
54
+          data[y][x][c] = frame.data[y][x][c];
55
+  }
56
+
57
+  public void clear( )
58
+  {
59
+    int x, y, c;
60
+    for( y = 0; y < height; y++ )
61
+      for( x = 0; x < width; x++ )
62
+        for( c = 0; c < channels; c++ )
63
+          data[y][x][c] = 0;
64
+  }
65
+
66
+  public int getHeight( )
67
+  {
68
+    return height;
69
+  }
70
+
71
+  public int getWidth( )
72
+  {
73
+    return width;
74
+  }
75
+
76
+  public int getChannels( )
77
+  {
78
+    return channels;
79
+  }
80
+
81
+  public int getMaxval( )
82
+  {
83
+    return maxval;
84
+  }
85
+
86
+  public int getDuration( )
87
+  {
88
+    return duration;
89
+  }
90
+
91
+  public void setDuration( int duration )
92
+  {
93
+    if( duration < 1 ) duration = 1;
94
+    if( duration > 65535 ) duration = 65535;
95
+    this.duration = duration;
96
+  }
97
+
98
+  public byte getPixel( int y, int x, int c )
99
+  {
100
+    if( y < 0 || y >= height ||
101
+        x < 0 || x >= width ||
102
+	c < 0 || c >= channels )
103
+      return 0;
104
+    return data[y][x][c];
105
+  }
106
+
107
+  public void setPixel( int y, int x, int c, byte val )
108
+  {
109
+    if( y < 0 || y >= height ||
110
+        x < 0 || x >= width ||
111
+	c < 0 || c >= channels )
112
+      return;
113
+    if( val > maxval )
114
+      val = (byte)maxval;
115
+    data[y][x][c] = val;
116
+  }
117
+
118
+  public Color getColor( int y, int x )
119
+  {
120
+    if( y < 0 || y >= height ||
121
+        x < 0 || x >= width ||
122
+        channels < 1 )
123
+      return new Color( 0, 0, 0 );
124
+    if( channels == 1 )
125
+      return new Color( (((int)data[y][x][0] & 0xFF) * 255 + maxval / 2) / maxval,
126
+                        (((int)data[y][x][0] & 0xFF) * 255 + maxval / 2) / maxval,
127
+                        (((int)data[y][x][0] & 0xFF) * 255 + maxval / 2) / maxval );
128
+    if( channels == 2 )
129
+      return new Color( (((int)data[y][x][0] & 0xFF) * 255 + maxval / 2) / maxval,
130
+                        (((int)data[y][x][1] & 0xFF) * 255 + maxval / 2) / maxval, 0 );
131
+    return new Color( (((int)data[y][x][0] & 0xFF) * 255 + maxval / 2) / maxval,
132
+                      (((int)data[y][x][1] & 0xFF) * 255 + maxval / 2) / maxval,
133
+                      (((int)data[y][x][2] & 0xFF) * 255 + maxval / 2) / maxval );
134
+  }
135
+
136
+  public void setColor( int y, int x, Color color )
137
+  {
138
+    int alpha, alpha_, c;
139
+    if( y < 0 || y >= height ||
140
+        x < 0 || x >= width )
141
+      return;
142
+    alpha = color.getAlpha( );
143
+    alpha_ = 255 - alpha;
144
+    if( channels >= 1 )
145
+      data[y][x][0] = (byte)(( ((color.getRed( ) * maxval + 127) / 255) * alpha
146
+                             + ((int)data[y][x][0] & 0xFF) * alpha_
147
+                             ) / 255);
148
+    if( channels >= 2 )
149
+      data[y][x][1] = (byte)(( ((color.getGreen( ) * maxval + 127) / 255) * alpha
150
+                             + ((int)data[y][x][1] & 0xFF) * alpha_
151
+                             ) / 255);
152
+    if( channels >= 3 )
153
+      data[y][x][2] = (byte)(( ((color.getBlue( ) * maxval + 127) / 255) * alpha
154
+                             + ((int)data[y][x][2] & 0xFF) * alpha_
155
+                             ) / 255);
156
+    for( c = 3; c < channels; c++ )
157
+      data[y][x][c] = (byte)(( 0
158
+                             + ((int)data[y][x][2] & 0xFF) * alpha_
159
+                             ) / 255);
160
+  }
161
+
162
+  public void resize( int height, int width, int channels, int maxval )
163
+  {
164
+    byte[][][] data;
165
+    int y, x, c;
166
+    int emptyY, emptyX, skipY, skipX, rangeY, rangeX, val, div;
167
+
168
+    if( height < 1 ) height = 1;
169
+    if( height > 1024 ) height = 1024;
170
+    if( width < 1 ) width = 1;
171
+    if( width > 1024 ) width = 1024;
172
+    if( channels < 1 ) channels = 1;
173
+    if( channels > 16 ) channels = 16;
174
+    if( maxval < 1 ) maxval = 1;
175
+    if( maxval > 255 ) maxval = 255;
176
+
177
+    if( height == this.height &&
178
+        width == this.width &&
179
+        channels == this.channels &&
180
+        maxval == this.maxval )
181
+      return;
182
+
183
+    //allocate new data array
184
+    data = new byte[height][width][channels];
185
+    for( y = 0; y < height; y++ )
186
+      for( x = 0; x < width; x++ )
187
+        for( c = 0; c < channels; c++ )
188
+          data[y][x][c] = 0;
189
+
190
+    //get number of pixels to skip / to leave empty in X and Y direction
191
+    if( height > this.height )
192
+    {
193
+      emptyY = (height - this.height) / 2; 
194
+      skipY = 0;
195
+      rangeY = this.height;
196
+    }
197
+    else
198
+    {
199
+      emptyY = 0;
200
+      skipY = (this.height - height) / 2;
201
+      rangeY = height;
202
+    }
203
+    if( width > this.width )
204
+    {
205
+      emptyX = (width - this.width) / 2; 
206
+      skipX = 0;
207
+      rangeX = this.width;
208
+    }
209
+    else
210
+    {
211
+      emptyX = 0;
212
+      skipX = (this.width - width) / 2;
213
+      rangeX = width;
214
+    }
215
+
216
+    //resize frame with help of calculated parameters
217
+    for( y = 0; y < rangeY; y++ )
218
+    {
219
+      for( x = 0; x < rangeX; x++ )
220
+      {
221
+        if( channels >= this.channels ) //add channels: copy last channel into new channels
222
+	{
223
+          for( c = 0; c < this.channels; c++ )
224
+            data[emptyY + y][emptyX + x][c] = (byte)((((int)this.data[skipY + y][skipX + x][c] & 0xFF) * maxval + this.maxval / 2) / this.maxval);
225
+	  for( ; c < channels; c++ )
226
+	    data[emptyY + y][emptyX + x][c] = data[emptyY + y][emptyX + x][this.channels - 1];
227
+	}
228
+	else //remove channels: merge leftover channels with last kept channel
229
+	{
230
+          val = 0;
231
+          for( c = 0; c < channels - 1; c++ )
232
+            data[emptyY + y][emptyX + x][c] = (byte)((((int)this.data[skipY + y][skipX + x][c] & 0xFF) * maxval + this.maxval / 2) / this.maxval);
233
+          for( c = channels - 1; c < this.channels; c++ )
234
+            val += (int)this.data[skipY + y][skipX + x][c] & 0xFF;
235
+          div = this.maxval * (this.channels - channels + 1);
236
+          data[emptyY + y][emptyX + x][channels - 1] = (byte)((val * maxval + div / 2) / div);
237
+	}
238
+      }
239
+    }
240
+
241
+    this.height = height;
242
+    this.width = width;
243
+    this.channels = channels;
244
+    this.maxval = maxval;
245
+    this.data = data;
246
+  }
247
+
248
+  public String toString( )
249
+  {
250
+    String str = "";
251
+    int h, w, c, val;
252
+    for( h = 0; h < height; h++ )
253
+    {
254
+      for( w = 0; w < width; w++ )
255
+      {
256
+        val = 0;
257
+        for( val = 0, c = 0; c < channels; c++ )
258
+          val += (data[h][w][c] & 0xFF);
259
+        val = val * 7 / maxval / channels;
260
+        str = str + " -+*%#&@".substring( val, val + 1); 
261
+      }
262
+      str = str + "\n";
263
+    }
264
+    return str + duration + " ms\n";
265
+  }
266
+
267
+} //public class BlinkenFrame
... ...
@@ -0,0 +1,216 @@
1
+/* BlinkenLightsInteractiveMovieProgram
2
+ * version 0.2 date 2004-11-10
3
+ * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ * powered by eventphone.de
7
+ */
8
+
9
+import java.awt.*;
10
+import java.awt.event.*;
11
+import java.awt.image.*;
12
+import javax.swing.*;
13
+
14
+public class BlinkenFrameDisplay extends JLabel
15
+                                 implements Scrollable, MouseListener, MouseMotionListener
16
+{
17
+  BlinkenFrame frame = null;
18
+  int height = 0, width = 0, channels = 1, maxval = 1;
19
+  int zoom = 8;
20
+  Dimension dimension = new Dimension( 0, 0 );
21
+  ImageIcon icon = null;
22
+  Image image = null;
23
+  Graphics graphics = null;
24
+  BlinkenFrameDisplayListener displayListener = null;
25
+  BlinkenFrameDisplayInterceptor displayInterceptor = null;
26
+
27
+  public BlinkenFrameDisplay( )
28
+  {
29
+    addMouseListener( this );
30
+    addMouseMotionListener( this );
31
+  }
32
+
33
+  public Dimension getPreferredScrollableViewportSize( )
34
+  {
35
+    return new Dimension( 200, 200 );
36
+  }
37
+
38
+  public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction )
39
+  {
40
+    if( orientation == SwingConstants.HORIZONTAL )
41
+      return visibleRect.width * 2 / 3 + 1;
42
+    else
43
+      return visibleRect.height * 2 / 3 + 1;
44
+  }
45
+
46
+  public boolean getScrollableTracksViewportHeight( )
47
+  {
48
+    return false;
49
+  }
50
+
51
+  public boolean getScrollableTracksViewportWidth( )
52
+  {
53
+    return false;
54
+  }
55
+
56
+  public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction )
57
+  {
58
+    if( orientation == SwingConstants.HORIZONTAL )
59
+      return visibleRect.width / 30 + 1;
60
+    else
61
+      return visibleRect.height / 30 + 1;
62
+  }
63
+
64
+  public void mouseClicked( MouseEvent e )
65
+  {
66
+    if( displayListener != null )
67
+      displayListener.blinkenFrameDisplayClicked( e.getY( ) / zoom, e.getX( ) / zoom, height, width );
68
+  }
69
+
70
+  public void mouseEntered( MouseEvent e )
71
+  {
72
+    if( displayListener != null )
73
+      displayListener.blinkenFrameDisplayEntered( e.getY( ) / zoom, e.getX( ) / zoom, height, width );
74
+  }
75
+
76
+  public void mouseExited( MouseEvent e )
77
+  {
78
+    if( displayListener != null )
79
+      displayListener.blinkenFrameDisplayExited( e.getY( ) / zoom, e.getX( ) / zoom, height, width );
80
+  }
81
+
82
+  public void mousePressed( MouseEvent e )
83
+  {
84
+    if( displayListener != null )
85
+      displayListener.blinkenFrameDisplayPressed( e.getY( ) / zoom, e.getX( ) / zoom, height, width );
86
+  }
87
+
88
+  public void mouseReleased( MouseEvent e )
89
+  {
90
+    if( displayListener != null )
91
+      displayListener.blinkenFrameDisplayReleased( e.getY( ) / zoom, e.getX( ) / zoom, height, width );
92
+  }
93
+
94
+  public void mouseDragged( MouseEvent e )
95
+  {
96
+    if( displayListener != null )
97
+      displayListener.blinkenFrameDisplayDragged( e.getY( ) / zoom, e.getX( ) / zoom, height, width );
98
+  }
99
+
100
+  public void mouseMoved( MouseEvent e )
101
+  {
102
+    if( displayListener != null )
103
+      displayListener.blinkenFrameDisplayMoved( e.getY( ) / zoom, e.getX( ) / zoom, height, width );
104
+  }
105
+
106
+  public void setFrame( BlinkenFrame newFrame )
107
+  {
108
+    //remember frame
109
+    frame = newFrame;
110
+
111
+    //no frame
112
+    if( frame == null )
113
+    {
114
+      //reset dimensions
115
+      height = 0;
116
+      width = 0;
117
+      channels = 1;
118
+      maxval = 1;
119
+    }
120
+    //frame available
121
+    else
122
+    {
123
+      //get dimensions of frame
124
+      height = frame.getHeight( );
125
+      width = frame.getWidth( );
126
+      channels = frame.getChannels( );
127
+      maxval = frame.getMaxval( );
128
+    }
129
+
130
+    updateDisplay( );
131
+  }
132
+
133
+  public void setZoom( int zoomExp )
134
+  {
135
+    //get new zoom factor
136
+    if( zoomExp < 0 )
137
+      zoomExp = 0;
138
+    if( zoomExp > 6 )
139
+      zoomExp = 6;
140
+    zoom = 1 << zoomExp;
141
+
142
+    updateDisplay( );
143
+  }
144
+
145
+  public void updateDisplay( )
146
+  {
147
+    int h, w, y, yy, x, xx;
148
+    boolean sizeChanged, newIcon;
149
+
150
+    //calculate pixel dimensions
151
+    h = height * zoom;
152
+    w = width * zoom;
153
+    sizeChanged = dimension.height != h || dimension.width != w;
154
+
155
+    //update size
156
+    if( sizeChanged )
157
+    {
158
+      dimension.height = h;
159
+      dimension.width = w;
160
+      setPreferredSize( dimension );
161
+      revalidate( ); //propagate update
162
+    }
163
+
164
+    //no frame
165
+    if( frame == null )
166
+    {
167
+      //remove image
168
+      setIcon( null );
169
+      image = null;
170
+      graphics = null;
171
+    }
172
+
173
+    //frame available
174
+    else
175
+    {
176
+      //create new image and icon
177
+      newIcon = sizeChanged || icon == null || image == null || graphics == null;
178
+      if( newIcon )
179
+      {
180
+        image = new BufferedImage( dimension.width, dimension.height, BufferedImage.TYPE_INT_RGB );
181
+	icon = new ImageIcon( image );
182
+	image = icon.getImage( );
183
+        graphics = image.getGraphics( );
184
+      }
185
+
186
+      //create image from frame
187
+      for( y = 0, yy = 0; y < height; y++, yy += zoom )
188
+      {
189
+        for( x = 0, xx = 0; x < width; x++, xx += zoom )
190
+        {
191
+          graphics.setColor( frame.getColor( y, x ) );
192
+          graphics.fillRect( xx, yy, zoom, zoom );
193
+        }
194
+      }
195
+
196
+      //call interceptor
197
+      if( displayInterceptor != null )
198
+        displayInterceptor.blinkenFrameDisplayNewImage( height, width, zoom, graphics );
199
+
200
+      //update image
201
+      if( newIcon )
202
+	setIcon( icon );
203
+      repaint( );
204
+    }
205
+  }
206
+
207
+  public void setDisplayListener( BlinkenFrameDisplayListener newDisplayListener )
208
+  {
209
+    displayListener = newDisplayListener;
210
+  }
211
+
212
+  public void setDisplayInterceptor( BlinkenFrameDisplayInterceptor newDisplayInterceptor )
213
+  {
214
+    displayInterceptor = newDisplayInterceptor;
215
+  }
216
+}
... ...
@@ -0,0 +1,14 @@
1
+/* BlinkenLightsInteractiveMovieProgram
2
+ * version 0.2 date 2004-11-10
3
+ * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ * powered by eventphone.de
7
+ */
8
+
9
+import java.awt.*;
10
+
11
+public interface BlinkenFrameDisplayInterceptor
12
+{
13
+  public void blinkenFrameDisplayNewImage( int height, int width, int zoom, Graphics graphics );
14
+}
... ...
@@ -0,0 +1,18 @@
1
+/* BlinkenLightsInteractiveMovieProgram
2
+ * version 0.2 date 2004-11-10
3
+ * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ * powered by eventphone.de
7
+ */
8
+
9
+public interface BlinkenFrameDisplayListener
10
+{
11
+  public void blinkenFrameDisplayClicked( int y, int x, int height, int width );
12
+  public void blinkenFrameDisplayDragged( int y, int x, int height, int width );
13
+  public void blinkenFrameDisplayEntered( int y, int x, int height, int width );
14
+  public void blinkenFrameDisplayExited( int y, int x, int height, int width );
15
+  public void blinkenFrameDisplayMoved( int y, int x, int height, int width );
16
+  public void blinkenFrameDisplayPressed( int y, int x, int height, int width );
17
+  public void blinkenFrameDisplayReleased( int y, int x, int height, int width );
18
+}
... ...
@@ -0,0 +1,1455 @@
1
+/* BlinkenLightsInteractiveMovieProgram
2
+ * version 0.2 date 2004-11-10
3
+ * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ * powered by eventphone.de
7
+ */
8
+
9
+import java.awt.*;
10
+import java.awt.image.*;
11
+
12
+public class BlinkenFrameEditor extends BlinkenFrameDisplay
13
+                                implements BlinkenFrameDisplayListener, BlinkenFrameDisplayInterceptor
14
+{
15
+  //tool constants
16
+  public static final int toolNone = 0,
17
+                          toolColorPicker = 1,
18
+                          toolDot = 2,
19
+                          toolLine = 3,
20
+                          toolRect = 4,
21
+                          toolFilledRect = 5,
22
+                          toolCircle = 6,
23
+                          toolFilledCircle = 7,
24
+                          toolCopy = 8,
25
+                          toolPaste = 9;
26
+
27
+  //configuration variables
28
+  BlinkenFrameDisplayListener displayListener = null;
29
+  BlinkenFrameDisplayInterceptor displayInterceptor = null;
30
+  BlinkenFrameEditorListener editorListener = null;
31
+  int tool = toolNone;
32
+  Color color = Color.white;
33
+
34
+  //internal state variables
35
+  int pressX = 0, pressY = 0; //position of mouse where mouse button was pressed
36
+  boolean press = false; //if this position is available
37
+  int curX = 0, curY = 0; //current position of mouse
38
+  boolean cur = false; //if this position is available
39
+
40
+  //clipboard for copy & paste
41
+  BlinkenFrame clipboard = null; //contents
42
+  int clipboardRefX = 0, clipboardRefY = 0; //reference position
43
+
44
+  //undo / redo buffers
45
+  static final int maxUndo = 16; //maximum depth of undo / redo buffer
46
+  BlinkenFrame buffersUndo[], buffersRedo[]; //the buffers
47
+  int cntUndo, cntRedo; //number of entries in buffers
48
+
49
+  public BlinkenFrameEditor( )
50
+  {
51
+    int i;
52
+
53
+    super.setDisplayListener( this );
54
+    super.setDisplayInterceptor( this );
55
+
56
+    //allocate array for undo / redo buffers
57
+    buffersUndo = new BlinkenFrame[maxUndo];
58
+    buffersRedo = new BlinkenFrame[maxUndo];
59
+    for( i = 0; i < maxUndo; i++ )
60
+    {
61
+      buffersUndo[i] = null;
62
+      buffersRedo[i] = null;
63
+    }
64
+    cntUndo = 0;
65
+    cntRedo = 0;
66
+  }
67
+
68
+  //mouse button was clicked in the frame display
69
+  public void blinkenFrameDisplayClicked( int y, int x, int height, int width )
70
+  {
71
+    int r, g, b;
72
+
73
+    press = false; //forget position of last mouse button pressing
74
+    curX = x; //remember current mouse position
75
+    curY = y;
76
+    cur = true;
77
+
78
+    updateDisplay( );
79
+    updateInfo( );
80
+
81
+    if( displayListener != null )
82
+      displayListener.blinkenFrameDisplayClicked( y, x, height, width );
83
+  }
84
+
85
+  //mouse was dragged in the frame display
86
+  public void blinkenFrameDisplayDragged( int y, int x, int height, int width )
87
+  {
88
+    boolean curChanged;
89
+
90
+    curChanged = cur && (curX != x || curY != y);
91
+    curX = x; //remember current mouse position
92
+    curY = y;
93
+    cur = true;
94
+
95
+    //apply active tool
96
+    switch( tool )
97
+    {
98
+      case toolColorPicker:
99
+        if( frame != null && cur && editorListener != null )
100
+          editorListener.blinkenFrameEditorColorPicked( frame.getColor( curY, curX ) );
101
+        break;
102
+      case toolDot:
103
+        if( frame != null && cur && curChanged )
104
+        {
105
+          //we do not save undo information here, it was saved in blinkenFrameDisplayPressed
106
+          frame.setColor( curY, curX, color );
107
+          if( editorListener != null )
108
+            editorListener.blinkenFrameEditorFrameChanged( );
109
+        }
110
+        break;
111
+    }
112
+
113
+    updateDisplay( );
114
+    updateInfo( );
115
+
116
+    if( displayListener != null )
117
+      displayListener.blinkenFrameDisplayDragged( y, x, height, width );
118
+  }
119
+
120
+  //mouse entered the frame display
121
+  public void blinkenFrameDisplayEntered( int y, int x, int height, int width )
122
+  {
123
+    curX = x; //remember current mouse position
124
+    curY = y;
125
+    cur = true;
126
+
127
+    updateDisplay( );
128
+    updateInfo( );
129
+
130
+    if( displayListener != null )
131
+      displayListener.blinkenFrameDisplayEntered( y, x, height, width );
132
+  }
133
+
134
+  //mouse exited the frame display
135
+  public void blinkenFrameDisplayExited( int y, int x, int height, int width )
136
+  {
137
+    cur = false; //forget mouse position
138
+
139
+    updateDisplay( );
140
+    updateInfo( );
141
+
142
+    if( displayListener != null )
143
+      displayListener.blinkenFrameDisplayExited( y, x, height, width );
144
+  }
145
+
146
+  //mouse was moved in the frame display
147
+  public void blinkenFrameDisplayMoved( int y, int x, int height, int width )
148
+  {
149
+    press = false; //forget position of last mouse button pressing
150
+    curX = x; //remember current mouse position
151
+    curY = y;
152
+    cur = true;
153
+
154
+    updateDisplay( );
155
+    updateInfo( );
156
+
157
+    if( displayListener != null )
158
+      displayListener.blinkenFrameDisplayMoved( y, x, height, width );
159
+  }
160
+
161
+  //mouse button was pressed in the frame display
162
+  public void blinkenFrameDisplayPressed( int y, int x, int height, int width )
163
+  {
164
+    String pos, size;
165
+    int y1, y2, x1, x2, c;
166
+
167
+    pressX = x; //remember current position as position of last mouse button pressing
168
+    pressY = y;
169
+    press = true;
170
+    curX = x; //remember current mouse position
171
+    curY = y;
172
+    cur = true;
173
+
174
+    //apply active tool
175
+    switch( tool )
176
+    {
177
+      case toolColorPicker:
178
+        if( frame != null && editorListener != null )
179
+          editorListener.blinkenFrameEditorColorPicked( frame.getColor( curY, curX ) );
180
+        break;
181
+      case toolDot:
182
+        if( frame != null && cur )
183
+        {
184
+          undoSave( );
185
+          frame.setColor( curY, curX, color );
186
+          if( editorListener != null )
187
+            editorListener.blinkenFrameEditorFrameChanged( );
188
+        }
189
+        break;
190
+      case toolPaste:
191
+        if( frame != null && clipboard != null )
192
+        {
193
+          undoSave( );
194
+          y1 = curY - clipboardRefY;
195
+          y2 = clipboard.getHeight( );
196
+          x1 = curX - clipboardRefX;
197
+          x2 = clipboard.getWidth( );
198
+          for( y = 0; y < y2; y++ )
199
+            for( x = 0; x < x2; x++ )
200
+              for( c = 0; c < channels; c++ )
201
+                frame.setPixel( y1 + y, x1 + x, c, clipboard.getPixel( y, x, c ) );
202
+          if( editorListener != null )
203
+            editorListener.blinkenFrameEditorFrameChanged( );
204
+        }
205
+        break;
206
+    }
207
+
208
+    updateDisplay( );
209
+    updateInfo( );
210
+
211
+    if( displayListener != null )
212
+      displayListener.blinkenFrameDisplayPressed( y, x, height, width );
213
+  }
214
+
215
+  //mouse button was released in the frame display
216
+  public void blinkenFrameDisplayReleased( int y, int x, int height, int width )
217
+  {
218
+    int y1, y2, x1, x2, xx, yy, c;
219
+    long rx2, ry2, z, zx, zy, z1, z2, z3, z1a, z2a, z3a;
220
+    boolean inv;
221
+
222
+    curX = x; //remember current mouse position
223
+    curY = y;
224
+    press = true;
225
+
226
+    //apply active tool
227
+    switch( tool )
228
+    {
229
+      case toolLine:
230
+        if( frame != null && press && cur )
231
+        {
232
+          undoSave( );
233
+          graphics.setColor( color );
234
+          inv = (pressY < curY && pressX > curX) || (pressY > curY && pressX < curX);
235
+          y1 = Math.min( pressY, curY );
236
+          y2 = Math.max( pressY, curY ) - y1;
237
+          x1 = Math.min( pressX, curX );
238
+          x2 = Math.max( pressX, curX ) - x1;
239
+          if( x2 == y2 ) //neccessary because of x2 == y2 == 0
240
+          {
241
+            if( inv )
242
+              for( x = 0; x <= x2; x++ )
243
+                frame.setColor( (y1 + x), (x1 + x2 - x), color );
244
+            else
245
+              for( x = 0; x <= x2; x++ )
246
+                frame.setColor( (y1 + x), (x1 + x), color );
247
+          }
248
+          else if( x2 > y2 )
249
+          {
250
+            if( inv )
251
+              for( x = 0; x <= x2; x++ )
252
+              {
253
+                y = (x * y2 + x2 / 2) / x2;
254
+                frame.setColor( (y1 + y), (x1 + x2 - x), color );
255
+              }
256
+            else
257
+              for( x = 0; x <= x2; x++ )
258
+              {
259
+                y = (x * y2 + x2 / 2) / x2;
260
+                frame.setColor( (y1 + y), (x1 + x), color );
261
+              }
262
+          }
263
+          else
264
+          {
265
+            if( inv )
266
+              for( y = 0; y <= y2; y++ )
267
+              {
268
+                x = (y * x2 + y2 / 2) / y2;
269
+                frame.setColor( (y1 + y2 - y), (x1 + x), color );
270
+              }
271
+            else
272
+              for( y = 0; y <= y2; y++ )
273
+              {
274
+                x = (y * x2 + y2 / 2) / y2;
275
+                frame.setColor( (y1 + y), (x1 + x), color );
276
+              }
277
+          }
278
+          if( editorListener != null )
279
+            editorListener.blinkenFrameEditorFrameChanged( );
280
+        }
281
+        break;
282
+      case toolRect:
283
+        if( frame != null && press && cur )
284
+        {
285
+          undoSave( );
286
+          y1 = Math.min( pressY, curY );
287
+          y2 = Math.max( pressY, curY );
288
+          x1 = Math.min( pressX, curX );
289
+          x2 = Math.max( pressX, curX );
290
+          for( y = y1; y <= y2; y++ )
291
+            frame.setColor( y, x1, color );
292
+          if( x1 != x2 )
293
+            for( y = y1; y <= y2; y++ )
294
+              frame.setColor( y, x2, color );
295
+          for( x = x1 + 1; x < x2; x++ )
296
+            frame.setColor( y1, x, color );
297
+          if( y1 != y2 )
298
+            for( x = x1 + 1; x < x2; x++ )
299
+              frame.setColor( y2, x, color );
300
+          if( editorListener != null )
301
+            editorListener.blinkenFrameEditorFrameChanged( );
302
+        }
303
+        break;
304
+      case toolFilledRect:
305
+        if( frame != null && press && cur )
306
+        {
307
+          undoSave( );
308
+          y1 = Math.min( pressY, curY );
309
+          y2 = Math.max( pressY, curY );
310
+          x1 = Math.min( pressX, curX );
311
+          x2 = Math.max( pressX, curX );
312
+          for( y = y1; y <= y2; y++ )
313
+            for( x = x1; x <= x2; x++ )
314
+              frame.setColor( y, x, color );
315
+          if( editorListener != null )
316
+            editorListener.blinkenFrameEditorFrameChanged( );
317
+        }
318
+        break;
319
+      case toolCircle:
320
+        if( frame != null && press && cur )
321
+        {
322
+          undoSave( );
323
+          y1 = pressY;
324
+          y2 = Math.abs( curY - pressY );
325
+          x1 = pressX;
326
+          x2 = Math.abs( curX - pressX );
327
+          rx2 = (long)x2 * (long)x2;
328
+          ry2 = (long)y2 * (long)y2;
329
+          for( x = x2, y = 0, z = 0; x >= 0 && y <= y2; )
330
+          {
331
+            frame.setColor( y1 + y, x1 + x, color );
332
+            if( y != 0 )
333
+              frame.setColor( y1 - y, x1 + x, color );
334
+            if( x != 0 )
335
+            {
336
+              frame.setColor( y1 + y, x1 - x, color );
337
+              if( y != 0 )
338
+                frame.setColor( y1 - y, x1 - x, color );
339
+            }
340
+            zy = (long)(2 * y + 1) * rx2;
341
+            zx = (long)(2 * x - 1) * ry2;
342
+            z1a = Math.abs( z1 = z - zy );
343
+            z2a = Math.abs( z2 = z + zx );
344
+            z3a = Math.abs( z3 = z + zx - zy );
345
+            if( z1a < z2a && z1a < z3a ) { y++; z = z1; }
346
+            else if( z2a < z3a ) { x--; z = z2; }
347
+            else { x--; y++; z = z3; }
348
+          } 
349
+          if( editorListener != null )
350
+            editorListener.blinkenFrameEditorFrameChanged( );
351
+        }
352
+        break;
353
+      case toolFilledCircle:
354
+        if( frame != null && press && cur )
355
+        {
356
+          undoSave( );
357
+          y1 = pressY;
358
+          y2 = Math.abs( curY - pressY );
359
+          x1 = pressX;
360
+          x2 = Math.abs( curX - pressX );
361
+          rx2 = (long)x2 * (long)x2;
362
+          ry2 = (long)y2 * (long)y2;
363
+          for( x = x2, y = 0, yy = -1, z = 0; x >= 0 && y <= y2; )
364
+          {
365
+            if( y != yy ) //do not draw a line twice (if two pixels of circle are left/right to each other)
366
+            {
367
+              for( xx = x1 - x; xx <= x1 + x; xx++ )
368
+                frame.setColor( y1 + y, xx, color );
369
+              if( y != 0 )
370
+                for( xx = x1 - x; xx <= x1 + x; xx++ )
371
+                  frame.setColor( y1 - y, xx, color );
372
+              yy = y;
373
+            }
374
+            zy = (long)(2 * y + 1) * rx2;
375
+            zx = (long)(2 * x - 1) * ry2;
376
+            z1a = Math.abs( z1 = z - zy );
377
+            z2a = Math.abs( z2 = z + zx );
378
+            z3a = Math.abs( z3 = z + zx - zy );
379
+            if( z1a < z2a && z1a < z3a ) { y++; z = z1; }
380
+            else if( z2a < z3a ) { x--; z = z2; }
381
+            else { x--; y++; z = z3; }
382
+          } 
383
+          if( editorListener != null )
384
+            editorListener.blinkenFrameEditorFrameChanged( );
385
+        }
386
+        break;
387
+      case toolCopy:
388
+        if( frame != null && press && cur )
389
+        {
390
+          y1 = Math.min( pressY, curY );
391
+          y2 = Math.max( pressY, curY ) - y1 + 1;
392
+          x1 = Math.min( pressX, curX );
393
+          x2 = Math.max( pressX, curX ) - x1 + 1;
394
+          clipboard = new BlinkenFrame( y2, x2, channels, maxval, 0 ); //get contents into clipboard
395
+          for( y = 0; y < y2; y++ )
396
+            for( x = 0; x < x2; x++ )
397
+              for( c = 0; c < channels; c++ )
398
+                clipboard.setPixel( y, x, c, frame.getPixel( y1 + y, x1 + x, c ) );
399
+          clipboardRefX = pressX - x1; //remember reference position
400
+          clipboardRefY = pressY - y1;
401
+        }
402
+        break;
403
+    }
404
+
405
+    press = false; //forget position of last mouse button pressing
406
+
407
+    updateDisplay( );
408
+    updateInfo( );
409
+
410
+    if( displayListener != null )
411
+      displayListener.blinkenFrameDisplayReleased( y, x, height, width );
412
+  }
413
+
414
+  public void blinkenFrameDisplayNewImage( int height, int width, int zoom, Graphics graphics )
415
+  {
416
+    int y1, y2, x1, x2, y, x, xx, yy;
417
+    long rx2, ry2, z, zx, zy, z1, z2, z3, z1a, z2a, z3a;
418
+    boolean inv;
419
+
420
+    //draw preview for active tool
421
+    switch( tool )
422
+    {
423
+      case toolColorPicker:
424
+      case toolDot:
425
+        if( cur )
426
+        {
427
+          graphics.setColor( color );
428
+          graphics.drawRect( zoom * curX, zoom * curY, zoom - 1, zoom - 1 );
429
+        }
430
+        break;
431
+      case toolLine:
432
+        if( press && cur )
433
+        {
434
+          graphics.setColor( color );
435
+          inv = (pressY < curY && pressX > curX) || (pressY > curY && pressX < curX);
436
+          y1 = Math.min( pressY, curY );
437
+          y2 = Math.max( pressY, curY ) - y1;
438
+          x1 = Math.min( pressX, curX );
439
+          x2 = Math.max( pressX, curX ) - x1;
440
+          if( x2 == y2 ) //neccessary because of x2 == y2 == 0
441
+          {
442
+            if( inv )
443
+              for( x = 0; x <= x2; x++ )
444
+                graphics.drawRect( zoom * (x1 + x2 - x), zoom * (y1 + x), zoom - 1, zoom - 1 );
445
+            else
446
+              for( x = 0; x <= x2; x++ )
447
+                graphics.drawRect( zoom * (x1 + x), zoom * (y1 + x), zoom - 1, zoom - 1 );
448
+          }
449
+          else if( x2 > y2 )
450
+          {
451
+            if( inv )
452
+              for( x = 0; x <= x2; x++ )
453
+              {
454
+                y = (x * y2 + x2 / 2) / x2;
455
+                graphics.drawRect( zoom * (x1 + x2 - x), zoom * (y1 + y), zoom - 1, zoom - 1 );
456
+              }
457
+            else
458
+              for( x = 0; x <= x2; x++ )
459
+              {
460
+                y = (x * y2 + x2 / 2) / x2;
461
+                graphics.drawRect( zoom * (x1 + x), zoom * (y1 + y), zoom - 1, zoom - 1 );
462
+              }
463
+          }
464
+          else
465
+          {
466
+            if( inv )
467
+              for( y = 0; y <= y2; y++ )
468
+              {
469
+                x = (y * x2 + y2 / 2) / y2;
470
+                graphics.drawRect( zoom * (x1 + x), zoom * (y1 + y2 - y), zoom - 1, zoom - 1 );
471
+              }
472
+            else
473
+              for( y = 0; y <= y2; y++ )
474
+              {
475
+                x = (y * x2 + y2 / 2) / y2;
476
+                graphics.drawRect( zoom * (x1 + x), zoom * (y1 + y), zoom - 1, zoom - 1 );
477
+              }
478
+          }
479
+        }
480
+        else if( cur )
481
+        {
482
+          graphics.setColor( color );
483
+          graphics.drawRect( zoom * curX, zoom * curY, zoom - 1, zoom - 1 );
484
+        }
485
+        break;
486
+      case toolRect:
487
+        if( press && cur )
488
+        {
489
+          graphics.setColor( color );
490
+          y1 = Math.min( pressY, curY );
491
+          y2 = Math.max( pressY, curY );
492
+          x1 = Math.min( pressX, curX );
493
+          x2 = Math.max( pressX, curX );
494
+          for( y = y1; y <= y2; y++ )
495
+            graphics.drawRect( zoom * x1, zoom * y, zoom - 1, zoom - 1 );
496
+          if( x1 != x2 )
497
+            for( y = y1; y <= y2; y++ )
498
+              graphics.drawRect( zoom * x2, zoom * y, zoom - 1, zoom - 1 );
499
+          for( x = x1 + 1; x < x2; x++ )
500
+            graphics.drawRect( zoom * x, zoom * y1, zoom - 1, zoom - 1 );
501
+          if( y1 != y2 )
502
+            for( x = x1 + 1; x < x2; x++ )
503
+              graphics.drawRect( zoom * x, zoom * y2, zoom - 1, zoom - 1 );
504
+        }
505
+        else if( cur )
506
+        {
507
+          graphics.setColor( color );
508
+          graphics.drawRect( zoom * curX, zoom * curY, zoom - 1, zoom - 1 );
509
+        }
510
+        break;
511
+      case toolFilledRect:
512
+        if( press && cur )
513
+        {
514
+          graphics.setColor( color );
515
+          y1 = Math.min( pressY, curY );
516
+          y2 = Math.max( pressY, curY );
517
+          x1 = Math.min( pressX, curX );
518
+          x2 = Math.max( pressX, curX );
519
+          for( y = y1; y <= y2; y++ )
520
+            for( x = x1; x <= x2; x++ )
521
+              graphics.drawRect( zoom * x, zoom * y, zoom - 1, zoom - 1 );
522
+        }
523
+        else if( cur )
524
+        {
525
+          graphics.setColor( color );
526
+          graphics.drawRect( zoom * curX, zoom * curY, zoom - 1, zoom - 1 );
527
+        }
528
+        break;
529
+      case toolCircle:
530
+        if( press && cur )
531
+        {
532
+          graphics.setColor( color );
533
+          y1 = pressY;
534
+          y2 = Math.abs( curY - pressY );
535
+          x1 = pressX;
536
+          x2 = Math.abs( curX - pressX );
537
+          rx2 = (long)x2 * (long)x2;
538
+          ry2 = (long)y2 * (long)y2;
539
+          for( x = x2, y = 0, z = 0; x >= 0 && y <= y2; )
540
+          {
541
+            graphics.drawRect( zoom * (x1 + x), zoom * (y1 + y), zoom - 1, zoom - 1 );
542
+            if( y != 0 )
543
+              graphics.drawRect( zoom * (x1 + x), zoom * (y1 - y), zoom - 1, zoom - 1 );
544
+            if( x != 0 )
545
+            {
546
+              graphics.drawRect( zoom * (x1 - x), zoom * (y1 + y), zoom - 1, zoom - 1 );
547
+              if( y != 0 )
548
+                graphics.drawRect( zoom * (x1 - x), zoom * (y1 - y), zoom - 1, zoom - 1 );
549
+            }
550
+            zy = (long)(2 * y + 1) * rx2;
551
+            zx = (long)(2 * x - 1) * ry2;
552
+            z1a = Math.abs( z1 = z - zy );
553
+            z2a = Math.abs( z2 = z + zx );
554
+            z3a = Math.abs( z3 = z + zx - zy );
555
+            if( z1a < z2a && z1a < z3a ) { y++; z = z1; }
556
+            else if( z2a < z3a ) { x--; z = z2; }
557
+            else { x--; y++; z = z3; }
558
+          } 
559
+        }
560
+        else if( cur )
561
+        {
562
+          graphics.setColor( color );
563
+          graphics.drawRect( zoom * curX, zoom * curY, zoom - 1, zoom - 1 );
564
+        }
565
+        break;
566
+      case toolFilledCircle:
567
+        if( press && cur )
568
+        {
569
+          graphics.setColor( color );
570
+          y1 = pressY;
571
+          y2 = Math.abs( curY - pressY );
572
+          x1 = pressX;
573
+          x2 = Math.abs( curX - pressX );
574
+          rx2 = (long)x2 * (long)x2;
575
+          ry2 = (long)y2 * (long)y2;
576
+          for( x = x2, y = 0, yy = -1, z = 0; x >= 0 && y <= y2; )
577
+          {
578
+            if( y != yy ) //do not draw a line twice (if two pixels of circle are left/right to each other)
579
+            {
580
+              for( xx = x1 - x; xx <= x1 + x; xx++ )
581
+                graphics.drawRect( zoom * xx, zoom * (y1 + y), zoom - 1, zoom - 1 );
582
+              if( y != 0 )
583
+                for( xx = x1 - x; xx <= x1 + x; xx++ )
584
+                  graphics.drawRect( zoom * xx, zoom * (y1 - y), zoom - 1, zoom - 1 );
585
+              yy = y;
586
+            }
587
+            zy = (long)(2 * y + 1) * rx2;
588
+            zx = (long)(2 * x - 1) * ry2;
589
+            z1a = Math.abs( z1 = z - zy );
590
+            z2a = Math.abs( z2 = z + zx );
591
+            z3a = Math.abs( z3 = z + zx - zy );
592
+            if( z1a < z2a && z1a < z3a ) { y++; z = z1; }
593
+            else if( z2a < z3a ) { x--; z = z2; }
594
+            else { x--; y++; z = z3; }
595
+          } 
596
+        }
597
+        else if( cur )
598
+        {
599
+          graphics.setColor( color );
600
+          graphics.drawRect( zoom * curX, zoom * curY, zoom - 1, zoom - 1 );
601
+        }
602
+        break;
603
+      case toolCopy:
604
+        if( press && cur )
605
+        {
606
+          graphics.setColor( color );
607
+          y1 = Math.min( pressY, curY );
608
+          y2 = Math.max( pressY, curY ) - y1 + 1;
609
+          x1 = Math.min( pressX, curX );
610
+          x2 = Math.max( pressX, curX ) - x1 + 1;
611
+          graphics.drawRect( zoom * x1, zoom * y1, zoom * x2 - 1, zoom * y2 - 1 );
612
+        }
613
+        else if( cur )
614
+        {
615
+          graphics.setColor( color );
616
+          graphics.drawRect( zoom * curX, zoom * curY, zoom - 1, zoom - 1 );
617
+        }
618
+        break;
619
+      case toolPaste:
620
+        if( clipboard != null )
621
+        {
622
+          y1 = curY - clipboardRefY;
623
+          y2 = clipboard.getHeight( );
624
+          x1 = curX - clipboardRefX;
625
+          x2 = clipboard.getWidth( );
626
+          for( y = 0; y < y2; y++ )
627
+          {
628
+            for( x = 0; x < x2; x++ )
629
+            {
630
+              graphics.setColor( clipboard.getColor( y, x ) );
631
+              graphics.drawRect( zoom * (x1 + x), zoom * (y1 + y), zoom - 1, zoom - 1 );
632
+            }
633
+          }
634
+        }
635
+    }
636
+
637
+    if( displayInterceptor != null )
638
+      displayInterceptor.blinkenFrameDisplayNewImage( height, width, zoom, graphics );
639
+  }
640
+  
641
+  public void setFrame( BlinkenFrame newFrame )
642
+  {
643
+    super.setFrame( newFrame );
644
+
645
+    //adapt clipboard to new format
646
+    if( newFrame != null && clipboard != null )
647
+      clipboard.resize( clipboard.getHeight( ), //keep size of clipboard
648
+                        clipboard.getWidth( ),
649
+                        frame.getChannels( ), //adapt color format of clipboard
650
+                        frame.getMaxval( ) );
651
+
652
+    //reset undo function
653
+    undoReset( );
654
+  }
655
+
656
+  public void updateDisplay( )
657
+  {
658
+    super.updateDisplay( );
659
+
660
+    updateInfo( );
661
+  }
662
+
663
+  private void updateInfo( )
664
+  {
665
+    String pos = "", size = "";
666
+    int y1, y2, x1, x2;
667
+
668
+    if( editorListener != null )
669
+    {
670
+      //current mouse position is available
671
+      if( cur )
672
+      {
673
+        pos = curX + "," + curY;
674
+
675
+        //position where mouse button was pressed is available
676
+        if( press )
677
+        {
678
+          //show info for active tool
679
+          switch( tool )
680
+          {
681
+            //rectangular region
682
+            case toolLine:
683
+            case toolRect:
684
+            case toolFilledRect:
685
+            case toolCopy:
686
+              pos = pressX + "," + pressY + ".." + pos
687
+                  + " (" + (Math.abs( pressX - curX ) + 1) + "x" + (Math.abs( pressY - curY ) + 1) + ")";
688
+              break;
689
+            //circular region
690
+            case toolCircle:
691
+            case toolFilledCircle:
692
+              pos = pressX + "," + pressY + ".." + pos
693
+                  + " (" + Math.abs( pressX - curX ) + "r" + Math.abs( pressY - curY ) + ")";
694
+              break;
695
+          }
696
+        }
697
+
698
+        //pasting
699
+        if( tool == toolPaste && clipboard != null )
700
+        {
701
+          y1 = curY - clipboardRefY;
702
+          y2 = clipboard.getHeight( );
703
+          x1 = curX - clipboardRefX;
704
+          x2 = clipboard.getWidth( );
705
+          pos += " [" + x1 + "," + y1 + ".." + (x1 + x2 - 1) + "," + (y1 + y2 - 1) + "]"
706
+               + " (" + x2 + "," + y2 + ")";
707
+        }
708
+
709
+        pos += " ";
710
+      }
711
+
712
+      //a frame is availabale
713
+      if( height >= 1 && width >= 1 )
714
+        size = " (" + width + "x" + height + "-" + channels + "/" + (maxval + 1) + ")";
715
+
716
+      if( editorListener != null )
717
+        editorListener.blinkenFrameEditorInfo( pos + "-" + size );
718
+    }
719
+  }
720
+
721
+  public void setTool( int newTool )
722
+  {
723
+    tool = newTool;
724
+
725
+    pressX = -1; //forget position of last mouse button pressing
726
+    pressY = -1;
727
+
728
+    updateDisplay( );
729
+    updateInfo( );
730
+  }
731
+
732
+  public void setColor( Color newColor )
733
+  {
734
+    color = newColor;
735
+
736
+    updateDisplay( );
737
+  }
738
+
739
+  public void actionInvert( )
740
+  {
741
+    int he, wi, ch, ma, y, x, c;
742
+
743
+    //invert clipboard
744
+    if( tool == toolPaste )
745
+    {
746
+      if( clipboard != null )
747
+      {
748
+        he = clipboard.getHeight( );
749
+        wi = clipboard.getWidth( );
750
+        ch = clipboard.getChannels( );
751
+        ma = clipboard.getMaxval( );
752
+        for( y = 0; y < he; y++ )
753
+          for( x = 0; x < wi; x++ )
754
+            for( c = 0; c < ch; c++ )
755
+              clipboard.setPixel( y, x, c, (byte)(ma - ((int)clipboard.getPixel( y, x, c ) & 0xFF)) );
756
+      }
757
+    }
758
+
759
+    //invert frame
760
+    else
761
+    {
762
+      if( frame != null )
763
+      {
764
+        undoSave( );
765
+        for( y = 0; y < height; y++ )
766
+          for( x = 0; x < width; x++ )
767
+            for( c = 0; c < channels; c++ )
768
+              frame.setPixel( y, x, c, (byte)(maxval - ((int)frame.getPixel( y, x, c ) & 0xFF)) );
769
+        updateDisplay( );
770
+        if( editorListener != null )
771
+          editorListener.blinkenFrameEditorFrameChanged( );
772
+      }
773
+    }
774
+  }
775
+
776
+  public void actionRotate90( )
777
+  {
778
+    int he, wi, ch, y, x, c, y1, x1;
779
+    BlinkenFrame buffer;
780
+
781
+    //rotate clipboard 90 degrees
782
+    if( tool == toolPaste )
783
+    {
784
+      if( clipboard != null )
785
+      {
786
+        he = clipboard.getWidth( );
787
+        wi = clipboard.getHeight( );
788
+        ch = clipboard.getChannels( );
789
+        buffer = new BlinkenFrame( he, wi, ch, clipboard.getMaxval( ), 0 );
790
+        for( y = 0; y < he; y++ )
791
+          for( x = 0; x < wi; x++ )
792
+            for( c = 0; c < ch; c++ )
793
+              buffer.setPixel( y, x, c, clipboard.getPixel( wi - 1 - x, y, c ) );
794
+        clipboard = buffer;
795
+        x = wi - 1 - clipboardRefY;
796
+        y = clipboardRefX;
797
+        clipboardRefY = y;
798
+        clipboardRefX = x;
799
+      }
800
+    }
801
+
802
+    //rotate frame 90 degrees
803
+    else
804
+    {
805
+      if( frame != null )
806
+      {
807
+        undoSave( );
808
+        he = Math.min( height, width );
809
+        wi = he;
810
+        y1 = (height - he) / 2;
811
+        x1 = (width - wi) / 2;
812
+        buffer = new BlinkenFrame( he, wi, channels, maxval, 0 );
813
+        for( y = 0; y < he; y++ )
814
+          for( x = 0; x < wi; x++ )
815
+            for( c = 0; c < channels; c++ )
816
+              buffer.setPixel( y, x, c, frame.getPixel( y1 + wi - 1 - x, x1 + y, c ) );
817
+        for( y = 0; y < he; y++ )
818
+          for( x = 0; x < wi; x++ )
819
+            for( c = 0; c < channels; c++ )
820
+              frame.setPixel( y1 + y, x1 + x, c, buffer.getPixel( y, x, c ) );
821
+        updateDisplay( );
822
+        if( editorListener != null )
823
+          editorListener.blinkenFrameEditorFrameChanged( );
824
+      }
825
+    }
826
+  }
827
+
828
+  public void actionRotate180( )
829
+  {
830
+    int he, wi, ch, y, x, c;
831
+    byte b1, b2;
832
+
833
+    //rotate clipboard 180 degrees
834
+    if( tool == toolPaste )
835
+    {
836
+      if( clipboard != null )
837
+      {
838
+        he = clipboard.getHeight( );
839
+        wi = clipboard.getWidth( );
840
+        ch = clipboard.getChannels( );
841
+        for( y = 0; y < he / 2; y++ )
842
+          for( x = 0; x < wi; x++ )
843
+            for( c = 0; c < ch; c++ )
844
+            {
845
+              b1 = clipboard.getPixel( y, x, c );
846
+              b2 = clipboard.getPixel( he - 1 - y, wi - 1 - x, c );
847
+              clipboard.setPixel( he - 1 - y, wi - 1 - x, c, b1 );
848
+              clipboard.setPixel( y, x, c, b2 );
849
+            }
850
+        if( (he & 1) != 0 )
851
+          for( x = 0; x < wi / 2; x++ )
852
+            for( c = 0; c < ch; c++ )
853
+            {
854
+              b1 = clipboard.getPixel( he / 2, x, c );
855
+              b2 = clipboard.getPixel( he / 2, wi - 1 - x, c );
856
+              clipboard.setPixel( he / 2, wi - 1 - x, c, b1 );
857
+              clipboard.setPixel( he / 2, x, c, b2 );
858
+            }
859
+        clipboardRefY = he - 1 - clipboardRefY;
860
+        clipboardRefX = wi - 1 - clipboardRefX;
861
+      }
862
+    }
863
+
864
+    //rotate frame 180 degrees
865
+    else
866
+    {
867
+      if( frame != null )
868
+      {
869
+        undoSave( );
870
+        for( y = 0; y < height / 2; y++ )
871
+          for( x = 0; x < width; x++ )
872
+            for( c = 0; c < channels; c++ )
873
+            {
874
+              b1 = frame.getPixel( y, x, c );
875
+              b2 = frame.getPixel( height - 1 - y, width - 1 - x, c );
876
+              frame.setPixel( height - 1 - y, width - 1 - x, c, b1 );
877
+              frame.setPixel( y, x, c, b2 );
878
+            }
879
+        if( (height & 1) != 0 )
880
+          for( x = 0; x < width / 2; x++ )
881
+            for( c = 0; c < channels; c++ )
882
+            {
883
+              b1 = frame.getPixel( height / 2, x, c );
884
+              b2 = frame.getPixel( height / 2, width - 1 - x, c );
885
+              frame.setPixel( height / 2, width - 1 - x, c, b1 );
886
+              frame.setPixel( height / 2, x, c, b2 );
887
+            }
888
+        updateDisplay( );
889
+        if( editorListener != null )
890
+          editorListener.blinkenFrameEditorFrameChanged( );
891
+      }
892
+    }
893
+  }
894
+
895
+  public void actionRotate270( )
896
+  {
897
+    int he, wi, ch, y, x, c, y1, x1;
898
+    BlinkenFrame buffer;
899
+
900
+    //rotate clipboard 270 degrees
901
+    if( tool == toolPaste )
902
+    {
903
+      if( clipboard != null )
904
+      {
905
+        he = clipboard.getWidth( );
906
+        wi = clipboard.getHeight( );
907
+        ch = clipboard.getChannels( );
908
+        buffer = new BlinkenFrame( he, wi, ch, clipboard.getMaxval( ), 0 );
909
+        for( y = 0; y < he; y++ )
910
+          for( x = 0; x < wi; x++ )
911
+            for( c = 0; c < ch; c++ )
912
+              buffer.setPixel( y, x, c, clipboard.getPixel( x, he - 1 - y, c ) );
913
+        clipboard = buffer;
914
+        x = clipboardRefY;
915
+        y = he - 1 - clipboardRefX;
916
+        clipboardRefY = y;
917
+        clipboardRefX = x;
918
+      }
919
+    }
920
+
921
+    //rotate frame 270 degrees
922
+    else
923
+    {
924
+      if( frame != null )
925
+      {
926
+        undoSave( );
927
+        he = Math.min( height, width );
928
+        wi = he;
929
+        y1 = (height - he) / 2;
930
+        x1 = (width - wi) / 2;
931
+        buffer = new BlinkenFrame( he, wi, channels, maxval, 0 );
932
+        for( y = 0; y < he; y++ )
933
+          for( x = 0; x < wi; x++ )
934
+            for( c = 0; c < channels; c++ )
935
+              buffer.setPixel( y, x, c, frame.getPixel( y1 + x, x1 + he - 1 - y, c ) );
936
+        for( y = 0; y < he; y++ )
937
+          for( x = 0; x < wi; x++ )
938
+            for( c = 0; c < channels; c++ )
939
+              frame.setPixel( y1 + y, x1 + x, c, buffer.getPixel( y, x, c ) );
940
+        updateDisplay( );
941
+        if( editorListener != null )
942
+          editorListener.blinkenFrameEditorFrameChanged( );
943
+      }
944
+    }
945
+  }
946
+
947
+  public void actionMirrorHor( )
948
+  {
949
+    int he, wi, ch, y, x, c;
950
+    byte b1, b2;
951
+
952
+    //mirror clipboard horizontally
953
+    if( tool == toolPaste )
954
+    {
955
+      if( clipboard != null )
956
+      {
957
+        he = clipboard.getHeight( );
958
+        wi = clipboard.getWidth( );
959
+        ch = clipboard.getChannels( );
960
+        for( y = 0; y < he / 2; y++ )
961
+          for( x = 0; x < wi; x++ )
962
+            for( c = 0; c < ch; c++ )
963
+            {
964
+              b1 = clipboard.getPixel( y, x, c );
965
+              b2 = clipboard.getPixel( he - 1 - y, x, c );
966
+              clipboard.setPixel( he - 1 - y, x, c, b1 );
967
+              clipboard.setPixel( y, x, c, b2 );
968
+            }
969
+        clipboardRefY = he - 1 - clipboardRefY;
970
+      }
971
+    }
972
+
973
+    //mirror frame horizontally
974
+    else
975
+    {
976
+      if( frame != null )
977
+      {
978
+        undoSave( );
979
+        for( y = 0; y < height / 2; y++ )
980
+          for( x = 0; x < width; x++ )
981
+            for( c = 0; c < channels; c++ )
982
+            {
983
+              b1 = frame.getPixel( y, x, c );
984
+              b2 = frame.getPixel( height - 1 - y, x, c );
985
+              frame.setPixel( height - 1 - y, x, c, b1 );
986
+              frame.setPixel( y, x, c, b2 );
987
+            }
988
+        updateDisplay( );
989
+        if( editorListener != null )
990
+          editorListener.blinkenFrameEditorFrameChanged( );
991
+      }
992
+    }
993
+  }
994
+
995
+  public void actionMirrorVer( )
996
+  {
997
+    int he, wi, ch, y, x, c;
998
+    byte b1, b2;
999
+
1000
+    //mirror clipboard vertically
1001
+    if( tool == toolPaste )
1002
+    {
1003
+      if( clipboard != null )
1004
+      {
1005
+        he = clipboard.getHeight( );
1006
+        wi = clipboard.getWidth( );
1007
+        ch = clipboard.getChannels( );
1008
+        for( y = 0; y < he; y++ )
1009
+          for( x = 0; x < wi / 2; x++ )
1010
+            for( c = 0; c < ch; c++ )
1011
+            {
1012
+              b1 = clipboard.getPixel( y, x, c );
1013
+              b2 = clipboard.getPixel( y, wi - 1 - x, c );
1014
+              clipboard.setPixel( y, wi - 1 - x, c, b1 );
1015
+              clipboard.setPixel( y, x, c, b2 );
1016
+            }
1017
+        clipboardRefX = he - 1 - clipboardRefX;
1018
+      }
1019
+    }
1020
+
1021
+    //mirror frame vertically
1022
+    else
1023
+    {
1024
+      if( frame != null )
1025
+      {
1026
+        undoSave( );
1027
+        for( y = 0; y < height; y++ )
1028
+          for( x = 0; x < width / 2; x++ )
1029
+            for( c = 0; c < channels; c++ )
1030
+            {
1031
+              b1 = frame.getPixel( y, x, c );
1032
+              b2 = frame.getPixel( y, width - 1 - x, c );
1033
+              frame.setPixel( y, width - 1 - x, c, b1 );
1034
+              frame.setPixel( y, x, c, b2 );
1035
+            }
1036
+        updateDisplay( );
1037
+        if( editorListener != null )
1038
+          editorListener.blinkenFrameEditorFrameChanged( );
1039
+      }
1040
+    }
1041
+  }
1042
+
1043
+  public void actionMirrorDiag( )
1044
+  {
1045
+    int he, wi, ch, y, x, c, y1, x1;
1046
+    BlinkenFrame buffer;
1047
+
1048
+    //mirror clipboard diagonally (\)
1049
+    if( tool == toolPaste )
1050
+    {
1051
+      if( clipboard != null )
1052
+      {
1053
+        he = clipboard.getWidth( );
1054
+        wi = clipboard.getHeight( );
1055
+        ch = clipboard.getChannels( );
1056
+        buffer = new BlinkenFrame( he, wi, ch, clipboard.getMaxval( ), 0 );
1057
+        for( y = 0; y < he; y++ )
1058
+          for( x = 0; x < wi; x++ )
1059
+            for( c = 0; c < ch; c++ )
1060
+              buffer.setPixel( y, x, c, clipboard.getPixel( x, y, c ) );
1061
+        clipboard = buffer;
1062
+        x = clipboardRefY;
1063
+        y = clipboardRefX;
1064
+        clipboardRefY = y;
1065
+        clipboardRefX = x;
1066
+      }
1067
+    }
1068
+
1069
+    //mirror frame diagonally (\)
1070
+    else
1071
+    {
1072
+      if( frame != null )
1073
+      {
1074
+        undoSave( );
1075
+        he = Math.min( height, width );
1076
+        wi = he;
1077
+        y1 = (height - he) / 2;
1078
+        x1 = (width - wi) / 2;
1079
+        buffer = new BlinkenFrame( he, wi, channels, maxval, 0 );
1080
+        for( y = 0; y < he; y++ )
1081
+          for( x = 0; x < wi; x++ )
1082
+            for( c = 0; c < channels; c++ )
1083
+              buffer.setPixel( y, x, c, frame.getPixel( y1 + x, x1 + y, c ) );
1084
+        for( y = 0; y < he; y++ )
1085
+          for( x = 0; x < wi; x++ )
1086
+            for( c = 0; c < channels; c++ )
1087
+              frame.setPixel( y1 + y, x1 + x, c, buffer.getPixel( y, x, c ) );
1088
+        updateDisplay( );
1089
+        if( editorListener != null )
1090
+          editorListener.blinkenFrameEditorFrameChanged( );
1091
+      }
1092
+    }
1093
+  }
1094
+
1095
+  public void actionMirrorDiag2( )
1096
+  {
1097
+    int he, wi, ch, y, x, c, y1, x1;
1098
+    BlinkenFrame buffer;
1099
+
1100
+    //mirror clipboard diagonally (/)
1101
+    if( tool == toolPaste )
1102
+    {
1103
+      if( clipboard != null )
1104
+      {
1105
+        he = clipboard.getWidth( );
1106
+        wi = clipboard.getHeight( );
1107
+        ch = clipboard.getChannels( );
1108
+        buffer = new BlinkenFrame( he, wi, ch, clipboard.getMaxval( ), 0 );
1109
+        for( y = 0; y < he; y++ )
1110
+          for( x = 0; x < wi; x++ )
1111
+            for( c = 0; c < ch; c++ )
1112
+              buffer.setPixel( y, x, c, clipboard.getPixel( wi - 1 - x, he - 1 - y, c ) );
1113
+        clipboard = buffer;
1114
+        x = wi - 1 - clipboardRefY;
1115
+        y = he - 1 - clipboardRefX;
1116
+        clipboardRefY = y;
1117
+        clipboardRefX = x;
1118
+      }
1119
+    }
1120
+
1121
+    //mirror frame diagonally (/)
1122
+    else
1123
+    {
1124
+      if( frame != null )
1125
+      {
1126
+        undoSave( );
1127
+        he = Math.min( height, width );
1128
+        wi = he;
1129
+        y1 = (height - he) / 2;
1130
+        x1 = (width - wi) / 2;
1131
+        buffer = new BlinkenFrame( he, wi, channels, maxval, 0 );
1132
+        for( y = 0; y < he; y++ )
1133
+          for( x = 0; x < wi; x++ )
1134
+            for( c = 0; c < channels; c++ )
1135
+              buffer.setPixel( y, x, c, frame.getPixel( y1 + wi - 1 - x, x1 + he - 1 - y, c ) );
1136
+        for( y = 0; y < he; y++ )
1137
+          for( x = 0; x < wi; x++ )
1138
+            for( c = 0; c < channels; c++ )
1139
+              frame.setPixel( y1 + y, x1 + x, c, buffer.getPixel( y, x, c ) );
1140
+        updateDisplay( );
1141
+        if( editorListener != null )
1142
+          editorListener.blinkenFrameEditorFrameChanged( );
1143
+      }
1144
+    }
1145
+  }
1146
+
1147
+  public void actionRollLeft( )
1148
+  {
1149
+    int he, wi, ch, y, x, c;
1150
+    byte b;
1151
+
1152
+    //roll clipboard left
1153
+    if( tool == toolPaste )
1154
+    {
1155
+      if( clipboard != null )
1156
+      {
1157
+        he = clipboard.getHeight( );
1158
+        wi = clipboard.getWidth( );
1159
+        ch = clipboard.getChannels( );
1160
+        for( y = 0; y < he; y++ )
1161
+          for( c = 0; c < ch; c++ )
1162
+          {
1163
+            b = clipboard.getPixel( y, 0, c );
1164
+            for( x = 1; x < wi; x++ )
1165
+              clipboard.setPixel( y, x - 1, c, clipboard.getPixel( y, x, c ) );
1166
+            clipboard.setPixel( y, wi - 1, c, b );
1167
+          }
1168
+      }
1169
+    }
1170
+
1171
+    //roll frame left
1172
+    else
1173
+    {
1174
+      if( frame != null )
1175
+      {
1176
+        undoSave( );
1177
+        for( y = 0; y < height; y++ )
1178
+          for( c = 0; c < channels; c++ )
1179
+          {
1180
+            b = frame.getPixel( y, 0, c );
1181
+            for( x = 1; x < width; x++ )
1182
+              frame.setPixel( y, x - 1, c, frame.getPixel( y, x, c ) );
1183
+            frame.setPixel( y, width - 1, c, b );
1184
+          }
1185
+        updateDisplay( );
1186
+        if( editorListener != null )
1187
+          editorListener.blinkenFrameEditorFrameChanged( );
1188
+      }
1189
+    }
1190
+  }
1191
+
1192
+  public void actionRollRight( )
1193
+  {
1194
+    int he, wi, ch, y, x, c;
1195
+    byte b;
1196
+
1197
+    //roll clipboard right
1198
+    if( tool == toolPaste )
1199
+    {
1200
+      if( clipboard != null )
1201
+      {
1202
+        he = clipboard.getHeight( );
1203
+        wi = clipboard.getWidth( );
1204
+        ch = clipboard.getChannels( );
1205
+        for( y = 0; y < he; y++ )
1206
+          for( c = 0; c < ch; c++ )
1207
+          {
1208
+            b = clipboard.getPixel( y, wi - 1, c );
1209
+            for( x = wi - 2; x >= 0; x-- )
1210
+              clipboard.setPixel( y, x + 1, c, clipboard.getPixel( y, x, c ) );
1211
+            clipboard.setPixel( y, 0, c, b );
1212
+          }
1213
+      }
1214
+    }
1215
+
1216
+    //roll frame right
1217
+    else
1218
+    {
1219
+      if( frame != null )
1220
+      {
1221
+        undoSave( );
1222
+        for( y = 0; y < height; y++ )
1223
+          for( c = 0; c < channels; c++ )
1224
+          {
1225
+            b = frame.getPixel( y, width - 1, c );
1226
+            for( x = width - 2; x >= 0; x-- )
1227
+              frame.setPixel( y, x + 1, c, frame.getPixel( y, x, c ) );
1228
+            frame.setPixel( y, 0, c, b );
1229
+          }
1230
+        updateDisplay( );
1231
+        if( editorListener != null )
1232
+          editorListener.blinkenFrameEditorFrameChanged( );
1233
+      }
1234
+    }
1235
+  }
1236
+
1237
+  public void actionRollUp( )
1238
+  {
1239
+    int he, wi, ch, y, x, c;
1240
+    byte b;
1241
+
1242
+    //roll clipboard up
1243
+    if( tool == toolPaste )
1244
+    {
1245
+      if( clipboard != null )
1246
+      {
1247
+        he = clipboard.getHeight( );
1248
+        wi = clipboard.getWidth( );
1249
+        ch = clipboard.getChannels( );
1250
+        for( x = 0; x < wi; x++ )
1251
+          for( c = 0; c < ch; c++ )
1252
+          {
1253
+            b = clipboard.getPixel( 0, x, c );
1254
+            for( y = 1; y < he; y++ )
1255
+              clipboard.setPixel( y - 1, x, c, clipboard.getPixel( y, x, c ) );
1256
+            clipboard.setPixel( he - 1, x, c, b );
1257
+          }
1258
+      }
1259
+    }
1260
+
1261
+    //roll frame up
1262
+    else
1263
+    {
1264
+      if( frame != null )
1265
+      {
1266
+        undoSave( );
1267
+        for( x = 0; x < width; x++ )
1268
+          for( c = 0; c < channels; c++ )
1269
+          {
1270
+            b = frame.getPixel( 0, x, c );
1271
+            for( y = 1; y < height; y++ )
1272
+              frame.setPixel( y - 1, x, c, frame.getPixel( y, x, c ) );
1273
+            frame.setPixel( height - 1, x, c, b );
1274
+          }
1275
+        updateDisplay( );
1276
+        if( editorListener != null )
1277
+          editorListener.blinkenFrameEditorFrameChanged( );
1278
+      }
1279
+    }
1280
+  }
1281
+
1282
+  public void actionRollDown( )
1283
+  {
1284
+    int he, wi, ch, y, x, c;
1285
+    byte b;
1286
+
1287
+    //roll clipboard down
1288
+    if( tool == toolPaste )
1289
+    {
1290
+      if( clipboard != null )
1291
+      {
1292
+        he = clipboard.getHeight( );
1293
+        wi = clipboard.getWidth( );
1294
+        ch = clipboard.getChannels( );
1295
+        for( x = 0; x < wi; x++ )
1296
+          for( c = 0; c < ch; c++ )
1297
+          {
1298
+            b = clipboard.getPixel( he - 1, x, c );
1299
+            for( y = he - 2; y >= 0; y-- )
1300
+              clipboard.setPixel( y + 1, x, c, clipboard.getPixel( y, x, c ) );
1301
+            clipboard.setPixel( 0, x, c, b );
1302
+          }
1303
+      }
1304
+    }
1305
+
1306
+    //roll frame down
1307
+    else
1308
+    {
1309
+      if( frame != null )
1310
+      {
1311
+        undoSave( );
1312
+        for( x = 0; x < width; x++ )
1313
+          for( c = 0; c < channels; c++ )
1314
+          {
1315
+            b = frame.getPixel( height - 1, x, c );
1316
+            for( y = height - 2; y >= 0; y-- )
1317
+              frame.setPixel( y + 1, x, c, frame.getPixel( y, x, c ) );
1318
+            frame.setPixel( 0, x, c, b );
1319
+          }
1320
+        updateDisplay( );
1321
+        if( editorListener != null )
1322
+          editorListener.blinkenFrameEditorFrameChanged( );
1323
+      }
1324
+    }
1325
+  }
1326
+
1327
+  public void actionUndo( )
1328
+  {
1329
+    BlinkenFrame info;
1330
+    int y, x, c;
1331
+
1332
+    if( frame != null && cntUndo > 0 )
1333
+    {
1334
+      //save redo info
1335
+      //(redo buffer cannot be full here,
1336
+      // because it is the same size as the undo buffer)
1337
+      buffersRedo[cntRedo] = new BlinkenFrame( frame );
1338
+      cntRedo++;
1339
+
1340
+      //get and remove undo info from buffer
1341
+      cntUndo--;
1342
+      info = buffersUndo[cntUndo];
1343
+      buffersUndo[cntUndo] = null;
1344
+
1345
+      //restore frame from info
1346
+      for( y = 0; y < height; y++ )
1347
+        for( x = 0; x < width; x++ )
1348
+          for( c = 0; c < channels; c++ )
1349
+            frame.setPixel( y, x, c, info.getPixel( y, x, c ) );
1350
+      updateDisplay( );
1351
+      if( editorListener != null )
1352
+        editorListener.blinkenFrameEditorFrameChanged( );
1353
+
1354
+      //tell listener if undo or redo possible at the moment
1355
+      if( editorListener != null )
1356
+        editorListener.blinkenFrameEditorCanUndoRedo( cntUndo > 0, cntRedo > 0 );
1357
+    }
1358
+  }
1359
+
1360
+  public void actionRedo( )
1361
+  {
1362
+    BlinkenFrame info;
1363
+    int y, x, c;
1364
+
1365
+    if( frame != null && cntRedo > 0 )
1366
+    {
1367
+      //save undo info
1368
+      //(undo buffer cannot be full here,
1369
+      // because it is the same size as the redo buffer)
1370
+      buffersUndo[cntUndo] = new BlinkenFrame( frame );
1371
+      cntUndo++;
1372
+
1373
+      //get and remove redo info from buffer
1374
+      cntRedo--;
1375
+      info = buffersRedo[cntRedo];
1376
+      buffersRedo[cntRedo] = null;
1377
+
1378
+      //restore frame from info
1379
+      for( y = 0; y < height; y++ )
1380
+        for( x = 0; x < width; x++ )
1381
+          for( c = 0; c < channels; c++ )
1382
+            frame.setPixel( y, x, c, info.getPixel( y, x, c ) );
1383
+      updateDisplay( );
1384
+      if( editorListener != null )
1385
+        editorListener.blinkenFrameEditorFrameChanged( );
1386
+
1387
+      //tell listener if undo or redo possible at the moment
1388
+      if( editorListener != null )
1389
+        editorListener.blinkenFrameEditorCanUndoRedo( cntUndo > 0, cntRedo > 0 );
1390
+    }
1391
+  }
1392
+
1393
+  private void undoReset( )
1394
+  {
1395
+    int i;
1396
+
1397
+    //clear undo / redo buffers
1398
+    for( i = 0; i < maxUndo; i++ )
1399
+    {
1400
+      buffersUndo[i] = null;
1401
+      buffersRedo[i] = null;
1402
+    }
1403
+    cntUndo = 0;
1404
+    cntRedo = 0;
1405
+
1406
+    //undo or redo not possible at the moment
1407
+    if( editorListener != null )
1408
+      editorListener.blinkenFrameEditorCanUndoRedo( false, false );
1409
+  }
1410
+
1411
+  private void undoSave( )
1412
+  {
1413
+    int i;
1414
+
1415
+    if( frame != null )
1416
+    {
1417
+      //save undo info
1418
+      if( cntUndo >= maxUndo )
1419
+      {
1420
+        for( i = 0; i < maxUndo - 1; i++ ) //drop oldest undo info
1421
+          buffersUndo[i] = buffersUndo[i + 1];
1422
+        cntUndo = maxUndo - 1;
1423
+      }
1424
+      buffersUndo[cntUndo] = new BlinkenFrame( frame ); //save new undo info
1425
+      cntUndo++;
1426
+
1427
+      //clear redo buffer
1428
+      for( i = 0; i < maxUndo; i++ )
1429
+        buffersRedo[i] = null;
1430
+      cntRedo = 0;
1431
+    
1432
+      //only undo possible at the moment
1433
+      if( editorListener != null )
1434
+        editorListener.blinkenFrameEditorCanUndoRedo( true, false );
1435
+    }
1436
+  }
1437
+
1438
+  public void setDisplayListener( BlinkenFrameDisplayListener newDisplayListener )
1439
+  {
1440
+    displayListener = newDisplayListener;
1441
+  }
1442
+
1443
+  public void setDisplayInterceptor( BlinkenFrameDisplayInterceptor newDisplayInterceptor )
1444
+  {
1445
+    displayInterceptor = newDisplayInterceptor;
1446
+  }
1447
+
1448
+  public void setEditorListener( BlinkenFrameEditorListener newDisplayListener )
1449
+  {
1450
+    editorListener = newDisplayListener;
1451
+
1452
+    if( editorListener != null )
1453
+      editorListener.blinkenFrameEditorInfo( "-" );
1454
+  }
1455
+}
... ...
@@ -0,0 +1,17 @@
1
+/* BlinkenLightsInteractiveMovieProgram
2
+ * version 0.2 date 2004-11-10
3
+ * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ * powered by eventphone.de
7
+ */
8
+
9
+import java.awt.*;
10
+
11
+public interface BlinkenFrameEditorListener
12
+{
13
+  public void blinkenFrameEditorInfo( String info );
14
+  public void blinkenFrameEditorColorPicked( Color color );
15
+  public void blinkenFrameEditorFrameChanged( );
16
+  public void blinkenFrameEditorCanUndoRedo( boolean canUndo, boolean canRedo );
17
+}
... ...
@@ -0,0 +1,1181 @@
1
+/* BlinkenLightsInteractiveMovieProgram
2
+ * version 0.2 date 2004-11-10
3
+ * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ * powered by eventphone.de
7
+ */
8
+
9
+import java.util.*;
10
+import java.util.regex.*;
11
+import java.io.*;
12
+
13
+public class BlinkenMovie
14
+{
15
+
16
+  private int height;
17
+  private int width;
18
+  private int channels;
19
+  private int maxval;
20
+  private int infoCnt;
21
+  private String[][] infos;
22
+  private int frameCnt;
23
+  private BlinkenFrame[] frames;
24
+
25
+  BlinkenMovie( int height, int width, int channels, int maxval )
26
+  {
27
+    if( height < 1 ) height = 1;
28
+    if( height > 1024 ) height = 1024;
29
+    if( width < 1 ) width = 1;
30
+    if( width > 1024 ) width = 1024;
31
+    if( channels < 1 ) channels = 1;
32
+    if( channels > 16 ) channels = 16;
33
+    if( maxval < 1 ) maxval = 1;
34
+    if( maxval > 255 ) maxval = 255;
35
+
36
+    this.height = height;
37
+    this.width = width;
38
+    this.channels = channels;
39
+    this.maxval = maxval;
40
+    infoCnt = 0;
41
+    infos = new String[0][2];
42
+    frameCnt = 0;
43
+    frames = new BlinkenFrame[0];
44
+  }
45
+
46
+  BlinkenMovie( BlinkenMovie movie )
47
+  {
48
+    int i;
49
+
50
+    height = movie.height;
51
+    width = movie.width;
52
+    channels = movie.channels;
53
+    maxval = movie.maxval;
54
+    infoCnt = 0;
55
+    infos = new String[0][2];
56
+    frameCnt = 0;
57
+    frames = new BlinkenFrame[0];
58
+
59
+    for( i = 0; i < movie.infoCnt; i++ )
60
+      appendInfo( new String( movie.infos[i][0] ), new String( movie.infos[i][1] ) );
61
+
62
+    for( i = 0; i < movie.frameCnt; i++ )
63
+      appendFrame( new BlinkenFrame( movie.frames[i] ) );
64
+  }
65
+
66
+  public int getHeight( )
67
+  {
68
+    return height;
69
+  }
70
+
71
+  public int getWidth( )
72
+  {
73
+    return width;
74
+  }
75
+
76
+  public int getChannels( )
77
+  {
78
+    return channels;
79
+  }
80
+
81
+  public int getMaxval( )
82
+  {
83
+    return maxval;
84
+  }
85
+
86
+  public int getDuration( )
87
+  {
88
+    int duration, i;
89
+    duration = 0;
90
+    for( i = 0; i < frameCnt; i++ )
91
+      duration += frames[i].getDuration( );
92
+    return duration;
93
+  }
94
+
95
+  public int getInfoCnt( )
96
+  {
97
+    return infoCnt;
98
+  }
99
+
100
+  public String getInfoType( int infoNo )
101
+  {
102
+    if( infoCnt < 1 ) return "";
103
+    if( infoNo < 0 ) infoNo = 0;
104
+    if( infoNo >= infoCnt ) infoNo = infoCnt - 1;
105
+    return infos[infoNo][0];
106
+  }
107
+
108
+  public String getInfoData( int infoNo )
109
+  {
110
+    if( infoCnt < 1 ) return "";
111
+    if( infoNo < 0 ) infoNo = 0;
112
+    if( infoNo >= infoCnt ) infoNo = infoCnt - 1;
113
+    return infos[infoNo][1];
114
+  }
115
+
116
+  public void setInfo( int infoNo, String infoType, String infoData )
117
+  {
118
+    if( infoNo < 0 || infoNo >= infoCnt )
119
+      return;
120
+    infos[infoNo][0] = infoType;
121
+    infos[infoNo][1] = infoData;
122
+  }
123
+
124
+  public void insertInfo( int infoNo, String infoType, String infoData )
125
+  {
126
+    String[][] infos;
127
+    int i;
128
+
129
+    if( infoNo < 0 || infoNo > infoCnt )
130
+      return;
131
+
132
+    infos = new String[infoCnt+1][2];
133
+
134
+    for( i = 0; i < infoNo; i++ )
135
+    {
136
+      infos[i][0] = this.infos[i][0];
137
+      infos[i][1] = this.infos[i][1];
138
+    }
139
+
140
+    infos[infoNo][0] = infoType;
141
+    infos[infoNo][1] = infoData;
142
+
143
+    for( i = infoNo; i < infoCnt; i++ )
144
+    {
145
+      infos[i+1][0] = this.infos[i][0];
146
+      infos[i+1][1] = this.infos[i][1];
147
+    }
148
+
149
+    this.infos = infos;
150
+    infoCnt++;
151
+  }
152
+  
153
+  public void appendInfo( String infoType, String infoData )
154
+  {
155
+    insertInfo( infoCnt, infoType, infoData );
156
+  }
157
+
158
+  public void deleteInfo( int infoNo )
159
+  {
160
+    String[][] infos;
161
+    int i;
162
+
163
+    if( infoNo < 0 || infoNo >= infoCnt )
164
+      return;
165
+
166
+    infos = new String[infoCnt-1][2];
167
+
168
+    for( i = 0; i < infoNo; i++ )
169
+    {
170
+      infos[i][0] = this.infos[i][0];
171
+      infos[i][1] = this.infos[i][1];
172
+    }
173
+
174
+    for( i = infoNo; i < infoCnt-1; i++ )
175
+    {
176
+      infos[i][0] = this.infos[i+1][0];
177
+      infos[i][1] = this.infos[i+1][1];
178
+    }
179
+
180
+    this.infos = infos;
181
+    infoCnt--;
182
+  }
183
+  
184
+  public void deleteInfos( )
185
+  {
186
+    infos = new String[0][2];
187
+    infoCnt = 0;
188
+  }
189
+
190
+  public int getFrameCnt( )
191
+  {
192
+    return frameCnt;
193
+  }
194
+
195
+  public BlinkenFrame getFrame( int frameNo )
196
+  {
197
+    BlinkenFrame frame;
198
+    if( frameCnt < 1 )
199
+    {
200
+      frame = new BlinkenFrame( height, width, channels, maxval, 0 );
201
+      frame.clear( );
202
+      return frame;
203
+    }
204
+    if( frameNo < 0 ) frameNo = 0;
205
+    if( frameNo >= frameCnt ) frameNo = frameCnt - 1;
206
+    return frames[frameNo];
207
+  }
208
+
209
+  public void setFrame( int frameNo, BlinkenFrame frame )
210
+  {
211
+    if( frameNo < 0 || frameNo >= frameCnt )
212
+      return;
213
+    frame.resize( height, width, channels, maxval );
214
+    frames[frameNo] = frame;
215
+  }
216
+
217
+  public void insertFrame( int frameNo, BlinkenFrame frame )
218
+  {
219
+    BlinkenFrame[] frames;
220
+    int i;
221
+
222
+    if( frameNo < 0 || frameNo > frameCnt )
223
+      return;
224
+
225
+    frames = new BlinkenFrame[frameCnt+1];
226
+
227
+    for( i = 0; i < frameNo; i++ )
228
+      frames[i] = this.frames[i];
229
+
230
+    frame.resize( height, width, channels, maxval );
231
+    frames[frameNo] = frame;
232
+
233
+    for( i = frameNo; i < frameCnt; i++ )
234
+      frames[i+1] = this.frames[i];
235
+
236
+    this.frames = frames;
237
+    frameCnt++;
238
+  }
239
+  
240
+  public void appendFrame( BlinkenFrame frame )
241
+  {
242
+    insertFrame( frameCnt, frame );
243
+  }
244
+
245
+  public void deleteFrame( int frameNo )
246
+  {
247
+    BlinkenFrame[] frames;
248
+    int i;
249
+
250
+    if( frameNo < 0 || frameNo >= frameCnt )
251
+      return;
252
+
253
+    frames = new BlinkenFrame[frameCnt-1];
254
+
255
+    for( i = 0; i < frameNo; i++ )
256
+      frames[i] = this.frames[i];
257
+
258
+    for( i = frameNo; i < frameCnt-1; i++ )
259
+      frames[i] = this.frames[i+1];
260
+
261
+    this.frames = frames;
262
+    frameCnt--;
263
+  }
264
+  
265
+  public void deleteFrames( )
266
+  {
267
+    frames = new BlinkenFrame[0];
268
+    frameCnt = 0;
269
+  }
270
+
271
+  public void resize( int height, int width, int channels, int maxval )
272
+  {
273
+    int i;
274
+
275
+    if( height < 1 ) height = 1;
276
+    if( height > 1024 ) height = 1024;
277
+    if( width < 1 ) width = 1;
278
+    if( width > 1024 ) width = 1024;
279
+    if( channels < 1 ) channels = 1;
280
+    if( channels > 16 ) channels = 16;
281
+    if( maxval < 1 ) maxval = 1;
282
+    if( maxval > 255 ) maxval = 255;
283
+
284
+    this.height = height;
285
+    this.width = width;
286
+    this.channels = channels;
287
+    this.maxval = maxval;
288
+
289
+    for( i = 0; i < frameCnt; i++ )
290
+      frames[i].resize( height, width, channels, maxval );
291
+  }
292
+
293
+  public String toString( )
294
+  {
295
+    String str = "BlinkenMovie " + width + "x" + height + "-" + channels + "/" + (maxval + 1) + "\n";
296
+    int i;
297
+    for( i = 0; i < infoCnt; i++ )
298
+      str += infos[i][0] + " = " + infos[i][1] + "\n";
299
+    for( i = 0; i < frameCnt; i++ )
300
+      str += "frame " + i + "\n" + frames[i].toString( );
301
+    return str;
302
+  }
303
+
304
+  public boolean loadBlm( String filename )
305
+  {
306
+    Pattern header, infoLine, startOfFrame, dataLine;
307
+    BufferedReader file;
308
+    String line, pixel;
309
+    Matcher matcher;
310
+    int width, height, duration, y, x, val;
311
+    BlinkenFrame frame;
312
+
313
+    //initialize needed regexp patterns
314
+    header = Pattern.compile( "^ *# BlinkenLights Movie ([0-9]+)x([0-9]+)" );
315
+    infoLine = Pattern.compile( "^ *# ?([A-Za-z0-9]+)(?: *= *|: *)(.*)" );
316
+    startOfFrame = Pattern.compile( "^ *@([0-9]+)" );
317
+    dataLine = Pattern.compile( " *([01])" );
318
+
319
+    //delete all frames
320
+    deleteInfos( );
321
+    deleteFrames( );
322
+    resize( 0, 0, 0, 0 );
323
+
324
+    //try to read file
325
+    try
326
+    {
327
+      //open file
328
+      file = new BufferedReader( new FileReader( filename ) );
329
+
330
+      //read magic and size
331
+      if( (line = file.readLine( )) != null )
332
+      {
333
+        if( (matcher = header.matcher( line )).find( ) )
334
+        {
335
+          width = Integer.parseInt( matcher.group( 1 ) );
336
+          height = Integer.parseInt( matcher.group( 2 ) );
337
+          resize( height, width, 1, 1 );
338
+
339
+          //create unused dummy frame for beginning
340
+          frame = new BlinkenFrame( height, width, 1, 1, 0 );
341
+          y = 0;
342
+
343
+          //read frames
344
+          while( (line = file.readLine( )) != null )
345
+          {
346
+
347
+            //info line
348
+            if( (matcher = infoLine.matcher( line )).find( ) )
349
+            {
350
+              appendInfo( matcher.group( 1 ), matcher.group( 2 ) );
351
+            }
352
+
353
+            //start of frame
354
+            else if( (matcher = startOfFrame.matcher( line )).find( ) )
355
+            {
356
+              duration = Integer.parseInt( matcher.group( 1 ) );
357
+              //create new frame and append it to movie
358
+              frame = new BlinkenFrame( this.height, this.width, 1, 1, duration );
359
+              frame.clear( );
360
+              appendFrame( frame );
361
+              y = 0;
362
+            }
363
+
364
+            //data line
365
+            else if( (matcher = dataLine.matcher( line )).find( ) )
366
+            {
367
+              x = 0;
368
+              while( true )
369
+              {
370
+                pixel = matcher.group( 1 );
371
+                val = Integer.parseInt( pixel );
372
+                frame.setPixel( y, x, 0, (byte)val ); //set pixel
373
+                if( ! matcher.find( ) ) //get next pixel
374
+                  break;
375
+                x++; //next pixel
376
+              }
377
+              y++; //next row
378
+            }
379
+
380
+          } //while( (line = ...
381
+        } //if( matcher = header..matcher( ...
382
+      } //if( (line = ...
383
+
384
+      //close file
385
+      file.close( );
386
+
387
+      //success
388
+      return true;
389
+    }
390
+    catch( IOException e ) { }
391
+
392
+    //some error
393
+    return false;
394
+  }
395
+
396
+  public boolean loadBmm( String filename )
397
+  {
398
+    Pattern header, infoLine, startOfFrame, dataLine;
399
+    BufferedReader file;
400
+    String line, pixel;
401
+    Matcher matcher;
402
+    int width, height, duration, y, x, val;
403
+    BlinkenFrame frame;
404
+
405
+    //initialize needed regexp patterns
406
+    header = Pattern.compile( "^ *# BlinkenMini Movie ([0-9]+)x([0-9]+)" );
407
+    infoLine = Pattern.compile( "^ *# ?([A-Za-z0-9]+)(?: *= *|: *)(.*)" );
408
+    startOfFrame = Pattern.compile( "^ *@([0-9]+)" );
409
+    dataLine = Pattern.compile( " *(0x[0-9A-Fa-f]+|[0-9]+)" );
410
+
411
+    //delete all frames
412
+    deleteInfos( );
413
+    deleteFrames( );
414
+    resize( 0, 0, 0, 0 );
415
+
416
+    //try to read file
417
+    try
418
+    {
419
+      //open file
420
+      file = new BufferedReader( new FileReader( filename ) );
421
+
422
+      //read magic and size
423
+      if( (line = file.readLine( )) != null )
424
+      {
425
+        if( (matcher = header.matcher( line )).find( ) )
426
+        {
427
+          width = Integer.parseInt( matcher.group( 1 ) );
428
+          height = Integer.parseInt( matcher.group( 2 ) );
429
+          resize( height, width, 1, 255 );
430
+
431
+          //create unused dummy frame for beginning
432
+          frame = new BlinkenFrame( height, width, 1, 255, 0 );
433
+          y = 0;
434
+
435
+          //read frames
436
+          while( (line = file.readLine( )) != null )
437
+          {
438
+
439
+            //info line
440
+            if( (matcher = infoLine.matcher( line )).find( ) )
441
+            {
442
+              appendInfo( matcher.group( 1 ), matcher.group( 2 ) );
443
+            }
444
+
445
+            //start of frame
446
+            else if( (matcher = startOfFrame.matcher( line )).find( ) )
447
+            {
448
+              duration = Integer.parseInt( matcher.group( 1 ) );
449
+              //create new frame and append it to movie
450
+              frame = new BlinkenFrame( this.height, this.width, 1, 255, duration );
451
+              frame.clear( );
452
+              appendFrame( frame );
453
+              y = 0;
454
+            }
455
+
456
+            //data line
457
+            else if( (matcher = dataLine.matcher( line )).find( ) )
458
+            {
459
+              x = 0;
460
+              while( true )
461
+              {
462
+                pixel = matcher.group( 1 );
463
+                if( pixel.length( ) >= 2 && pixel.substring( 0, 2 ).equals( "0x" ) )
464
+                  val = Integer.parseInt( pixel.substring( 2 ), 0x10 );
465
+                else
466
+                  val = Integer.parseInt( pixel );
467
+                frame.setPixel( y, x, 0, (byte)val ); //set pixel
468
+                if( ! matcher.find( ) ) //get next pixel
469
+                  break;
470
+                x++; //next pixel
471
+              }
472
+              y++; //next row
473
+            }
474
+
475
+          } //while( (line = ...
476
+        } //if( matcher = header..matcher( ...
477
+      } //if( (line = ...
478
+
479
+      //close file
480
+      file.close( );
481
+
482
+      //success
483
+      return true;
484
+    }
485
+    catch( IOException e ) { }
486
+
487
+    //some error
488
+    return false;
489
+  }
490
+
491
+  public boolean loadBml( String filename )
492
+  {
493
+    Pattern blmTag, blmHeight, blmWidth, blmChannels, blmBits;
494
+    Pattern infoTitle, infoDescription, infoGeneric, infoCreator, infoAuthor, infoEmail, infoUrl;
495
+    Pattern frameTag, frameDuration, rowTag, tag;
496
+    BufferedReader file;
497
+    String line, data, row;
498
+    boolean blmTagFound;
499
+    Matcher matcher, submatcher;
500
+    int height, width, channels, bits, maxval, chrs, duration, y, x, c, len, i, val;
501
+    BlinkenFrame frame;
502
+
503
+    //initialize needed regexp patterns
504
+    blmTag = Pattern.compile( "^[^<]*<blm([^>]*)>" );
505
+    blmHeight = Pattern.compile( "height=\"?([0-9]*)\"?" );
506
+    blmWidth = Pattern.compile( "width=\"?([0-9]*)\"?" );
507
+    blmChannels = Pattern.compile( "channels=\"?([0-9]*)\"?" );
508
+    blmBits = Pattern.compile( "bits=\"?([0-9]*)\"?" );
509
+    infoTitle = Pattern.compile( "^[^<]*<title>([^<]*)</title>" );
510
+    infoDescription = Pattern.compile( "[^<]*<description>([^<]*)</description>" );
511
+    infoGeneric = Pattern.compile( "^([A-Za-z0-9]+)(?: *= *|: *)(.*)" );
512
+    infoCreator = Pattern.compile( "^[^<]*<creator>([^<]*)</creator>" );
513
+    infoAuthor = Pattern.compile( "^[^<]*<author>([^<]*)</author>" );
514
+    infoEmail = Pattern.compile( "^[^<]*<email>([^<]*)</email>" );
515
+    infoUrl = Pattern.compile( "^[^<]*<url>([^<]*)</url>" );
516
+    frameTag = Pattern.compile( "^[^<]*<frame([^>]*)>" );
517
+    frameDuration = Pattern.compile( "duration=\"?([0-9]*)\"?" );
518
+    rowTag = Pattern.compile( "^[^<]*<row>([0-9A-Fa-f]*)</row>" );
519
+    tag = Pattern.compile( "^[^<]*<[^>]*>" );
520
+
521
+    //delete all frames
522
+    deleteInfos( );
523
+    deleteFrames( );
524
+    resize( 0, 0, 0, 0 );
525
+
526
+    //try to read file
527
+    try
528
+    {
529
+      //open file
530
+      file = new BufferedReader( new FileReader( filename ) );
531
+
532
+      //create unused dummy frame for beginning
533
+      frame = new BlinkenFrame( 0, 0, 0, 0, 0 );
534
+      y = 0;
535
+      chrs = 1;
536
+
537
+      //read file
538
+      data = "";
539
+      blmTagFound = false;
540
+      while( (line = file.readLine( )) != null )
541
+      {
542
+        data += " " + line; //add new line to data
543
+
544
+        //match tags
545
+        while( true )
546
+        {
547
+
548
+          //no blm tag yet
549
+          if( ! blmTagFound )
550
+          {
551
+
552
+            //blm tag
553
+            if( (matcher = blmTag.matcher( data )).find( ) )
554
+            {
555
+              //remove matched part
556
+              data = data.substring( matcher.end( ) );
557
+              //get attributes
558
+              width = 0;
559
+              height = 0;
560
+              channels = 0;
561
+              bits = 0;
562
+              maxval = 0;
563
+              if( (submatcher = blmHeight.matcher( matcher.group( 1 ) )).find( ) ) //height
564
+                height = Integer.parseInt( submatcher.group( 1 ) );
565
+              if( (submatcher = blmWidth.matcher( matcher.group( 1 ) )).find( ) ) //width
566
+                width = Integer.parseInt( submatcher.group( 1 ) );
567
+              if( (submatcher = blmChannels.matcher( matcher.group( 1 ) )).find( ) ) //channels
568
+                channels = Integer.parseInt( submatcher.group( 1 ) );
569
+              if( (submatcher = blmBits.matcher( matcher.group( 1 ) )).find( ) ) //bits
570
+                maxval = (1 << (bits = Integer.parseInt( submatcher.group( 1 ) ))) - 1;
571
+              //remember that blm tag was found
572
+              blmTagFound = true;
573
+              //set movie size
574
+              resize( height, width, channels, maxval );
575
+              //get number of characters per channel
576
+              chrs = (bits + 3) >> 2;
577
+            }
578
+
579
+            //unknown tag
580
+            else if( (matcher = tag.matcher( data )).find( ) )
581
+              //remove matched part
582
+              data = data.substring( matcher.end( ) );
583
+
584
+            //nothing matches
585
+            else
586
+              //end loop
587
+              break;
588
+
589
+          } //if( ! blmTagFound )
590
+
591
+          //blm tag was already found
592
+          else
593
+          {
594
+
595
+            //title tag
596
+            if( (matcher = infoTitle.matcher( data )).find( ) )
597
+            {
598
+              //remove matched part
599
+              data = data.substring( matcher.end( ) );
600
+              //add info to movie
601
+              appendInfo( "title", matcher.group( 1 ) );
602
+            }
603
+
604
+            //description tag
605
+            else if( (matcher = infoDescription.matcher( data )).find( ) )
606
+            {
607
+              //remove matched part
608
+              data = data.substring( matcher.end( ) );
609
+              //check if generic info
610
+              if( (submatcher = infoGeneric.matcher( matcher.group( 1 ) )).find( ) )
611
+                //add info to movie
612
+                appendInfo( submatcher.group( 1 ), submatcher.group( 2 ) );
613
+              else
614
+                //add info to movie
615
+                appendInfo( "description", matcher.group( 1 ) );
616
+            }
617
+
618
+            //creator tag
619
+            else if( (matcher = infoCreator.matcher( data )).find( ) )
620
+            {
621
+              //remove matched part
622
+              data = data.substring( matcher.end( ) );
623
+              //add info to movie
624
+              appendInfo( "creator", matcher.group( 1 ) );
625
+            }
626
+
627
+            //author tag
628
+            else if( (matcher = infoAuthor.matcher( data )).find( ) )
629
+            {
630
+              //remove matched part
631
+              data = data.substring( matcher.end( ) );
632
+              //add info to movie
633
+              appendInfo( "author", matcher.group( 1 ) );
634
+            }
635
+
636
+            //email tag
637
+            else if( (matcher = infoEmail.matcher( data )).find( ) )
638
+            {
639
+              //remove matched part
640
+              data = data.substring( matcher.end( ) );
641
+              //add info to movie
642
+              appendInfo( "email", matcher.group( 1 ) );
643
+            }
644
+
645
+            //url tag
646
+            else if( (matcher = infoUrl.matcher( data )).find( ) )
647
+            {
648
+              //remove matched part
649
+              data = data.substring( matcher.end( ) );
650
+              //add info to movie
651
+              appendInfo( "url", matcher.group( 1 ) );
652
+            }
653
+
654
+            //frame tag
655
+            else if( (matcher = frameTag.matcher( data )).find( ) )
656
+            {
657
+              //remove matched part
658
+              data = data.substring( matcher.end( ) );
659
+              //get attributes
660
+              duration = 0;
661
+              if( (submatcher = frameDuration.matcher( matcher.group( 1 ) )).find( ) ) //duration
662
+                duration = Integer.parseInt( submatcher.group( 1 ) );
663
+              //create new frame and append it to movie
664
+              frame = new BlinkenFrame( this.height, this.width, this.channels, this.maxval, duration );
665
+              frame.clear( );
666
+              appendFrame( frame );
667
+              y = 0;
668
+            }
669
+
670
+            //row tag
671
+            else if( (matcher = rowTag.matcher( data )).find( ) )
672
+            {
673
+              //remove matched part
674
+              data = data.substring( matcher.end( ) );
675
+              //parse row
676
+              row = matcher.group( 1 );
677
+              len = row.length( );
678
+              i = 0;
679
+              for( x = 0; x < this.width && i + chrs <= len; x++ )
680
+              {
681
+                for( c = 0; c < this.channels && i + chrs <= len; c++, i += chrs )
682
+                {
683
+                  val = Integer.parseInt( row.substring( i, i + chrs ), 0x10 );
684
+                  frame.setPixel( y, x, c, (byte)val ); //set pixel
685
+                }
686
+              }
687
+              y++; //next row
688
+            }
689
+
690
+            //unknown tag
691
+            else if( (matcher = tag.matcher( data )).find( ) )
692
+              //remove matched part
693
+              data = data.substring( matcher.end( ) );
694
+
695
+            //nothing matches
696
+            else
697
+              //end loop
698
+              break;
699
+
700
+          } //if( ! blmTagFound ) ... else
701
+
702
+        } //while( true )
703
+
704
+      } //while( (line = ...
705
+
706
+      //close file
707
+      file.close( );
708
+
709
+      //success
710
+      return true;
711
+    }
712
+    catch( IOException e ) { }
713
+
714
+    //some error
715
+    return false;
716
+  }
717
+
718
+  public boolean loadBbm( String filename )
719
+  {
720
+    RandomAccessFile file;
721
+    byte[] header, subHeader, infoHeader, frameStartMarker, frameData;
722
+    int headerMagic, headerHeight, headerWidth, headerChannels, headerMaxval;
723
+    int headerFrameCnt, headerDuration, headerFramePtr;
724
+    int subHeaderMagic, subHeaderSize;
725
+    int frameStartMarkerMagic;
726
+    StringTokenizer tokenizer;
727
+    String infoType, infoData;
728
+    int fileLength, frameLength;
729
+    int duration, i, y, x, c;
730
+    BlinkenFrame frame;
731
+
732
+    //delete all frames
733
+    deleteInfos( );
734
+    deleteFrames( );
735
+    resize( 0, 0, 0, 0 );
736
+
737
+    //try to read file
738
+    try
739
+    {
740
+      //open file
741
+      file = new RandomAccessFile( filename, "r" );
742
+
743
+      //read header
744
+      header = new byte[24];
745
+      file.readFully( header );
746
+      headerMagic = ((int)header[0] & 0xFF) << 24 | ((int)header[1] & 0xFF) << 16 | ((int)header[2] & 0xFF) << 8 | ((int)header[3] & 0xFF);
747
+      headerHeight = ((int)header[4] & 0xFF) << 8 | ((int)header[5] & 0xFF);
748
+      headerWidth = ((int)header[6] & 0xFF) << 8 | ((int)header[7] & 0xFF);
749
+      headerChannels = ((int)header[8] & 0xFF) << 8 | ((int)header[9] & 0xFF);
750
+      headerMaxval = ((int)header[10] & 0xFF) << 8 | ((int)header[11] & 0xFF);
751
+      headerFrameCnt = ((int)header[12] & 0xFF) << 24 | ((int)header[13] & 0xFF) << 16 | ((int)header[14] & 0xFF) << 8 | ((int)header[15] & 0xFF);
752
+      headerDuration = ((int)header[16] & 0xFF) << 24 | ((int)header[17] & 0xFF) << 16 | ((int)header[18] & 0xFF) << 8 | ((int)header[19] & 0xFF);
753
+      headerFramePtr = ((int)header[20] & 0xFF) << 24 | ((int)header[21] & 0xFF) << 16 | ((int)header[22] & 0xFF) << 8 | ((int)header[23] & 0xFF);
754
+      //check magic
755
+      if( headerMagic == 0x23542666 )
756
+      {
757
+
758
+        //adapt movie format
759
+        resize( headerHeight, headerWidth, headerChannels, headerMaxval );
760
+
761
+        //read subheaders
762
+        subHeader = new byte[6];
763
+        while( file.getFilePointer( ) + 6 <= headerFramePtr )
764
+        {
765
+          file.readFully( subHeader );
766
+          subHeaderMagic = ((int)subHeader[0] & 0xFF) << 24 | ((int)subHeader[1] & 0xFF) << 16 | ((int)subHeader[2] & 0xFF) << 8 | ((int)subHeader[3] & 0xFF);
767
+          subHeaderSize = ((int)subHeader[4] & 0xFF) << 8 | ((int)subHeader[5] & 0xFF);
768
+
769
+          //header fits into gap to frame start
770
+          if( file.getFilePointer( ) + subHeaderSize - 6 <= headerFramePtr )
771
+          {
772
+            //info header
773
+            if( subHeaderMagic == 0x696E666F ) //'i' 'n' 'f' 'o'
774
+            {
775
+              //read rest of info header
776
+              infoHeader = new byte[subHeaderSize - 6];
777
+              file.readFully( infoHeader );
778
+              //parse information
779
+              tokenizer = new StringTokenizer( new String( infoHeader ), "\000" );
780
+              if( tokenizer.countTokens( ) >= 2 )
781
+              {
782
+                infoType = tokenizer.nextToken();
783
+                infoData = tokenizer.nextToken();
784
+                appendInfo( infoType, infoData );
785
+              }
786
+            }
787
+
788
+            //unknown subHeader
789
+            else
790
+              //skip
791
+              file.skipBytes( subHeaderSize - 6 );
792
+
793
+          } //if( file.getFilePointer( ) ...
794
+        } //while( file.getFilePointer( ) ...
795
+
796
+        //seek to start of frames
797
+        file.seek( headerFramePtr );
798
+
799
+        //read frame start marker
800
+        frameStartMarker = new byte[4];
801
+        file.readFully( frameStartMarker );
802
+        frameStartMarkerMagic = ((int)frameStartMarker[0] & 0xFF) << 24 | ((int)frameStartMarker[1] & 0xFF) << 16 | ((int)frameStartMarker[2] & 0xFF) << 8 | ((int)frameStartMarker[3] & 0xFF);
803
+        if( frameStartMarkerMagic == 0x66726D73 ) //'f' 'r' 'm' 's'
804
+        {
805
+
806
+          //read frames
807
+          fileLength = (int)file.length( );
808
+          frameLength = 2 + headerHeight * headerWidth * headerChannels;
809
+          frameData = new byte[frameLength];
810
+          while( file.getFilePointer( ) + frameLength <= fileLength )
811
+          {
812
+            //read frame
813
+            file.readFully( frameData );
814
+            duration = ((int)frameData[0] & 0xFF) << 8 | ((int)frameData[1] & 0xFF);
815
+            //build frame and append it to movie
816
+            frame = new BlinkenFrame( this.height, this.width, this.channels, this.maxval, duration );
817
+            i = 2;
818
+            for( y = 0; y < headerHeight; y++ )
819
+              for( x = 0; x < headerWidth; x++ )
820
+                for( c = 0; c < headerChannels; c++, i++ )
821
+                  frame.setPixel( y, x, c, frameData[i] );
822
+            appendFrame( frame );
823
+
824
+          } //while( file.getFilePointer ...
825
+
826
+        } //if( frameStartMarkerMagic ...
827
+
828
+      } //if( headerMagic ...
829
+
830
+      //close file
831
+      file.close( );
832
+
833
+      //success
834
+      return true;
835
+    }
836
+    catch( IOException e ) { }
837
+
838
+    //some error
839
+    return false;
840
+  }
841
+
842
+  public boolean load( String filename )
843
+  {
844
+    if( filename.endsWith( ".blm" ) )
845
+      return loadBlm( filename );
846
+    if( filename.endsWith( ".bmm" ) )
847
+      return loadBmm( filename );
848
+    if( filename.endsWith( ".bml" ) )
849
+      return loadBml( filename );
850
+    if( filename.endsWith( ".bbm" ) )
851
+      return loadBbm( filename );
852
+    deleteFrames( );
853
+    return false;
854
+  }
855
+
856
+  public boolean saveBlm( String filename )
857
+  {
858
+    BlinkenMovie movie;
859
+    BufferedWriter file;
860
+    int cnt, i, y, x;
861
+    String line;
862
+
863
+    //convert movie to suitable format
864
+    movie = new BlinkenMovie( this );
865
+    movie.resize( movie.height, movie.width, 1, 1 );
866
+
867
+    try
868
+    {
869
+      //open file
870
+      file = new BufferedWriter( new FileWriter( filename ) );
871
+
872
+      //write header line
873
+      file.write( "# BlinkenLights Movie " + movie.width + "x" + movie.height + "\n" );
874
+
875
+      //write information lines
876
+      cnt = movie.getInfoCnt( );
877
+      for( i = 0; i < cnt; i++ )
878
+        file.write( "# " + movie.getInfoType( i ) + " = " + movie.getInfoData( i ) + "\n" );
879
+
880
+      //write frames
881
+      cnt = movie.getFrameCnt( );
882
+      for( i = 0; i < cnt; i++ )
883
+      {
884
+        file.write( "\n@" + movie.frames[i].getDuration( ) + "\n" );
885
+        for( y = 0; y < movie.height; y++ )
886
+        {
887
+          line = "";
888
+          for( x = 0; x < movie.width; x++ )
889
+          {
890
+            if( movie.frames[i].getPixel( y, x, 0 ) != 0 )
891
+              line = line + "1";
892
+            else
893
+              line = line + "0";
894
+          }
895
+          file.write( line + "\n" );
896
+        }
897
+      }
898
+
899
+      //close file
900
+      file.close( );
901
+
902
+      //success
903
+      return true;
904
+    }
905
+    catch( IOException e ) { }
906
+
907
+    //some error
908
+    return false;
909
+  }
910
+
911
+  public boolean saveBmm( String filename )
912
+  {
913
+    BlinkenMovie movie;
914
+    BufferedWriter file;
915
+    int cnt, i, y, x, val;
916
+    String line;
917
+
918
+    //convert movie to suitable format
919
+    movie = new BlinkenMovie( this );
920
+    movie.resize( movie.height, movie.width, 1, 255 );
921
+
922
+    try
923
+    {
924
+      //open file
925
+      file = new BufferedWriter( new FileWriter( filename ) );
926
+
927
+      //write header line
928
+      file.write( "# BlinkenMini Movie " + movie.width + "x" + movie.height + "\n" );
929
+
930
+      //write information lines
931
+      cnt = movie.getInfoCnt( );
932
+      for( i = 0; i < cnt; i++ )
933
+        file.write( "# " + movie.getInfoType( i ) + " = " + movie.getInfoData( i ) + "\n" );
934
+
935
+      //write frames
936
+      cnt = movie.getFrameCnt( );
937
+      for( i = 0; i < cnt; i++ )
938
+      {
939
+        file.write( "\n@" + movie.frames[i].getDuration( ) + "\n" );
940
+        for( y = 0; y < movie.height; y++ )
941
+        {
942
+          line = "";
943
+          for( x = 0; x < movie.width; x++ )
944
+          {
945
+            val = (int)movie.frames[i].getPixel( y, x, 0 ) & 0xFF;
946
+            if( val < 0x10 )
947
+              line = line + " 0x0" + Integer.toHexString( val ).toUpperCase( );
948
+            else
949
+              line = line + " 0x" + Integer.toHexString( val ).toUpperCase( );
950
+          }
951
+          file.write( line.substring( 1 ) + "\n" );
952
+        }
953
+      }
954
+
955
+      //close file
956
+      file.close( );
957
+
958
+      //success
959
+      return true;
960
+    }
961
+    catch( IOException e ) { }
962
+
963
+    //some error
964
+    return false;
965
+  }
966
+
967
+  public boolean saveBml( String filename )
968
+  {
969
+    BlinkenMovie movie;
970
+    BufferedWriter file;
971
+    int bits, cnt, i, y, x, c, val;
972
+    String infoType, infoData, line;
973
+
974
+    //convert movie to suitable format
975
+    movie = new BlinkenMovie( this );
976
+    val = movie.maxval; //get number of bits
977
+    for( bits = 0; val != 0; val >>= 1, bits++ );
978
+    movie.resize( movie.height, movie.width, movie.channels, (1 << bits) - 1 );
979
+
980
+    try
981
+    {
982
+      //open file
983
+      file = new BufferedWriter( new FileWriter( filename ) );
984
+
985
+      //write header line
986
+      file.write( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
987
+
988
+      //write blm start tag
989
+      file.write( "<blm width=\"" + movie.width + "\" height=\"" + movie.height
990
+                  + "\" bits=\"" + bits + "\" channels=\"" + movie.channels + "\">\n" );
991
+
992
+      //write information lines
993
+      file.write( "\t<header>\n" );
994
+      cnt = movie.getInfoCnt( );
995
+      for( i = 0; i < cnt; i++ )
996
+      {
997
+        infoType = movie.getInfoType( i );
998
+        infoData = movie.getInfoData( i );
999
+        if( infoType.equals( "title" ) )
1000
+          file.write( "\t\t<title>" + infoData + "</title>\n" );
1001
+        else if( infoType.equals( "description" ) )
1002
+          file.write( "\t\t<description>" + infoData + "</description>\n" );
1003
+        else if( infoType.equals( "creator" ) )
1004
+          file.write( "\t\t<creator>" + infoData + "</creator>\n" );
1005
+        else if( infoType.equals( "author" ) )
1006
+          file.write( "\t\t<author>" + infoData + "</author>\n" );
1007
+        else if( infoType.equals( "email" ) )
1008
+          file.write( "\t\t<email>" + infoData + "</email>\n" );
1009
+        else if( infoType.equals( "url" ) )
1010
+          file.write( "\t\t<url>" + infoData + "</url>\n" );
1011
+        else
1012
+          file.write( "\t\t<description>" + infoType + ": " + infoData + "</description>\n" );
1013
+      }
1014
+      file.write( "\t</header>\n" );
1015
+
1016
+      //write frames
1017
+      cnt = movie.getFrameCnt( );
1018
+      for( i = 0; i < cnt; i++ )
1019
+      {
1020
+        file.write( "\n\t<frame duration=\"" + movie.frames[i].getDuration( ) + "\">\n" );
1021
+        for( y = 0; y < movie.height; y++ )
1022
+        {
1023
+          line = "";
1024
+          for( x = 0; x < movie.width; x++ )
1025
+          {
1026
+            for( c = 0; c < movie.channels; c++ )
1027
+            {
1028
+              val = (int)movie.frames[i].getPixel( y, x, c ) & 0xFF;
1029
+              if( bits > 4 && val < 0x10 )
1030
+                line = line + "0" + Integer.toHexString( val ).toUpperCase( );
1031
+              else
1032
+                line = line + Integer.toHexString( val ).toUpperCase( );
1033
+            }
1034
+          }
1035
+          file.write( "\t\t<row>" + line + "</row>\n" );
1036
+        }
1037
+        file.write( "\t</frame>\n" );
1038
+      }
1039
+
1040
+      //write blm end tag
1041
+      file.write( "</blm>\n" );
1042
+
1043
+      //close file
1044
+      file.close( );
1045
+
1046
+      //success
1047
+      return true;
1048
+    }
1049
+    catch( IOException e ) { }
1050
+
1051
+    //some error
1052
+    return false;
1053
+  }
1054
+
1055
+  public boolean saveBbm( String filename )
1056
+  {
1057
+    RandomAccessFile file;
1058
+    byte[] header, infoHeader, framePointer, frameStartMarker, frameData;
1059
+    int cnt, duration, i, j, len, y, x, c, val;
1060
+    String infoType, infoData, line;
1061
+
1062
+    try
1063
+    {
1064
+      //open file
1065
+      file = new RandomAccessFile( filename, "rw" );
1066
+
1067
+      //write header
1068
+      header = new byte[24];
1069
+      header[0] = 0x23; //magic
1070
+      header[1] = 0x54;
1071
+      header[2] = 0x26;
1072
+      header[3] = 0x66;
1073
+      header[4] = (byte)(this.height >> 8);
1074
+      header[5] = (byte)this.height;
1075
+      header[6] = (byte)(this.width >> 8);
1076
+      header[7] = (byte)this.width;
1077
+      header[8] = (byte)(this.channels >> 8);
1078
+      header[9] = (byte)this.channels;
1079
+      header[10] = (byte)(this.maxval >> 8);
1080
+      header[11] = (byte)this.maxval;
1081
+      cnt = this.getFrameCnt( );
1082
+      header[12] = (byte)(cnt >> 24);
1083
+      header[13] = (byte)(cnt >> 16);
1084
+      header[14] = (byte)(cnt >> 8);
1085
+      header[15] = (byte)cnt;
1086
+      duration = 0;
1087
+      for( i = 0; i < cnt; i++ )
1088
+        duration += this.frames[i].getDuration( ); 
1089
+      header[16] = (byte)(duration >> 24);
1090
+      header[17] = (byte)(duration >> 16);
1091
+      header[18] = (byte)(duration >> 8);
1092
+      header[19] = (byte)duration;
1093
+      header[20] = 0; //frame pointer is written later
1094
+      header[21] = 0;
1095
+      header[22] = 0;
1096
+      header[23] = 0;
1097
+      file.write( header );
1098
+
1099
+      //write information
1100
+      cnt = this.getInfoCnt( );
1101
+      for( i = 0; i < cnt; i++ )
1102
+      {
1103
+        infoType = this.getInfoType( i );
1104
+        if( infoType.length( ) > 32760 )
1105
+          infoType = infoType.substring( 0, 32760 );
1106
+        infoData = this.getInfoData( i );
1107
+        if( infoData.length( ) > 32760 )
1108
+          infoData = infoData.substring( 0, 32760 );
1109
+        len = 8 + infoType.length( ) + infoData.length( );
1110
+        infoHeader = new byte[6];
1111
+        infoHeader[0] = 0x69; //'i'
1112
+        infoHeader[1] = 0x6E; //'n'
1113
+        infoHeader[2] = 0x66; //'f'
1114
+        infoHeader[3] = 0x6F; //'o'
1115
+        infoHeader[4] = (byte)(len >> 8);
1116
+        infoHeader[5] = (byte)len;
1117
+        file.write( infoHeader );
1118
+        file.write( (infoType + "\000" + infoData + "\000").getBytes( ) );
1119
+      }
1120
+
1121
+      //write frame pointer
1122
+      framePointer = new byte[4];
1123
+      val = (int)file.getFilePointer( );
1124
+      framePointer[0] = (byte)(val >> 24);
1125
+      framePointer[1] = (byte)(val >> 16);
1126
+      framePointer[2] = (byte)(val >> 8);
1127
+      framePointer[3] = (byte)val;
1128
+      file.seek( 20 );
1129
+      file.write( framePointer );
1130
+      file.seek( val );
1131
+
1132
+      //write frame start marker
1133
+      frameStartMarker = new byte[4];
1134
+      frameStartMarker[0] = 0x66; //'f'
1135
+      frameStartMarker[1] = 0x72; //'r'
1136
+      frameStartMarker[2] = 0x6D; //'m'
1137
+      frameStartMarker[3] = 0x73; //'s'
1138
+      file.write( frameStartMarker );
1139
+
1140
+      //write frames
1141
+      len = 2 + this.height * this.width * this.channels;
1142
+      frameData = new byte[len];
1143
+      cnt = this.getFrameCnt( );
1144
+      for( i = 0; i < cnt; i++ )
1145
+      {
1146
+        val = this.frames[i].getDuration( );
1147
+        frameData[0] = (byte)(val >> 8);
1148
+        frameData[1] = (byte)val;
1149
+        for( j = 2, y = 0; y < this.height; y++ )
1150
+          for( x = 0; x < this.width; x++ )
1151
+            for( c = 0; c < this.channels; c++, j++ )
1152
+              frameData[j] = this.frames[i].getPixel( y, x, c );
1153
+        file.write( frameData );
1154
+      }
1155
+
1156
+      //close file
1157
+      file.close( );
1158
+
1159
+      //success
1160
+      return true;
1161
+    }
1162
+    catch( IOException e ) { }
1163
+
1164
+    //some error
1165
+    return false;
1166
+  }
1167
+
1168
+  public boolean save( String filename )
1169
+  {
1170
+    if( filename.endsWith( ".blm" ) )
1171
+      return saveBlm( filename );
1172
+    if( filename.endsWith( ".bmm" ) )
1173
+      return saveBmm( filename );
1174
+    if( filename.endsWith( ".bml" ) )
1175
+      return saveBml( filename );
1176
+    if( filename.endsWith( ".bbm" ) )
1177
+      return saveBbm( filename );
1178
+    return false;
1179
+  }
1180
+
1181
+} //public class BlinkenMovie
... ...
@@ -0,0 +1,52 @@
1
+# BlinkenLightsInteractiveMovieProgram
2
+# version 0.2 date 2004-11-10
3
+# Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info>
4
+# Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+# a blinkenarea.org project
6
+# powered by eventphone.de
7
+
8
+JAVAC=javac
9
+JAR=jar
10
+KEYTOOL=keytool
11
+JARSIGNER=jarsigner
12
+KEYPASS=BlinkenLightsInteractiveMovieProgram
13
+JAVA=java
14
+UPLOAD_CMD=scp -r
15
+UPLOAD_DEST=gate:~/www/html/Blimp/
16
+
17
+CLASS_FILES=BlinkenFileFilter.class BlinkenFrame.class BlinkenFrameDisplay.class \
18
+            BlinkenFrameDisplayListener.class BlinkenFrameDisplayInterceptor.class \
19
+            BlinkenFrameEditor.class BlinkenFrameEditorListener.class \
20
+            BlinkenMovie.class Blimp.class
21
+
22
+IMAGE_FILES=images/ColorPicker.png images/Dot.png images/Line.png \
23
+            images/Rectangle.png images/FilledRectangle.png \
24
+            images/Circle.png images/FilledCircle.png \
25
+            images/Copy.png images/Paste.png \
26
+            images/Invert.png images/MirrorHor.png images/RollLeft.png \
27
+            images/Rotate90.png images/MirrorVer.png images/RollRight.png \
28
+            images/Rotate180.png images/MirrorDiag.png images/RollUp.png \
29
+            images/Rotate270.png images/MirrorDiag2.png images/RollDown.png \
30
+            images/Undo.png images/Redo.png
31
+
32
+.phony: all clean jar run
33
+
34
+all: $(CLASS_FILES)
35
+
36
+%.class: %.java
37
+	$(JAVAC) $<
38
+
39
+clean:
40
+	rm -f $(CLASS_FILES) Blimp.jar
41
+
42
+jar: Blimp.jar
43
+
44
+Blimp.keystore:
45
+	$(KEYTOOL) -genkey -alias Blimp -keystore Blimp.keystore -keypass $(KEYPASS) -storepass $(KEYPASS)
46
+
47
+Blimp.jar: Blimp.mf Blimp.keystore $(CLASS_FILES) $(IMAGE_FILES)
48
+	$(JAR) cmf Blimp.mf Blimp.jar $(CLASS_FILES) $(IMAGE_FILES)
49
+	$(JARSIGNER) -keystore Blimp.keystore -storepass $(KEYPASS) Blimp.jar Blimp
50
+
51
+run: $(CLASS_FILES)
52
+	$(JAVA) Blimp
... ...
@@ -0,0 +1,9 @@
1
+EditMenu
2
+  ScaleMovie
3
+CommandLineInterface
4
+  InitialFormat
5
+  InitialFile
6
+  ConverterFunctionality
7
+FileFormats
8
+  gif
9
+  avi
... ...
@@ -0,0 +1,77 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<blm width="26" height="20" bits="8" channels="3">
3
+	<header>
4
+		<description>type1: info1</description>
5
+		<description>type2: info2</description>
6
+		<description>type2: info2</description>
7
+	</header>
8
+
9
+	<frame duration="1000">
10
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
11
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
12
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
13
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
14
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
15
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
16
+		<row>000000000000000000000000000000600000800000900000900000900000900000900000900000900000900000A00000A00000A00000A00000A00000A00000600000000000000000000000000000</row>
17
+		<row>000000000000000000000000000000A00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000800000000000000000000000000000</row>
18
+		<row>000000000000000000000000000000800000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000800000000000000000000000000000</row>
19
+		<row>000000000000000000000000000000800000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000800000000000000000000000000000</row>
20
+		<row>000000000000000000000000000000700000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000800000000000000000000000000000</row>
21
+		<row>000000000000000000000000000000500000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000800000000000000000000000000000</row>
22
+		<row>000000000000000000000000000000500000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000800000000000000000000000000000</row>
23
+		<row>000000000000000000000000000000400000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000800000000000000000000000000000</row>
24
+		<row>000000000000000000000000000000100000600000600000600000500000500000500000500000500000400000400000400000300000300000300000200000100000000000000000000000000000</row>
25
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
26
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
27
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
28
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
29
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
30
+	</frame>
31
+
32
+	<frame duration="1000">
33
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
34
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
35
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
36
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
37
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
38
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
39
+		<row>00000000000000000000000000000000600000800000B00000B00000B00000B00000B00000B00000B00000B00000A00000A00000A00000A00000A00000A000006000000000000000000000000000</row>
40
+		<row>00000000000000000000000000000000A00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E000008000000000000000000000000000</row>
41
+		<row>00000000000000000000000000000000800000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E000008000000000000000000000000000</row>
42
+		<row>00000000000000000000000000000000800000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E000008000000000000000000000000000</row>
43
+		<row>00000000000000000000000000000000700000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E000008000000000000000000000000000</row>
44
+		<row>00000000000000000000000000000000500000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E000008000000000000000000000000000</row>
45
+		<row>00000000000000000000000000000000500000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E000008000000000000000000000000000</row>
46
+		<row>00000000000000000000000000000000400000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E000008000000000000000000000000000</row>
47
+		<row>000000000000000000000000000000001000006000006000006000005000005000005000005000005000004000004000004000003000003000003000002000001000000000000000000000000000</row>
48
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
49
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
50
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
51
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
52
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
53
+	</frame>
54
+
55
+	<frame duration="500">
56
+		<row>0000FFFF00FF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080</row>
57
+		<row>00FFFFFFFFFF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
58
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
59
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
60
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
61
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
62
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
63
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
64
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
65
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
66
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
67
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
68
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
69
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
70
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
71
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
72
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
73
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
74
+		<row>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</row>
75
+		<row>0000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF</row>
76
+	</frame>
77
+</blm>
... ...
@@ -0,0 +1,70 @@
1
+# BlinkenLights Movie 26x20
2
+# type1 = info1
3
+# type2 = info2
4
+# type2 = info2
5
+
6
+@100
7
+00000000000000000000000000
8
+00000000000000000000000000
9
+00000000000000000000000000
10
+00000000000000000000000000
11
+00000000000000000000000000
12
+00000000000000000000000000
13
+00000011111111111111100000
14
+00000111111111111111110000
15
+00000111111111111111110000
16
+00000111111111111111110000
17
+00000011111111111111110000
18
+00000011111111111111110000
19
+00000011111111111111110000
20
+00000011111111111111110000
21
+00000000000000000000000000
22
+00000000000000000000000000
23
+00000000000000000000000000
24
+00000000000000000000000000
25
+00000000000000000000000000
26
+00000000000000000000000000
27
+
28
+@100
29
+00000000000000000000000000
30
+00000000000000000000000000
31
+00000000000000000000000000
32
+00000000000000000000000000
33
+00000000000000000000000000
34
+00000000000000000000000000
35
+00000011111111111111100000
36
+00000111111111111111110000
37
+00000111111111111111110000
38
+00000111111111111111110000
39
+00000011111111111111110000
40
+00000011111111111111110000
41
+00000011111111111111110000
42
+00000011111111111111110000
43
+00000000000000000000000000
44
+00000000000000000000000000
45
+00000000000000000000000000
46
+00000000000000000000000000
47
+00000000000000000000000000
48
+00000000000000000000000000
49
+
50
+@500
51
+10000000000000000000000001
52
+00000000000000000000000000
53
+00000000000000000000000000
54
+00000000000000000000000000
55
+00000000000000000000000000
56
+00000000000000000000000000
57
+00000000000000000000000000
58
+00000000000000000000000000
59
+00000000000000000000000000
60
+00000000000000000000000000
61
+00000000000000000000000000
62
+00000000000000000000000000
63
+00000000000000000000000000
64
+00000000000000000000000000
65
+00000000000000000000000000
66
+00000000000000000000000000
67
+00000000000000000000000000
68
+00000000000000000000000000
69
+00000000000000000000000000
70
+10000000000000000000000001
... ...
@@ -0,0 +1,77 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<blm width="26" height="20" bits="8" channels="1">
3
+	<header>
4
+		<description>type1: info1</description>
5
+		<description>type2: info2</description>
6
+		<description>type2: info2</description>
7
+	</header>
8
+
9
+	<frame duration="100">
10
+		<row>0000000000000000000000000000000000000000000000000000</row>
11
+		<row>0000000000000000000000000000000000000000000000000000</row>
12
+		<row>0000000000000000000000000000000000000000000000000000</row>
13
+		<row>0000000000000000000000000000000000000000000000000000</row>
14
+		<row>0000000000000000000000000000000000000000000000000000</row>
15
+		<row>0000000000000000000000000000000000000000000000000000</row>
16
+		<row>000000000060809090909090909090A0A0A0A0A0A06000000000</row>
17
+		<row>0000000000A0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
18
+		<row>000000000080E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
19
+		<row>000000000080E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
20
+		<row>000000000070E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
21
+		<row>000000000050E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
22
+		<row>000000000050E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
23
+		<row>000000000040E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
24
+		<row>0000000000106060605050505050404040303030201000000000</row>
25
+		<row>0000000000000000000000000000000000000000000000000000</row>
26
+		<row>0000000000000000000000000000000000000000000000000000</row>
27
+		<row>0000000000000000000000000000000000000000000000000000</row>
28
+		<row>0000000000000000000000000000000000000000000000000000</row>
29
+		<row>0000000000000000000000000000000000000000000000000000</row>
30
+	</frame>
31
+
32
+	<frame duration="100">
33
+		<row>0000000000000000000000000000000000000000000000000000</row>
34
+		<row>0000000000000000000000000000000000000000000000000000</row>
35
+		<row>0000000000000000000000000000000000000000000000000000</row>
36
+		<row>0000000000000000000000000000000000000000000000000000</row>
37
+		<row>0000000000000000000000000000000000000000000000000000</row>
38
+		<row>0000000000000000000000000000000000000000000000000000</row>
39
+		<row>00000000006080B0B0B0B0B0B0B0B0A0A0A0A0A0A06000000000</row>
40
+		<row>0000000000A0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
41
+		<row>000000000080E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
42
+		<row>000000000080E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
43
+		<row>000000000070E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
44
+		<row>000000000050E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
45
+		<row>000000000050E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
46
+		<row>000000000040E0E0E0E0E0E0E0E0E0E0E0E0E0E0E08000000000</row>
47
+		<row>0000000000106060605050505050404040303030201000000000</row>
48
+		<row>0000000000000000000000000000000000000000000000000000</row>
49
+		<row>0000000000000000000000000000000000000000000000000000</row>
50
+		<row>0000000000000000000000000000000000000000000000000000</row>
51
+		<row>0000000000000000000000000000000000000000000000000000</row>
52
+		<row>0000000000000000000000000000000000000000000000000000</row>
53
+	</frame>
54
+
55
+	<frame duration="500">
56
+		<row>FF00000000000000000000000000000000000000000000000080</row>
57
+		<row>0000000000000000000000000000000000000000000000000000</row>
58
+		<row>0000000000000000000000000000000000000000000000000000</row>
59
+		<row>0000000000000000000000000000000000000000000000000000</row>
60
+		<row>0000000000000000000000000000000000000000000000000000</row>
61
+		<row>0000000000000000000000000000000000000000000000000000</row>
62
+		<row>0000000000000000000000000000000000000000000000000000</row>
63
+		<row>0000000000000000000000000000000000000000000000000000</row>
64
+		<row>0000000000000000000000000000000000000000000000000000</row>
65
+		<row>0000000000000000000000000000000000000000000000000000</row>
66
+		<row>0000000000000000000000000000000000000000000000000000</row>
67
+		<row>0000000000000000000000000000000000000000000000000000</row>
68
+		<row>0000000000000000000000000000000000000000000000000000</row>
69
+		<row>0000000000000000000000000000000000000000000000000000</row>
70
+		<row>0000000000000000000000000000000000000000000000000000</row>
71
+		<row>0000000000000000000000000000000000000000000000000000</row>
72
+		<row>0000000000000000000000000000000000000000000000000000</row>
73
+		<row>0000000000000000000000000000000000000000000000000000</row>
74
+		<row>0000000000000000000000000000000000000000000000000000</row>
75
+		<row>80000000000000000000000000000000000000000000000000FF</row>
76
+	</frame>
77
+</blm>
... ...
@@ -0,0 +1,71 @@
1
+# BlinkenMini Movie 26x20
2
+# type1 = info1
3
+# type2 = info2
4
+# type2 = info2
5
+
6
+@100
7
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
8
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
9
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
10
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
11
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
12
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
13
+0x00 0x00 0x00 0x00 0x00 0x60 0x80 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xA0 0xA0 0xA0 0xA0 0xA0 0xA0 0x60 0x00 0x00 0x00 0x00 
14
+0x00 0x00 0x00 0x00 0x00 0xA0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0x80 0x00 0x00 0x00 0x00 
15
+0x00 0x00 0x00 0x00 0x00 0x80 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0x80 0x00 0x00 0x00 0x00 
16
+0x00 0x00 0x00 0x00 0x00 0x80 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0x80 0x00 0x00 0x00 0x00 
17
+0x00 0x00 0x00 0x00 0x00 0x70 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0x80 0x00 0x00 0x00 0x00 
18
+0x00 0x00 0x00 0x00 0x00 0x50 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0x80 0x00 0x00 0x00 0x00 
19
+0x00 0x00 0x00 0x00 0x00 0x50 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0x80 0x00 0x00 0x00 0x00 
20
+0x00 0x00 0x00 0x00 0x00 0x40 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0xE0 0x80 0x00 0x00 0x00 0x00 
21
+0x00 0x00 0x00 0x00 0x00 0x10 0x60 0x60 0x60 0x50 0x50 0x50 0x50 0x50 0x40 0x40 0x40 0x30 0x30 0x30 0x20 0x10 0x00 0x00 0x00 0x00 
22
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
23
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
24
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
25
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
26
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
27
+
28
+@100
29
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
30
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
31
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
32
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
33
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
34
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
35
+0 0 0 0 0 96 128 176 176 176 176 176 176 176 176 160 160 160 160 160 160 96 0 0 0 0 
36
+0 0 0 0 0 160 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 128 0 0 0 0 
37
+0 0 0 0 0 128 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 128 0 0 0 0 
38
+0 0 0 0 0 128 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 128 0 0 0 0 
39
+0 0 0 0 0 112 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 128 0 0 0 0 
40
+0 0 0 0 0 80 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 128 0 0 0 0 
41
+0 0 0 0 0 80 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 128 0 0 0 0 
42
+0 0 0 0 0 64 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 128 0 0 0 0 
43
+0 0 0 0 0 16 96 96 96 80 80 80 80 80 64 64 64 48 48 48 32 16 0 0 0 0 
44
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
45
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
46
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
47
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
48
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
49
+
50
+@500
51
+0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80
52
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
53
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
54
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
55
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
56
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
57
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
58
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
59
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
60
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
61
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
62
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
63
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
64
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
65
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
66
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
67
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
68
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
69
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
70
+0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF
71
+
0 72