Removed Action portions. Added logging framework. GUI architecture changed (move to dependancy injection)

This commit is contained in:
Jonathan Bernard 2009-12-23 18:29:13 -06:00
parent 5b19542355
commit cee6de1147
15 changed files with 1534 additions and 53 deletions

View File

@ -1,3 +0,0 @@
package com.jdbernard.timestamper
def emptyAction = action(name: 'Empty Action')

View File

@ -1,8 +0,0 @@
package com.jdbernard.timestamper
import java.awt.event.KeyEvent
gracefulExitAction = action (
name: 'Graceful Exit',
closure: controller.&exitGracefully
)

View File

@ -9,6 +9,13 @@ application {
//frameClass = 'javax.swing.JFrame' //frameClass = 'javax.swing.JFrame'
} }
mvcGroups { mvcGroups {
// MVC Group for "com.jdbernard.timestamper.TimeStamperMain"
'TimeStamperMain' {
model = 'com.jdbernard.timestamper.TimeStamperMainModel'
view = 'com.jdbernard.timestamper.TimeStamperMainView'
controller = 'com.jdbernard.timestamper.TimeStamperMainController'
}
// MVC Group for "com.jdbernard.timestamper.PunchcardDialog" // MVC Group for "com.jdbernard.timestamper.PunchcardDialog"
'PunchcardDialog' { 'PunchcardDialog' {
model = 'com.jdbernard.timestamper.PunchcardDialogModel' model = 'com.jdbernard.timestamper.PunchcardDialogModel'
@ -18,18 +25,9 @@ mvcGroups {
// MVC Group for "com.jdbernard.timestamper.NotesDialog" // MVC Group for "com.jdbernard.timestamper.NotesDialog"
'NotesDialog' { 'NotesDialog' {
actions = 'com.jdbernard.timestamper.NotesDialogActions'
model = 'com.jdbernard.timestamper.NotesDialogModel' model = 'com.jdbernard.timestamper.NotesDialogModel'
view = 'com.jdbernard.timestamper.NotesDialogView' view = 'com.jdbernard.timestamper.NotesDialogView'
controller = 'com.jdbernard.timestamper.NotesDialogController' controller = 'com.jdbernard.timestamper.NotesDialogController'
} }
// MVC Group for "com.jdbernard.timestamper.TimeStamperMain"
'TimeStamperMain' {
actions = 'com.jdbernard.timestamper.TimeStamperMainActions'
model = 'com.jdbernard.timestamper.TimeStamperMainModel'
view = 'com.jdbernard.timestamper.TimeStamperMainView'
controller = 'com.jdbernard.timestamper.TimeStamperMainController'
}
} }

View File

@ -0,0 +1,5 @@
import org.apache.log4j.Logger
onNewInstance = { klass, type, instance ->
instance.metaClass.logger = Logger.getLogger(klass.name)
}

View File

@ -1,11 +1,18 @@
package com.jdbernard.timestamper package com.jdbernard.timestamper
import java.awt.Point
class NotesDialogController { class NotesDialogController {
// these will be injected by Griffon // these will be injected by Griffon
def model def model
def view def view
void mvcGroupInit(Map args) { void mvcGroupInit(Map args) {
def loc = model.mainMVC.view.frame.location
Point p = new Point(0, (int) model.mainMVC.view.frame.bounds.height + 50)
p.translate((int) loc.x, (int) loc.y)
view.dialog.location = p
} }
} }

View File

@ -9,10 +9,17 @@ class TimeStamperMainController {
def view def view
void mvcGroupInit(Map args) { void mvcGroupInit(Map args) {
def notes = buildMVCGroup('NotesDialog')
def punchcard = buildMVCGroup('PunchcardDialog') if (logger.isTraceEnabled())
view.notesDialog = notes.view.notesDialog logger.trace("Initializeing TimeStamperMain MVC...")
view.punchcardDialog = punchcard.view.punchcardDialog
def thisMVC = ['model': model, 'view': view, 'controller': this]
model.notesDialogMVC = buildMVCGroup('NotesDialog', 'notesDialog',
'mainMVC': thisMVC)
model.punchcardDialogMVC = buildMVCGroup('PunchcardDialog',
'punchcardDialog', 'mainMVC': thisMVC)
// load application properties // load application properties
Properties prop = new Properties() Properties prop = new Properties()
@ -21,7 +28,9 @@ class TimeStamperMainController {
if (!model.configFile.exists()) model.configFile.createNewFile() if (!model.configFile.exists()) model.configFile.createNewFile()
try { model.configFile.withInputStream { prop.load(it) } } try { model.configFile.withInputStream { prop.load(it) } }
catch (IOException ioe) { /* TODO */ } catch (IOException ioe) {
logger.error('Unable to load configuration', ioe)
}
model.config = prop model.config = prop
@ -42,16 +51,27 @@ class TimeStamperMainController {
// load the last marker // load the last marker
model.currentMarker = model.timeline.getLastMarker(new Date()) model.currentMarker = model.timeline.getLastMarker(new Date())
if (logger.isTraceEnabled())
logger.trace("TimeStamperMain MVC initialization complete.")
} }
def exitGracefully = { evt = null -> def exitGracefully = { evt = null ->
if (logger.isTraceEnabled()) logger.trace("Exiting gracefully.")
// save config // save config
try { model.configFile.withOutputStream { out -> try { model.configFile.withOutputStream { out ->
model.config.store(out, null) } } model.config.store(out, null) } }
catch (IOException ioe) {} catch (IOException ioe) {
logger.error("Unable to save the configuration file", ioe)
}
// save timeline and properties // save timeline and properties
model.timelineProperties.save() model.timelineProperties.save()
if (logger.isTraceEnabled()) logger.trace("Completed graceful shutdown.")
app.shutdown() app.shutdown()
} }

View File

@ -21,4 +21,4 @@ import groovy.swing.SwingBuilder
import griffon.util.GriffonPlatformHelper import griffon.util.GriffonPlatformHelper
GriffonPlatformHelper.tweakForNativePlatform(app) GriffonPlatformHelper.tweakForNativePlatform(app)
SwingBuilder.lookAndFeel('mac', 'nimbus', 'gtk', ['metal', [boldFonts: false]]) SwingBuilder.lookAndFeel('mac', 'nimbus', 'gtk', ['metal', [boldFonts: false]])

View File

@ -4,4 +4,6 @@ import groovy.beans.Bindable
class NotesDialogModel { class NotesDialogModel {
// needs to be injected by buildMVCGroup call
def mainMVC
} }

View File

@ -3,5 +3,7 @@ package com.jdbernard.timestamper
import groovy.beans.Bindable import groovy.beans.Bindable
class PunchcardDialogModel { class PunchcardDialogModel {
// @Bindable String propName
} // needs to be injected by buildMVCGroup() call
def mainMVC
}

View File

@ -2,6 +2,7 @@ package com.jdbernard.timestamper
import groovy.beans.Bindable import groovy.beans.Bindable
import java.awt.Point import java.awt.Point
import java.awt.Rectangle
import java.util.Properties import java.util.Properties
import com.jdbernard.timestamper.core.Timeline import com.jdbernard.timestamper.core.Timeline
import com.jdbernard.timestamper.core.TimelineMarker import com.jdbernard.timestamper.core.TimelineMarker
@ -11,8 +12,12 @@ class TimeStamperMainModel {
@Bindable TimelineMarker currentMarker @Bindable TimelineMarker currentMarker
@Bindable Timeline timeline @Bindable Timeline timeline
@Bindable TimelineProperties timelineProperties @Bindable TimelineProperties timelineProperties
@Bindable Properties config Properties config
File configFile File configFile
def notesDialogMVC
def punchcardDialogMVC
@Bindable Point absoluteLocation @Bindable Point absoluteLocation
@Bindable Rectangle frameSize
} }

View File

@ -0,0 +1,13 @@
log4j.rootLogger=ERROR, stdout
log4j.logger.com.jdbernard.timestamper=TRACE
# set up stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%t] %-5p - %m%n
# set up fileout
log4j.appender.fileout=org.apache.log4j.FileAppender
log4j.appender.fileout.File=TimeStamper.log
log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
log4j.appender.fileout.layout.ConversionPattern=%5p [%t] - %m%n

1399
griffon-app/session.vim Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,49 +8,52 @@ import javax.swing.BoxLayout
import net.miginfocom.swing.MigLayout import net.miginfocom.swing.MigLayout
Point mousePressRelativeToDialog Point mousePressRelativeToDialog
Point offsetFromMainFrame Point offsetFromMainFrame = new Point(0,0)
boolean coupledToMainFrame = false
mousePressed = { evt -> mousePressRelativeToDialog = evt?.point } mousePressed = { evt -> mousePressRelativeToDialog = evt?.point }
mouseDragged = { evt -> mouseDragged = { evt ->
GUIUtil.componentDragged(view.notesDialog, evt, GUIUtil.componentDragged(dialog, evt, mousePressRelativeToDialog,
mousePressRelativeToDialog,
new Rectangle(Toolkit.defaultToolkit.screenSize), new Rectangle(Toolkit.defaultToolkit.screenSize),
app.views.TimeStamperMain.frame.bounds) model.mainMVC.view.frame.bounds)
Point p = app.views.TimeStamperMain.frame.location offsetFromMainFrame = GUIUtil.calculateOffset(
offsetFromMainFrame = new Point(notesDialog.location) model.mainMVC.view.frame, dialog)
offsetFromMainFrame.translate((int) -p.x, (int) -p.y)
coupledToMainFrame = GUIUtil.componentsCoupled(dialog,
model.mainMVC.view.frame);
} }
notesDialog = dialog( dialog = dialog(
title: 'Notes', title: 'Notes',
modal: false, modal: false,
undecorated: true, undecorated: true,
minimumSize: [325, 200], minimumSize: [325, 200],
iconImage: imageIcon('/16-em-pencil.png').image, iconImage: imageIcon('/16-em-pencil.png').image,
iconImages: [imageIcon('/16-em-pencil.png').image], iconImages: [imageIcon('/16-em-pencil.png').image],
location: bind(source: app.models.TimeStamperMain, location: bind(source: model.mainMVC.model,
sourceProperty: 'absoluteLocation', sourceProperty: 'absoluteLocation',
converter: { loc -> converter: { loc ->
Point p = new Point(offsetFromMainFrame) if (coupledToMainFrame) {
p.translate((int) loc.x, (int) loc.y) Point p = new Point(offsetFromMainFrame)
return p}) p.translate((int) loc.x, (int) loc.y)
return p
} else return dialog.location })
) { ) {
panel( panel(
border:lineBorder(color: Color.BLACK, thickness:1, parent:true), border:lineBorder(color: Color.BLACK, thickness:1, parent:true),
mousePressed: mousePressed, mousePressed: mousePressed,
mouseDragged: mouseDragged, mouseDragged: mouseDragged,
layout: new MigLayout('insets 5 5 5 5, fill') layout: new MigLayout('insets 5 5 5 5, fill')
) { ) {
scrollPane(constraints: 'growx, growy, spany 2') { scrollPane(constraints: 'growx, growy') {
notesTextArea = textArea(lineWrap: true, columns: 20, rows: 5, notesTextArea = textArea(lineWrap: true, columns: 20, rows: 5,
wrapStyleWord: true, wrapStyleWord: true,
text: bind(source: app.models.TimeStamperMain, text: bind(source: model.mainMVC.model,
sourceProperty: 'currentMarker', sourceProperty: 'currentMarker',
sourceValue: { app.models.TimeStamperMain.currentMarker?.notes})) sourceValue: { model.mainMVC.model.currentMarker?.notes}))
} }
} }
} }

View File

@ -1,6 +1,5 @@
package com.jdbernard.timestamper package com.jdbernard.timestamper
import groovy.beans.Bindable
import java.awt.Color import java.awt.Color
import java.awt.Font import java.awt.Font
import java.awt.Point import java.awt.Point
@ -12,8 +11,6 @@ import javax.swing.Timer
import com.jdbernard.timestamper.core.Timeline import com.jdbernard.timestamper.core.Timeline
import net.miginfocom.swing.MigLayout import net.miginfocom.swing.MigLayout
Point mousePressRelativeToFrame
/* ========== * /* ========== *
* GUI Events * * GUI Events *
* ========== */ * ========== */
@ -38,7 +35,7 @@ showToolsMenu = { evt = null ->
} }
showNotes = { evt = null -> showNotes = { evt = null ->
notesDialog.visible = notesVisibleButton.selected model.notesDialogMVC.view.dialog.visible = notesVisibleButton.selected
} }
showPunchcard = { evt = null -> showPunchcard = { evt = null ->
@ -119,7 +116,7 @@ frame = application(title:'TimeStamper',
border: emptyBorder(0), border: emptyBorder(0),
contentAreaFilled: false, contentAreaFilled: false,
hideActionText: true) hideActionText: true)
button(gracefulExitAction, button(actionPerformed: controller.&exitGracefully,
icon: imageIcon('/16-em-cross.png'), icon: imageIcon('/16-em-cross.png'),
rolloverIcon: imageIcon('/16-em-cross-hover.png'), rolloverIcon: imageIcon('/16-em-cross-hover.png'),
border: emptyBorder(0), border: emptyBorder(0),

View File

@ -83,9 +83,50 @@ public class GUIUtil {
return finalWindowPoint; return finalWindowPoint;
} }
def static componentDragged(frame, MouseEvent evt, static void componentDragged(frame, MouseEvent evt,
Point mousePressRelativeToFrame, Rectangle... snapBoxes) { Point mousePressRelativeToFrame, Rectangle... snapBoxes) {
frame.location = calculateWindowMovement(evt.getLocationOnScreen(), frame.location = calculateWindowMovement(evt.getLocationOnScreen(),
mousePressRelativeToFrame, frame.bounds, snapBoxes) mousePressRelativeToFrame, frame.bounds, snapBoxes)
} }
static boolean componentsCoupled(c1, c2) {
def h1 = c1.bounds.height
def w1 = c1.bounds.width
def h2 = c2.bounds.height
def w2 = c2.bounds.width
return (
( // horizontal
( // snapped horizontally
((c1.x - c2.x).abs() < 20) || // c1 left edge to c2 left edge
((c1.x + w1 - c2.x - w2).abs() < 20) || // c1 right edge to c2 right edge
((c1.x + w1 - c2.x).abs() < 20) || // c1 right edge to c2 left edge
((c2.x + w2 - c1.x).abs() < 20) // c1 left edge to c2 right edge
) && (// touching vertically
(c1.y <= c2.y && c1.y + h1 >= c2.y) ||
(c1.y <= c2.y + h2 && c1.y + h1 >= c2.y + h2) ||
(c1.y > c2.y && c1.y + h1 < c2.y + h2)
)
) || ( // vertical
( // snapped vertically
((c1.y - c2.y).abs() < 20) || // c1 top to c2 top
((c1.y + h1 - c2.y - h2).abs() < 20) || // c1 bot to c2 bot
((c1.y + h1 - c2.y).abs() < 20) || // c1 bot to c2 top
((c2.y + h2 - c1.y).abs() < 20) // c1 top to c2 bot
) && (// touching horizontally
(c1.x <= c2.x && c1.x + w1 >= c2.x) ||
(c1.x <= c2.x + w2 && c1.x + w1 >= c2.x + w2) ||
(c1.x > c2.x && c1.x + w1 < c2.x + w2)
)
)
)
}
static Point calculateOffset(c1, c2) {
Point p = c1.location
Rectangle r = c1.bounds
Point offset = new Point(c2.location)
offset.translate((int) -p.x, (int) -p.y)
return offset
}
} }