From iverodin at gmail.com Fri Mar 7 16:26:54 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Fri, 7 Mar 2008 17:26:54 +0100 Subject: [rdnzl-devel] Rdnzl Problem In-Reply-To: References: <47A3A901.8000108@gmail.com> Message-ID: > Maybe you'd like to write a paragraph or two about generic types that we can add to the documentation? (ASCII text is > fine, doesn't have to be HTML.) So I've tried writing some documentation for this now finally, here goes: === CUT HERE ==== Generic types in RDNZL In summary, refer to a generic type with type-arguments filled with a list of type-names like ("System.Collections.Generic.List" "System.Int32") Motivation The name of a generic type, when 'closed' with type arguments so it is instantiable, is of the form "Basetype?arity[ParameterType1,...,ParameterTypeN]" and type names of this form can in general be used in all contexts like the argument to 'NEW' and so forth. However, for this type to be found, all the parameter types must either lie in the same assembly as the base type or their names must be assembly-qualified. Furthermore, the full 'path' to each type would have to be specified even if their namespaced had been imported with USE-NAMESPACE making this a bit unpractical. To solve this, all arguments that accept a string as a typename will also accept a list of typenames (including sublists for when type-arguments are themselves generic types) where these lists represent generic types with their parameters. Also, since the length of the list is enough to determine the arity of the type, the "?arity"-part of the type-name can be dropped. Each type-name element of the list will have its name resolved in the imported namespaces. The upshot is that for one can instantiate the type with full name System.Func`2[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 using (import-assembly "mscorlib") ;; Int32 lives here (import-assembly "System.Core") ;; Func (of diverse arities) lives here (use-namespace "System") (new '("Func" "Int32" "Int32") #'1+) From matthew.oconnor at calyptech.com Wed Mar 12 10:42:27 2008 From: matthew.oconnor at calyptech.com (Matthew O Connor) Date: Wed, 12 Mar 2008 21:42:27 +1100 Subject: [rdnzl-devel] DataGridView in virtual mode sample. Message-ID: Hi all, I have implemented a little example that shows the use of the .net DataGridView control in virtual mode using RDNZL and Lispworks. The DataGridView control is particularly difficult to get working properly. I can't count the number of times I crashed my environment getting this working but foreign function calls are always hairy. The only thing you should have to do to get it working is change the location of the RDNZL path. Enjoy, Matthew O'Connor ;; ======================================================================== ;; DataGridView Experiment. ;; ;; Matthew O'Connor ;; ;; This is basically a lisp implementation of the Microsoft "Walkthrough: ;; Implementing Virtual Mode in the Windows Forms DataGridView Control". ;; ;; The DataGridView is running in Virtual mode continually coming back ;; to Lisp through the callbacks to get and set data. ;; ;; The example is made a little more complex by the use of an object to ;; store the row that is currently being edited. This allows a cell edit ;; to be undone with a simple press of the Escape key. A second press ;; of the same key will undo the changes made to the entire row. ;; ;; As usual there is no warranty. Use at your own peril. I am relatively ;; new to Lisp so you are warned. ;; NOTE: You need to modify this to point to the location of RDNZL on your system. (load "../RDNZL/rdnzl-0.12.0/load.lisp") ;; ------------------------------------------------------------------------ ;; RDNZL setup. (rdnzl:enable-rdnzl-syntax) (rdnzl:import-types "System.Windows.Forms" "Application" "Form" "DockStyle" "DataGridView" "DataGridViewTextBoxColumn" "DataGridViewCellValueEventHandler" "DataGridViewRowEventHandler" "QuestionEventHandler" "DataGridViewCellEventHandler" "DataGridViewRowCancelEventHandler") (rdnzl:use-namespace "System.Windows.Forms") ;; ------------------------------------------------------------------------ ;; Utilities (defun filter (lst fn) (let ((acc nil)) (dolist (x lst) (let ((val (funcall fn x))) (if val (push x acc)))) (nreverse acc))) ;; ------------------------------------------------------------------------ ;; Units ;; The items that will be stored in the database. At the moment they are ;; simply all strings. (defclass unit () ((code :accessor code :initarg :code :initform "None") (description :accessor description :initarg :description :initform "None") (name :accessor name :initarg :name :initform "Unknown"))) ;; Create a unit with an optional description. (defun make-unit (code name &optional description) (if description (make-instance 'unit :code code :name name :description description) (make-instance 'unit :code code :name name))) ;; Clone a unit this is needed by the datagridview which makes an copy whilst ;; editing a row. (defun clone-unit (unit) (make-unit (code unit) (name unit) (description unit))) ;; ------------------------------------------------------------------------ ;; units database ;; A simple database of units. (defparameter *database-units* nil) (defun database-add-unit (unit) (setf *database-units* (append *database-units* (list unit)))) (defun database-remove-unit (unit) (setf *database-units* (remove unit *database-units*))) (defun database-remove-unit-at (index) (setf *database-units* (remove (nth index *database-units*) *database-units*))) (defun database-get-unit (code) (first (filter *database-units* #'(lambda (unit) (equal code (code unit)))))) (defun database-get-unit-at (index) (nth index *database-units*)) (defun database-update-unit-at (index unit) (setf (nth index *database-units*) unit)) (defun database-units-count () (length *database-units*)) ;; The data grid view that is used to represent the units. (defparameter *units-data-grid-view* nil) ;; Keeps track of the row index that we are currently editing. A value ;; of -1 indicates that we are not editing anything at the moment. (defparameter *row-in-edit* -1) ;; This is the unit that will be created to hold edits until they have ;; been validated. At this point they will be written back to the database. (defparameter *unit-in-edit* nil) ;; I'm not sure about this variable. It never seems to be altered anywhere ;; in the example code. (defparameter *row-scope-commit* t) ;; A little utility to get the count of rows. (defun data-grid-view-row-count () [%Count [%Rows *units-data-grid-view*]]) ;; Are we editing this row already. (defun is-row-in-edit (index) (= *row-in-edit* index)) (defun is-new-row (index) (= index (- (data-grid-view-row-count) 1))) ;; Is the supplied index the new row. (defun is-new-row-from-row (row) [%IsNewRow row]) ;; We are no longer editing any row so set the variables back to their ;; defaults. (defun reset-unit-in-edit () (setf *unit-in-edit* nil *row-in-edit* -1)) ;; ------------------------------------------------------------------------ ;; Implement the DataGridView callbacks. ;; ;; These are reasonably complex little calls. I refer you to the "Walkthrough" ;; mentioned at the start of the file for further information on how they ;; work. ;; The event occurs whenever the DataGridView requires the value to a cell for ;; display. It retrieves the value from either the database of units or from the ;; unit currently being edited. ;; ;; DataGridViewCellValueEventArgs ;; int ColumnIndex - The index of the column. ;; int RowIndex - The index of the row. ;; object Value - The value of the row. (defun cell-value-needed (object event) (let ((row-index [%RowIndex event]) (column-index [%ColumnIndex event]) (unit nil)) (unless (is-new-row row-index) (if (is-row-in-edit row-index) ;; then (setf unit *unit-in-edit*) ;; else (setf unit (database-get-unit-at row-index))) (unless (equal unit nil) (case column-index ((0) (setf [%Value event] (code unit))) ((1) (setf [%Value event] (name unit))) ((2) (setf [%Value event] (description unit)))))))) ;; This event occurs whenever a cell value has been updated on the DataGridView. ;; It gives the underlying storage mechanism a chance to update the value. ;; ;; If there is no unit currently in edit one is created. The updates do not go ;; directly to the database. They go via the unit currently in edit first. Once ;; the changes have been validated they are updated in the database. ;; ;; DataGridViewCellValueEventArgs ;; int ColumnIndex - The index of the column. ;; int RowIndex - The index of the row. ;; object Value - The value of the row. (defun cell-value-pushed (object event) (let ((row-index [%RowIndex event]) (column-index [%ColumnIndex event]) (unit-tmp nil)) (if (< row-index (length *database-units*)) (progn (when (equal *unit-in-edit* nil) (setf *unit-in-edit* (clone-unit (database-get-unit-at row-index)))) (setf unit-tmp *unit-in-edit* *row-in-edit* row-index)) ;; else (setf unit-tmp *unit-in-edit*)) (unless (equal unit-tmp nil) (let ((value [%Value event])) (case column-index ((0) (setf (code unit-tmp) (rdnzl:unbox (rdnzl:cast value "System.String")))) ((1) (setf (name unit-tmp) (rdnzl:unbox (rdnzl:cast value "System.String")))) ((2) (setf (description unit-tmp) (rdnzl:unbox (rdnzl:cast value "System.String"))))))))) ;; Occurs whenever a new row is needed at the end of a DataGridView. It simply ;; creates a new empty unit that becomes the current unit in edit. ;; ;; DataGridViewRowEventArgs: ;; DataGridViewRow Row - I'm not sure what the contents of the row are ;; as I currently don't use them. (defun new-row-needed (object event) (setf *unit-in-edit* (make-unit "" "" "") *row-in-edit* (- (data-grid-view-row-count) 1))) ;; This event occurs after a row has finished validating. At this point the data ;; can be pushed to the database. ;; ;; DataGridViewViewCellEventArgs ;; int ColumnIndex - The index of the column. ;; int RowIndex - The index of the row. (defun row-validated (object event) (let ((row-index [%RowIndex event])) (if (and (>= row-index (database-units-count)) (not (= row-index (- (data-grid-view-row-count) 1) ))) (progn (database-add-unit *unit-in-edit*) (reset-unit-in-edit)) (if (and (not (equal *unit-in-edit* nil)) (< row-index (database-units-count))) (progn (database-update-unit-at row-index *unit-in-edit*) (reset-unit-in-edit)) (if [%ContainsFocus *units-data-grid-view*] (reset-unit-in-edit)))))) ;; Used by the DataGridView to determine if current row has uncommitted changes. ;; ;; Note - I'm not quite sure how this works. ;; ;; QuestionEventArgs ;; Boolean Response - True or False. (defun row-dirty-state-needed (object event) (unless *row-scope-commit* (setf [%Response event] [%IsCurrentCellDirty *units-data-grid-view*]))) ;; Give the application the opportunity to cancel the edits in a row. ;; ;; A single escape key cancels the cell edit. A double cancels the row edit. ;; ;; QuestionEventArgs ;; Boolean Response - True to cancel the row-edit, False (I assume) to let it ;; continue. (defun cancel-row-edit (object event) (if (and (= *row-in-edit* (- (data-grid-view-row-count) 2)) (= *row-in-edit* (database-units-count))) (setf *unit-in-edit* (make-unit "" "" "")) (reset-unit-in-edit))) ;; Remove the row if it is in the database otherwise simply reset the unit in edit. ;; ;; DataGridViewRowCancelEventArgs: ;; DataGridViewRow Row - The row the user is deleting. ;; Boolean Cancel - Cancel the addition of the row. (defun user-deleting-row (object event) (let* ((data-grid-row [%Row event]) (row-index [%Index data-grid-row])) (when (< row-index (database-units-count)) (database-remove-unit-at row-index)) (when (= row-index *row-in-edit*) (reset-unit-in-edit)))) ;; ------------------------------------------------------------------------ ;; Main ;; Create a new text box column. (defun create-column (index name heading) (let ((col (rdnzl:new "DataGridViewTextBoxColumn"))) (setf [%DisplayIndex col] index [%Name col] name [%HeaderText col] heading) col)) ;; Create a DataGridView with three columns; Code, Name and Description. ;; It is placed in Virtual mode and all of the callbacks are hooked up. (defun create-units-editor () (setf *units-data-grid-view* (rdnzl:new "DataGridView") *row-in-edit* -1 *unit-in-edit* nil *row-scope-commit* t [%VirtualMode *units-data-grid-view*] t [%Dock *units-data-grid-view*] [$DockStyle.Fill] [%Text *units-data-grid-view*] "DataGridView Experiment" [%RowHeadersVisible *units-data-grid-view*] t) [Add [%Columns *units-data-grid-view*] (create-column 0 "code" "Code")] [Add [%Columns *units-data-grid-view*] (create-column 1 "name" "Name")] [Add [%Columns *units-data-grid-view*] (create-column 2 "description" "Description")] [+CellValueNeeded *units-data-grid-view* (rdnzl:new "DataGridViewCellValueEventHandler" #'cell-value-needed)] [+CellValuePushed *units-data-grid-view* (rdnzl:new "DataGridViewCellValueEventHandler" #'cell-value-pushed)] [+NewRowNeeded *units-data-grid-view* (rdnzl:new "DataGridViewRowEventHandler" #'new-row-needed)] [+UserDeletingRow *units-data-grid-view* (rdnzl:new "DataGridViewRowCancelEventHandler" #'user-deleting-row)] [+CancelRowEdit *units-data-grid-view* (rdnzl:new "QuestionEventHandler" #'cancel-row-edit)] [+RowDirtyStateNeeded *units-data-grid-view* (rdnzl:new "QuestionEventHandler" #'row-dirty-state-needed)] [+RowValidated *units-data-grid-view* (rdnzl:new "DataGridViewCellEventHandler" #'row-validated)]) ;; Call this function to run the application. (defun run-datagridview-experiment () (let ((form (rdnzl:new "Form"))) (create-units-editor) (setf [%Text form] "DataGridView Experiment") [Add [%Controls form] *units-data-grid-view*] [BringToFront *units-data-grid-view*] [Application.Run form])) (run-datagridview-experiment) -------------- next part -------------- An HTML attachment was scrubbed... URL: From edi at agharta.de Wed Mar 12 11:47:59 2008 From: edi at agharta.de (Edi Weitz) Date: Wed, 12 Mar 2008 12:47:59 +0100 Subject: [rdnzl-devel] DataGridView in virtual mode sample. In-Reply-To: (Matthew O. Connor's message of "Wed, 12 Mar 2008 21:42:27 +1100") References: Message-ID: On Wed, 12 Mar 2008 21:42:27 +1100, "Matthew O Connor" wrote: > I have implemented a little example that shows the use of the .net > DataGridView control in virtual mode using RDNZL and Lispworks. Nice, thanks. I haven't tried it yet, but can we add it to the RDNZL distribution? From matthew.oconnor at calyptech.com Thu Mar 13 00:51:33 2008 From: matthew.oconnor at calyptech.com (Matthew O'Connor) Date: Thu, 13 Mar 2008 00:51:33 +0000 Subject: [rdnzl-devel] DataGridView in virtual mode sample. In-Reply-To: References: Message-ID: <1205369493.6231.9.camel@localhost> Feel free to use it however you see fit. I plan to continue its development. I want to add multiple views of the same data and perhaps multi-level undo. I will keep it self contained so that a dll doesn't have to be shipped with it. Regards, Matthew On Wed, 2008-03-12 at 12:47 +0100, Edi Weitz wrote: > On Wed, 12 Mar 2008 21:42:27 +1100, "Matthew O Connor" wrote: > > > I have implemented a little example that shows the use of the .net > > DataGridView control in virtual mode using RDNZL and Lispworks. > > Nice, thanks. I haven't tried it yet, but can we add it to the RDNZL > distribution? > _______________________________________________ > rdnzl-devel mailing list > rdnzl-devel at common-lisp.net > http://common-lisp.net/cgi-bin/mailman/listinfo/rdnzl-devel From matthew.oconnor at calyptech.com Fri Mar 14 12:02:22 2008 From: matthew.oconnor at calyptech.com (Matthew O Connor) Date: Fri, 14 Mar 2008 23:02:22 +1100 Subject: [rdnzl-devel] C# pointer comparison. Message-ID: Hi, I have two instances of a DataGridView each bound to the same callbacks within Lisp. The callbacks each have two parameters, the first of which is a C# object. This object corresponds to the DataGridView that initiated the event. When I create the two DataGridViews I put them both into a list and when a callback is called I look through my list to match up the first parameter with one of the references in my list. However this is failing. How do I compare references to C# objects using RDNZL? I've noticed that there is an enum-to-int function which allows enums to be compared in Lisp. I may need something like this to compare the identity of objects in Lisp. Another approach would be to write a function in C# to compare object identity but I don't want to include a dll in my little example. Any help would be appreciated. Regards, Matthew -------------- next part -------------- An HTML attachment was scrubbed... URL: From edi at agharta.de Fri Mar 14 12:23:39 2008 From: edi at agharta.de (Edi Weitz) Date: Fri, 14 Mar 2008 13:23:39 +0100 Subject: [rdnzl-devel] C# pointer comparison. In-Reply-To: (Matthew O. Connor's message of "Fri, 14 Mar 2008 23:02:22 +1100") References: Message-ID: On Fri, 14 Mar 2008 23:02:22 +1100, "Matthew O Connor" wrote: > How do I compare references to C# objects using RDNZL? If you don't mind using unexported functionality, with two RDNZL containers C1 and C2 you'd do something like (untested) (fli:pointer-eq (rdnzl::pointer c1) (rdnzl::pointer c2)) on LispWorks. I'm not sure if this The Right Thing[TM] to do, but if it is (comments welcome), we should probably export a portable version of this. From matthew.oconnor at calyptech.com Sat Mar 15 00:32:55 2008 From: matthew.oconnor at calyptech.com (Matthew O Connor) Date: Sat, 15 Mar 2008 11:32:55 +1100 Subject: [rdnzl-devel] C# pointer comparison. References: Message-ID: Edi, I tried out the code you gave me but it doesn't do the trick. I've created some test code to illustrate my problem a bit better. It is listed below. There are two parts to it. You have to build a simple dll which contains a class that simply stores a reference passed to it as an "object". The lisp code simply instantiates a ReferenceComparisonTest object, passes it a reference to itself and then later compares what it passed in with what it got back to see if they are the same thing. They should be the same but clearly aren't. I think this is the crux of my problem. Here is the code: // C# using System; namespace ReferenceComparisonTest { public class ReferenceComparisonTest { private object _reference = null; public ReferenceComparisonTest() { _reference = this; } public object reference { get { return _reference; } set { _reference = value; } } } } ;; LISP (load "../RDNZL/rdnzl-0.12.0/load.lisp") (rdnzl:enable-rdnzl-syntax) (rdnzl:import-types "ReferenceComparisonTest" "ReferenceComparisonTest") (rdnzl:use-namespace "ReferenceComparisonTest") (defun pointers-equal (a b) (fli::pointer-eq (rdnzl::pointer a) (rdnzl::pointer b))) (defun main () (let ((x (rdnzl::new "ReferenceComparisonTest"))) ;; Make x store a reference to itself. (setf [%reference x] x) ;; Compare x with what was stored in x. These should be the same. (if (pointers-equal x [%reference x]) (format t "Excellent! The references are the same as expected.") (format t "Error! The references were different but should have been the same.")))) (main) Regards, Matthew -------------- next part -------------- A non-text attachment was scrubbed... Name: winmail.dat Type: application/ms-tnef Size: 3337 bytes Desc: not available URL: From edi at agharta.de Sat Mar 15 14:40:01 2008 From: edi at agharta.de (Edi Weitz) Date: Sat, 15 Mar 2008 15:40:01 +0100 Subject: [rdnzl-devel] C# pointer comparison. In-Reply-To: (Matthew O. Connor's message of "Sat, 15 Mar 2008 11:32:55 +1100") References: Message-ID: On Sat, 15 Mar 2008 11:32:55 +1100, "Matthew O Connor" wrote: > I tried out the code you gave me but it doesn't do the trick. I've > created some test code to illustrate my problem a bit better. It is > listed below. > > There are two parts to it. You have to build a simple dll which > contains a class that simply stores a reference passed to it as an > "object". The lisp code simply instantiates a > ReferenceComparisonTest object, passes it a reference to itself and > then later compares what it passed in with what it got back to see > if they are the same thing. > > They should be the same but clearly aren't. I think this is the crux > of my problem. Yes, I see now. I misunderstood your original question. What you want is a bit more complicated but certainly doable (I think) and would probably also be worthwhile to have as a part of RDNZL. You need to add new C++ code for this, though. Look at the DotNetContainer.h and DotNetReference.h files. A container has a public function getContainerObject which returns the object it wraps. You want to add a C++ function which accepts two DotNetContainer objects and compares the objects they wrap. This function can then be called from Lisp code via the FFI. I can add that, but it'll take be some time as I'm busy with other things right now, so feel free to play with this yourself. From matthew.oconnor at calyptech.com Mon Mar 17 03:03:14 2008 From: matthew.oconnor at calyptech.com (Matthew O'Connor) Date: Mon, 17 Mar 2008 03:03:14 +0000 Subject: [rdnzl-devel] C# pointer comparison. In-Reply-To: References: Message-ID: <1205722994.6868.9.camel@localhost> Edi, I will have a look at the files you mention and see if I can come up with new function that will do the comparison. I don't have C++ on my laptop yet. Once I have this I'll give it a go. Cheers, Matthew From matthew.oconnor at calyptech.com Tue Mar 18 23:13:01 2008 From: matthew.oconnor at calyptech.com (Matthew O'Connor) Date: Tue, 18 Mar 2008 23:13:01 +0000 Subject: [rdnzl-devel] C# pointer comparison. In-Reply-To: References: Message-ID: <1205881981.6868.49.camel@localhost> Edi, I've got a solution for this. I can't claim to completely understand what is happening with all of RDNZL but I was able to copy some other functionality and get what I thought was the correct implementation. What is the best way to have this reviewed, edited and possibly included in the distribution? Is there a regression suite for RDNZL? I haven't run any exhaustive tests. Regards, Matthew From matthew.oconnor at calyptech.com Tue Mar 18 23:17:22 2008 From: matthew.oconnor at calyptech.com (Matthew O'Connor) Date: Wed, 19 Mar 2008 10:17:22 +1100 Subject: [rdnzl-devel] Multiple DataGridViews in virtual mode example. Message-ID: <1205882242.6868.54.camel@localhost> Hi all once again, I have another example of the DataGridView. This time it is a simple MVC example. It is two DataGridViews showing/editing the same dataset. So you can change the data in one and see the updates reflected in the other. Feel free to to with it as you wish, but use it at your own peril. I am still just a Lisp newbie. Regards, Matthew ;; ======================================================================== ;; Multiple DataGridView Experiment. ;; ;; Matthew O'Connor ;; ;; This experiment is an extension of the last one in that there are now ;; two DataGridViews watching over the same database. ;; ;; The example is a simple implemenation of the Model View Controller ;; pattern. Each DataGridView is independent of the other but when the ;; underlying model (database) changes all views (DataGridView) are ;; notified and update their representation accordingly. ;; ;; You should be able add, remove or edit rows in one DataGridView and ;; have the updates appear in the other one. ;; ;; As with the previous experiment the DataGridView is running in Virtual ;; mode. The update mechanisms are a bit more complex now because ;; DataGridView that should be updated has to be determined. ;; ;; As usual there is no warranty. Use at your own peril. I am relatively ;; new to Lisp so you are warned. ;; NOTE: You need to modify this to point to the location of RDNZL on your system. (load "../RDNZL/rdnzl-0.12.0/load.lisp") ;; ------------------------------------------------------------------------ ;; RDNZL setup. (rdnzl:enable-rdnzl-syntax) (rdnzl:import-types "System.Windows.Forms" "Application" "Form" "Splitter" "DockStyle" "DataGridView" "DataGridViewTextBoxColumn" "DataGridViewCellValueEventHandler" "DataGridViewRowEventHandler" "QuestionEventHandler" "DataGridViewCellEventHandler" "DataGridViewRowCancelEventHandler") (rdnzl:use-namespace "System.Windows.Forms") (rdnzl:import-types "System.Drawing" "Size") (rdnzl:use-namespace "System.Drawing") ;; ------------------------------------------------------------------------ ;; Utilities (defun filter (lst fn) (let ((acc nil)) (dolist (x lst) (let ((val (funcall fn x))) (if val (push x acc)))) (nreverse acc))) (defun append1 (lst obj) (append lst (list obj))) ;; ------------------------------------------------------------------------ ;; Units ;; The items that will be stored in the database. At the moment they are ;; simply all strings. (defclass unit () ((code :accessor code :initarg :code :initform "None") (description :accessor description :initarg :description :initform "None") (name :accessor name :initarg :name :initform "Unknown"))) ;; Create a unit with an optional description. (defun make-unit (code name &optional description) (if description (make-instance 'unit :code code :name name :description description) (make-instance 'unit :code code :name name))) ;; Clone a unit this is needed by the datagridview which makes an copy whilst ;; editing a row. (defun clone-unit (unit) (make-unit (code unit) (name unit) (description unit))) ;; ------------------------------------------------------------------------ ;; units database ;; ;; The database has been slightly upgraded to support observers. Client code ;; registers with the database through the database-add-observer call ;; supplying their own callback ;; A simple database of units. (defparameter *database-units* nil) (defparameter *database-observers* nil) (defun database-add-observer (callback dgv) (setf *database-observers* (append1 *database-observers* (list callback dgv)))) (defun database-signal-observers (sender) (mapcar #'(lambda (x) (funcall (first x) (second x) sender)) *database-observers*)) (defun database-add-unit (modifier unit) (setf *database-units* (append *database-units* (list unit))) (database-signal-observers modifier)) (defun database-remove-unit (modifier unit) (setf *database-units* (remove unit *database-units*)) (database-signal-observers modifier)) (defun database-remove-unit-at (modifier index) (setf *database-units* (remove (nth index *database-units*) *database-units*)) (database-signal-observers modifier)) (defun database-get-unit (code) (first (filter *database-units* #'(lambda (unit) (equal code (code unit)))))) (defun database-get-unit-at (index) (nth index *database-units*)) (defun database-update-unit-at (modifier index unit) (setf (nth index *database-units*) unit) (database-signal-observers modifier)) (defun database-units-count () (length *database-units*)) ;; ------------------------------------------------------------------------ ;; DataGridView control variables. (defclass unit-data-grid-view () ((data-grid-view :accessor data-grid-view :initarg :data-grid-view :initform nil) (row-in-edit :accessor row-in-edit :initarg :row-in-edit :initform -1) (unit-in-edit :accessor unit-in-edit :initarg :unit-in-edit :initform nil) (row-scope-commit :accessor row-scope-commit :initarg :row-scope-commit :initform t))) (defparameter *unit-views* nil) (defun new-unit-data-grid-view (dgv) (let ((new-dgv (make-instance 'unit-data-grid-view :data-grid-view dgv))) (setf *unit-views* (append1 *unit-views* new-dgv)))) (defun get-unit-data-grid-view (dgv) (let ((name [%Name (rdnzl:cast dgv "DataGridView")])) (first (filter *unit-views* (lambda (x) (equal name [%Name (data-grid-view x)])))))) ;; A little utility to get the count of rows. (defun data-grid-view-row-count (udgv) [%Count [%Rows (data-grid-view udgv)]]) ;; Are we editing this row already. (defun is-row-in-edit (udgv index) (= (row-in-edit udgv) index)) (defun is-new-row (udgv index) (= index (- (data-grid-view-row-count udgv) 1))) ;; Is the supplied index the new row. ;; (defun is-new-row-from-row (row) ;; [%IsNewRow row]) ;; We are no longer editing any row so set the variables back to their ;; defaults. (defun reset-unit-in-edit (udgv) (setf (unit-in-edit udgv) nil (row-in-edit udgv) -1)) ;; ------------------------------------------------------------------------ ;; Implement the DataGridView callbacks. ;; ;; These are reasonably complex little calls. I refer you to the "Walkthrough" ;; mentioned at the start of the file for further information on how they ;; work. ;; The event occurs whenever the DataGridView requires the value to a cell for ;; display. It retrieves the value from either the database of units or from the ;; unit currently being edited. ;; ;; DataGridViewCellValueEventArgs ;; int ColumnIndex - The index of the column. ;; int RowIndex - The index of the row. ;; object Value - The value of the row. (defun cell-value-needed (object event) (let ((udgv (get-unit-data-grid-view object)) (row-index [%RowIndex event]) (column-index [%ColumnIndex event]) (unit nil)) (unless (is-new-row udgv row-index) (if (is-row-in-edit udgv row-index) ;; then (setf unit (unit-in-edit udgv)) ;; else (setf unit (database-get-unit-at row-index))) (unless (equal unit nil) (case column-index ((0) (setf [%Value event] (code unit))) ((1) (setf [%Value event] (name unit))) ((2) (setf [%Value event] (description unit)))))))) ;; This event occurs whenever a cell value has been updated on the DataGridView. ;; It gives the underlying storage mechanism a chance to update the value. ;; ;; If there is no unit currently in edit one is created. The updates do not go ;; directly to the database. They go via the unit currently in edit first. Once ;; the changes have been validated they are updated in the database. ;; ;; DataGridViewCellValueEventArgs ;; int ColumnIndex - The index of the column. ;; int RowIndex - The index of the row. ;; object Value - The value of the row. (defun cell-value-pushed (object event) (let ((udgv (get-unit-data-grid-view object)) (row-index [%RowIndex event]) (column-index [%ColumnIndex event]) (unit-tmp nil)) (if (< row-index (length *database-units*)) (progn (when (equal (unit-in-edit udgv) nil) (setf (unit-in-edit udgv) (clone-unit (database-get-unit-at row-index)))) (setf unit-tmp (unit-in-edit udgv) (row-in-edit udgv) row-index)) ;; else (setf unit-tmp (unit-in-edit udgv))) (unless (equal unit-tmp nil) (let ((value [%Value event])) (case column-index ((0) (setf (code unit-tmp) (rdnzl:unbox (rdnzl:cast value "System.String")))) ((1) (setf (name unit-tmp) (rdnzl:unbox (rdnzl:cast value "System.String")))) ((2) (setf (description unit-tmp) (rdnzl:unbox (rdnzl:cast value "System.String"))))))))) ;; Occurs whenever a new row is needed at the end of a DataGridView. It simply ;; creates a new empty unit that becomes the current unit in edit. ;; ;; DataGridViewRowEventArgs: ;; DataGridViewRow Row - I'm not sure what the contents of the row are ;; as I currently don't use them. (defun new-row-needed (object event) (let ((udgv (get-unit-data-grid-view object))) (setf (unit-in-edit udgv) (make-unit "" "" "") (row-in-edit udgv) (- (data-grid-view-row-count udgv) 1)))) ;; This event occurs after a row has finished validating. At this point the data ;; can be pushed to the database. ;; ;; DataGridViewViewCellEventArgs ;; int ColumnIndex - The index of the column. ;; int RowIndex - The index of the row. (defun row-validated (object event) (let ((udgv (get-unit-data-grid-view object)) (row-index [%RowIndex event])) (when [%Focused object] (if (and (>= row-index (database-units-count)) (not (= row-index (- (data-grid-view-row-count udgv) 1) ))) (progn (database-add-unit object (unit-in-edit udgv)) (reset-unit-in-edit udgv)) (if (and (not (equal (unit-in-edit udgv) nil)) (< row-index (database-units-count))) (progn (database-update-unit-at object row-index (unit-in-edit udgv)) (reset-unit-in-edit udgv)) (if [%ContainsFocus (data-grid-view udgv)] (reset-unit-in-edit udgv))))))) ;; Used by the DataGridView to determine if current row has uncommitted changes. ;; ;; Note - I'm not quite sure how this works. ;; ;; QuestionEventArgs ;; Boolean Response - True or False. (defun row-dirty-state-needed (object event) (let ((udgv (get-unit-data-grid-view object))) (unless (row-scope-commit udgv) (setf [%Response event] [%IsCurrentCellDirty udgv])))) ;; Give the application the opportunity to cancel the edits in a row. ;; ;; A single escape key cancels the cell edit. A double cancels the row edit. ;; ;; QuestionEventArgs ;; Boolean Response - True to cancel the row-edit, False (I assume) to let it ;; continue. (defun cancel-row-edit (object event) (let ((udgv (get-unit-data-grid-view object))) (if (and (= (row-in-edit udgv) (- (data-grid-view-row-count udgv) 2)) (= (row-in-edit udgv) (database-units-count))) (setf (unit-in-edit udgv) (make-unit "" "" "")) (reset-unit-in-edit udgv)))) ;; Remove the row if it is in the database otherwise simply reset the unit in edit. ;; ;; DataGridViewRowCancelEventArgs: ;; DataGridViewRow Row - The row the user is deleting. ;; Boolean Cancel - Cancel the addition of the row. (defun user-deleting-row (object event) (let* ((udgv (get-unit-data-grid-view object)) (data-grid-row [%Row event]) (row-index [%Index data-grid-row])) (when (< row-index (database-units-count)) (database-remove-unit-at object row-index)) (when (= row-index (row-in-edit udgv)) (reset-unit-in-edit udgv)))) ;; The callback that is registered with the database. ;; ;; The function will only update the DataGridView if it is not ;; the one that originated the modification in the first place. (let ((locked nil)) (defun data-grid-view-updated (dgv modifier) (let ((dgv-name [%Name dgv]) (modifier-name [%Name modifier])) (unless locked (setf locked t) (when (not (equal dgv-name modifier-name)) (let ((rows [%Rows dgv])) (setf [%RowCount dgv] 1) (setf [%RowCount dgv] (+ (database-units-count) 1)))) [Invalidate dgv] (setf locked nil))))) ;; ------------------------------------------------------------------------ ;; Main ;; Create a new text box column. (defun create-column (index name heading) (let ((col (rdnzl:new "DataGridViewTextBoxColumn"))) (setf [%DisplayIndex col] index [%Name col] name [%HeaderText col] heading) col)) ;; Create a DataGridView with three columns; Code, Name and Description. ;; It is placed in Virtual mode and all of the callbacks are hooked up. (defun create-units-editor (name) (let ((dgv (rdnzl:new "DataGridView"))) (setf [%VirtualMode dgv] t [%Dock dgv] [$DockStyle.Left] [%Text dgv] "DataGridView Experiment" [%Name dgv] name [%RowHeadersVisible dgv] t) [Add [%Columns dgv] (create-column 0 "code" "Code")] [Add [%Columns dgv] (create-column 1 "name" "Name")] [Add [%Columns dgv] (create-column 2 "description" "Description")] (when (> (database-units-count) 0) [Add [%Rows dgv] (database-units-count)]) [+CellValueNeeded dgv (rdnzl:new "DataGridViewCellValueEventHandler" #'cell-value-needed)] [+CellValuePushed dgv (rdnzl:new "DataGridViewCellValueEventHandler" #'cell-value-pushed)] [+NewRowNeeded dgv (rdnzl:new "DataGridViewRowEventHandler" #'new-row-needed)] [+UserDeletingRow dgv (rdnzl:new "DataGridViewRowCancelEventHandler" #'user-deleting-row)] [+CancelRowEdit dgv (rdnzl:new "QuestionEventHandler" #'cancel-row-edit)] [+RowDirtyStateNeeded dgv (rdnzl:new "QuestionEventHandler" #'row-dirty-state-needed)] [+RowValidated dgv (rdnzl:new "DataGridViewCellEventHandler" #'row-validated)] dgv)) ;; Call this function to run the application. (defun run-multiple-datagridview-experiment () (let ((form (rdnzl:new "Form")) (splitter (rdnzl:new "Splitter")) (dgv1 (create-units-editor "V1")) (dgv2 (create-units-editor "V2"))) (setf [%Text form] "DataGridView Experiment" [%Dock splitter] [$DockStyle.Left]) (new-unit-data-grid-view dgv1) (database-add-observer #'data-grid-view-updated dgv1) (new-unit-data-grid-view dgv2) (database-add-observer #'data-grid-view-updated dgv2) (setf [%Dock dgv1] [$DockStyle.Fill] [%Size form] (rdnzl::new "Size" 750 250) [%Size dgv1] (rdnzl::new "Size" 350 200) [%Size dgv2] (rdnzl::new "Size" 350 200)) [Add [%Controls form] dgv1] [Add [%Controls form] splitter] [Add [%Controls form] dgv2] [Application.Run form])) (defun reset () (setf *database-units* nil *database-observers* nil *unit-views* nil)) (run-multiple-datagridview-experiment) From edi at agharta.de Tue Mar 18 23:20:30 2008 From: edi at agharta.de (Edi Weitz) Date: Wed, 19 Mar 2008 00:20:30 +0100 Subject: [rdnzl-devel] C# pointer comparison. In-Reply-To: <1205881981.6868.49.camel@localhost> (Matthew O'Connor's message of "Tue, 18 Mar 2008 23:13:01 +0000") References: <1205881981.6868.49.camel@localhost> Message-ID: On Tue, 18 Mar 2008 23:13:01 +0000, Matthew O'Connor wrote: > I've got a solution for this. I can't claim to completely understand > what is happening with all of RDNZL but I was able to copy some > other functionality and get what I thought was the correct > implementation. > > What is the best way to have this reviewed, edited and possibly > included in the distribution? Just send it to the list and we'll look at it... :) > Is there a regression suite for RDNZL? I haven't run any exhaustive > tests. No, no regression suite. Just the examples so far. Thanks, Edi. From matthew.oconnor at calyptech.com Wed Mar 19 09:58:44 2008 From: matthew.oconnor at calyptech.com (Matthew O Connor) Date: Wed, 19 Mar 2008 20:58:44 +1100 Subject: [rdnzl-devel] C# pointer comparison. References: <1205881981.6868.49.camel@localhost> Message-ID: Edi, I've attached the files I touched along with a little test application. I basically added a function to the ffi.lisp file call same-objects that takes two C# references from lisp and returns true if they are the same underlying C# object. I added the respective code to DotNetReference and DotNetContainer as suggested. I don't know if what I did fits with the overall architecture. Please modify it to match any guidelines that you have. Let me know if there are any problems. Regards, Matthew -------------- next part -------------- A non-text attachment was scrubbed... Name: winmail.dat Type: application/ms-tnef Size: 8528 bytes Desc: not available URL: From edi at agharta.de Tue Mar 25 16:38:40 2008 From: edi at agharta.de (Edi Weitz) Date: Tue, 25 Mar 2008 17:38:40 +0100 Subject: [rdnzl-devel] C# pointer comparison. In-Reply-To: (Matthew O. Connor's message of "Sat, 15 Mar 2008 11:32:55 +1100") References: Message-ID: Sorry for coming back to this a bit late. I'll probably add the code you submitted to RDNZL anyway as it might turn out to be useful in specific cases, but note that there's a "portable" way to solve your problem. Simply replace this line > (if (pointers-equal x [%reference x]) with (if [Equals x [%reference x]] That should do the trick. Cheers, Edi. From edi at agharta.de Tue Mar 25 17:10:29 2008 From: edi at agharta.de (Edi Weitz) Date: Tue, 25 Mar 2008 18:10:29 +0100 Subject: [rdnzl-devel] New release 0.12.2 (Was: Rdnzl Problem) In-Reply-To: (Iver Odin Kvello's message of "Fri, 7 Mar 2008 17:26:54 +0100") References: <47A3A901.8000108@gmail.com> Message-ID: On Fri, 7 Mar 2008 17:26:54 +0100, "Iver Odin Kvello" wrote: > So I've tried writing some documentation for this now finally, here > goes: Thanks again. And I've finally added this to the "official" RDNZL documentation. I also added links to Matthews's DataGridView examples. No code changes, though. From edi at agharta.de Tue Mar 25 17:16:09 2008 From: edi at agharta.de (Edi Weitz) Date: Tue, 25 Mar 2008 18:16:09 +0100 Subject: [rdnzl-devel] C# pointer comparison. In-Reply-To: (Edi Weitz's message of "Tue, 25 Mar 2008 17:38:40 +0100") References: Message-ID: On Tue, 25 Mar 2008 17:38:40 +0100, Edi Weitz wrote: > I'll probably add the code you submitted to RDNZL anyway as it might > turn out to be useful in specific cases But wait. Looking at this more closely, I see that you're instantiating the objects (which you could do directly from DotNetContainer without going through DotNetReference, BTW) and then comparing them with ==. Is that the right way to do it? That's not a rhetorical question. I don't know the right answer, so I'd like to discuss it here. Does == have different semantics from Equals? Would it make sense to directly compare the pointers? (As we already have Equals "for free", I'm thinking of something like Common Lisp's EQ - a function which is usually not what you want but which might be useful if you know what you're doing.) Comments welcome. From matthew.oconnor at calyptech.com Tue Mar 25 23:03:24 2008 From: matthew.oconnor at calyptech.com (Matthew O'Connor) Date: Tue, 25 Mar 2008 23:03:24 +0000 Subject: [rdnzl-devel] C# pointer comparison. In-Reply-To: References: Message-ID: <1206486205.6192.37.camel@localhost> I am just beginning to discover the numerous different types of equals within Common Lisp so I can't comment on them. I can probably comment on the C# ones however. I think that by default '==' will default to the 'Equals' function. In C# however you can overload the '==' operator and make it work differently. An example of this might be an primary key comparison on objects stored in a database table. So you make '==' look at the key slot and return true or false based on this equality. So I think the short answer is for the default behaviour they are exactly the same but you can overload the operator at which point they will be different. Cheers, Matthew From matthew.oconnor at calyptech.com Thu Mar 27 09:39:42 2008 From: matthew.oconnor at calyptech.com (Matthew O Connor) Date: Thu, 27 Mar 2008 20:39:42 +1100 Subject: [rdnzl-devel] Menus Experiment Message-ID: Hi all, This time I have a little menus experiment. Again all you should have to do is change the path to RDNZL itself. Have fun! Regards, Matthew ;; ======================================================================== ;; Menus Experiment. ;; ;; Matthew O'Connor ;; ;; In this experiment I create some menus. I link the menus back to ;; callbacks when they are pressed and even control the state of the 'Save' ;; menu item by pressing the 'Save' or 'Save As' menu items. ;; ;; As usual there is no warranty. Use at your own peril. I am relatively ;; new to Lisp so you are warned. ;; NOTE: You need to modify this to point to the location of RDNZL on your system. (load "../RDNZL/rdnzl-0.12.0/load.lisp") (rdnzl:enable-rdnzl-syntax) (rdnzl:import-types "System.Windows.Forms" "Application" "Form" "MenuStrip" "ToolStripMenuItem" "ToolStripSeparator" "MessageBox" "MessageBoxButtons" "MessageBoxIcon") (rdnzl:use-namespace "System.Windows.Forms") ;; Display a message in a message box. (defun message(text &optional (caption "Message")) [MessageBox.Show text caption ;; we want the message box to have just the "OK" button [$MessageBoxButtons.OK] ;; We want an exclamation mark as the icon. [$MessageBoxIcon.Information]] nil) ;; Create a menu item and add it to the supplied menu strip. (defun create-menu (menu-strip-item x) (let ((menu (rdnzl:new "ToolStripMenuItem" (second x)))) ;; Bind the menu to the sybmol. (setf (symbol-value (first x)) menu) ;; Add the callback if one is supplied. (if (cddr x) [+Click menu (rdnzl:new "System.EventHandler" (car (cddr x)))]) (if (cdddr x) (setf [%Image menu] (rdnzl:property "Resources" (car (cdddr x))))) ;; Add the menu item to the parent. [Add [%DropDownItems menu-strip-item] menu])) ;; Create a menu splitter and add it to the supplied menu strip. (defun create-splitter (menu-strip-item) [Add [%DropDownItems menu-strip-item] (rdnzl:new "ToolStripSeparator")]) ;; Add either a menu item or a splitter to the supplied menu strip. (defun add-menus (menu-strip-item menus) (mapcar #'(lambda (x) (if (= (length x) 1) (create-splitter menu-strip-item) (create-menu menu-strip-item x))) menus)) ;; Build the menus. (defun build-menus (menu-strip menus) (mapcar #'(lambda (x) (let ((menu (rdnzl:new "ToolStripMenuItem" (second x)))) ;; Bind the menu to the symbol (setf (symbol-value (first x)) menu) ;; Take care of the children. (add-menus menu (third x)) ;; Add the menu to the MenuStrip. [Add [%Items menu-strip] menu])) menus)) ;; Callbacks. (defun file-new-clicked (object event) (message "file-new-clicked!!")) (defun file-open-clicked (object event) (message "file-open-clicked!!")) ;; Disable the Save menu item. (defun file-save-clicked (object event) (message "Disabling Save!") (setf [%Enabled menu-file-save] nil)) ;; Enable the Save menu item. (defun file-save-as-clicked (object event) (message "Enabling Save!") (setf [%Enabled menu-file-save] t)) ;; Exit the application (defun file-exit-clicked (object event) [Application.Exit]) (defun edit-undo-clicked (object event) (message "edit-undo-clicked!!")) (defun edit-redo-clicked (object event) (message "edit-redo-clicked!!")) ;; Create my experimental menu system. (defun create-menus (menu-strip) (setf menus '((menu-file "File" ((menu-file-new "New" file-new-clicked) (menu-file-open "Open" file-open-clicked) (menu-file-save "Save" file-save-clicked) (menu-file-save-as "Save As" file-save-as-clicked) (menu-file-splitter) (menu-file-exit "Exit" file-exit-clicked))) (menu-edit "Edit" ((menu-edit-undo "Undo" edit-undo-clicked) (menu-edit-redo "Redo" edit-redo-clicked))) (menu-help "Help"))) (build-menus menu-strip menus)) (defun run-menus-experiment () (let ((menu-strip (rdnzl:new "MenuStrip")) (form (rdnzl:new "Form"))) (setf [%Text form] "Menus Experiment") [Add [%Controls form] menu-strip] (create-menus menu-strip) [Application.Run form])) (run-menus-experiment) (rdnzl:disable-rdnzl-syntax) -------------- next part -------------- An HTML attachment was scrubbed... URL: