// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: InkClipboard.cs // Simple Ink Clipboard with Selection Sample Application // // This program demonstrates how to copy and paste ink into // another application such as Microsoft Word or Microsoft Paint. // It also allows the user to copy a selection of strokes and // paste the result into this application's existing ink object. // // The following clipboard formats are available: // // Metafile // Enhanced Metafile // Bitmap // Ink Serialized Format // Text Ink // Sketch Ink // TextInk and SketchInk are two flavors of Ink OLE Embeddable Objects // that Microsoft Word uses for ink that represents text or drawing // respectively. // It is possible to paste Ink Serialized Format, Text Ink, and // Sketch Ink into existing ink. // // In addition to the clipboard, this sample also illustrates how // to select strokes with a lasso. The user can move selected // strokes and modify their drawing attributes. This functionality // is a subset of the selection functionality already provided by // the InkOverlay control; it is implemented here for illustrative // purposes. // // The features used are: InkCollector, Ink Clipboard support, and // HitTest with lasso. // //-------------------------------------------------------------------------- using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Collections; using System.Windows.Forms; // The Ink namespace, which contains the Tablet PC Platform API using Microsoft.Ink; namespace Microsoft.Samples.TabletPC.InkClipboard { /// /// Enumeration of all possible application modes: /// /// Ink: The user is drawing new strokes /// LassoSelection: The user is selecting strokes with a lasso /// MoveInk: The user is moving selected strokes (this is not a menu item) /// /// public enum ApplicationMode { Ink, LassoSelection, MoveInk } /// /// The InkClipboard with Selection Sample Application form class /// public class InkClipboard : System.Windows.Forms.Form { // --------------- Constants --------------- // Declare constants for the pen widths used by this application. // Note that these constants are in high metric units (1 unit = .01mm) private const float ThinInkWidth = 10; private const float MediumInkWidth = 100; private const float ThickInkWidth = 200; // Declare constant for the size of the border around selected strokes private const int SelectedInkWidthIncrease = 105; // Declare constant for the size of a lasso point private const int DotSize = 6; // Declare constant for the spacing between lasso points private const int DotSpacing = 7; // Declare constant for the padding size of the rectangle that bounds // the selected strokes. private const int SelectionRectBuffer = 8; // Declare constant for the lasso hit test percent (specifies how much // of the stroke must fall within the lasso in order to be selected). private const float LassoPercent = 50; // The index for the x and y packet values. This index is used // to retrieve the lasso point data in the Packets event handler. private const int XPacketIndex = 0; private const int YPacketIndex = 1; // --------------- Fields --------------- // Declare the Ink Collector object private InkCollector myInkCollector = null; // The current application mode: inking, selecting, or moving the selection private ApplicationMode applicationMode = ApplicationMode.Ink; // The points in the selection lasso private ArrayList lassoPoints = null; // The array of rectangle selection handles private PictureBox[] selectionHandles; // The rectangle that bounds the selected strokes private Rectangle selectionRect = Rectangle.Empty; // The strokes that have been selected by the lasso private Strokes selectedStrokes = null; // The location where the move started (used when moving selected strokes) private Point startMoveLocation = Point.Empty; // The coordinates of the last lasso point drawn (empty if no dot has been drawn) private Point lastDrawnLassoDot = Point.Empty; // Declare the colors used in the selection lasso private Color dotEdgeColor = Color.White; private Color dotColor = SystemColors.Highlight; private Color connectorColor = Color.Black; // Declare the pens used to draw the selection lasso private Pen connectorPen = null; private Pen dotEdgePen = null; private Pen dotPen = null; // --------------- Constructor --------------- #region Standard Template Code private System.Windows.Forms.MenuItem miClear; private System.Windows.Forms.MenuItem miExit; private System.Windows.Forms.MenuItem miMainEdit; private System.Windows.Forms.MenuItem miCut; private System.Windows.Forms.MenuItem miCopy; private System.Windows.Forms.MenuItem miPaste; private System.Windows.Forms.MenuItem miInk; private System.Windows.Forms.MenuItem miMainFormat; private System.Windows.Forms.MenuItem miISF; private System.Windows.Forms.MenuItem miMetafile; private System.Windows.Forms.MenuItem miBMP; private System.Windows.Forms.MenuItem miEMF; private System.Windows.Forms.MenuItem miSInk; private System.Windows.Forms.MenuItem miTInk; private System.Windows.Forms.MenuItem miNoOLE; private System.Windows.Forms.MenuItem miThin; private System.Windows.Forms.MenuItem miMedium; private System.Windows.Forms.MenuItem miThick; private System.Windows.Forms.MenuItem miRed; private System.Windows.Forms.MenuItem miGreen; private System.Windows.Forms.MenuItem miBlue; private System.Windows.Forms.MenuItem miBlack; private System.Windows.Forms.PictureBox rightBottomHandle; private System.Windows.Forms.PictureBox centerBottomHandle; private System.Windows.Forms.PictureBox leftBottomHandle; private System.Windows.Forms.PictureBox rightCenterHandle; private System.Windows.Forms.PictureBox leftCenterHandle; private System.Windows.Forms.PictureBox rightTopHandle; private System.Windows.Forms.PictureBox centerTopHandle; private System.Windows.Forms.PictureBox leftTopHandle; private System.Windows.Forms.MenuItem miLassoSelect; private System.Windows.Forms.MainMenu mainMenu; private System.Windows.Forms.MenuItem miInkSeparator; private System.Windows.Forms.MenuItem miEditSeparator; private System.Windows.Forms.MenuItem miFormatSeparator; private System.Windows.Forms.MenuItem miStyle; private System.Windows.Forms.MenuItem miWidth; private System.Windows.Forms.MenuItem miColor; private System.Windows.Forms.MenuItem miMainAction; /// /// Required designer variable. /// private System.ComponentModel.Container components = null; #endregion public InkClipboard() { #region Standard Template Code // // Required for Windows Form Designer support // InitializeComponent(); #endregion } #region Standard Template Code /// /// Clean up any resources being used. /// protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } if (myInkCollector != null) { myInkCollector.Dispose(); } } base.Dispose( disposing ); } #endregion #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(InkClipboard)); this.mainMenu = new System.Windows.Forms.MainMenu(); this.miMainAction = new System.Windows.Forms.MenuItem(); this.miClear = new System.Windows.Forms.MenuItem(); this.miInkSeparator = new System.Windows.Forms.MenuItem(); this.miExit = new System.Windows.Forms.MenuItem(); this.miMainEdit = new System.Windows.Forms.MenuItem(); this.miCut = new System.Windows.Forms.MenuItem(); this.miCopy = new System.Windows.Forms.MenuItem(); this.miPaste = new System.Windows.Forms.MenuItem(); this.miEditSeparator = new System.Windows.Forms.MenuItem(); this.miLassoSelect = new System.Windows.Forms.MenuItem(); this.miInk = new System.Windows.Forms.MenuItem(); this.miMainFormat = new System.Windows.Forms.MenuItem(); this.miISF = new System.Windows.Forms.MenuItem(); this.miMetafile = new System.Windows.Forms.MenuItem(); this.miEMF = new System.Windows.Forms.MenuItem(); this.miBMP = new System.Windows.Forms.MenuItem(); this.miFormatSeparator = new System.Windows.Forms.MenuItem(); this.miSInk = new System.Windows.Forms.MenuItem(); this.miTInk = new System.Windows.Forms.MenuItem(); this.miNoOLE = new System.Windows.Forms.MenuItem(); this.miStyle = new System.Windows.Forms.MenuItem(); this.miWidth = new System.Windows.Forms.MenuItem(); this.miThin = new System.Windows.Forms.MenuItem(); this.miMedium = new System.Windows.Forms.MenuItem(); this.miThick = new System.Windows.Forms.MenuItem(); this.miColor = new System.Windows.Forms.MenuItem(); this.miRed = new System.Windows.Forms.MenuItem(); this.miGreen = new System.Windows.Forms.MenuItem(); this.miBlue = new System.Windows.Forms.MenuItem(); this.miBlack = new System.Windows.Forms.MenuItem(); this.rightBottomHandle = new System.Windows.Forms.PictureBox(); this.centerBottomHandle = new System.Windows.Forms.PictureBox(); this.leftBottomHandle = new System.Windows.Forms.PictureBox(); this.rightCenterHandle = new System.Windows.Forms.PictureBox(); this.leftCenterHandle = new System.Windows.Forms.PictureBox(); this.rightTopHandle = new System.Windows.Forms.PictureBox(); this.centerTopHandle = new System.Windows.Forms.PictureBox(); this.leftTopHandle = new System.Windows.Forms.PictureBox(); this.SuspendLayout(); // // mainMenu // this.mainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.miMainAction, this.miMainEdit, this.miMainFormat, this.miStyle}); // // miMainAction // this.miMainAction.Index = 0; this.miMainAction.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.miClear, this.miInkSeparator, this.miExit}); this.miMainAction.Text = "&Action"; // // miClear // this.miClear.Index = 0; this.miClear.Text = "&Clear"; this.miClear.Click += new System.EventHandler(this.miClear_Click); // // miInkSeparator // this.miInkSeparator.Index = 1; this.miInkSeparator.Text = "-"; // // miExit // this.miExit.Index = 2; this.miExit.Text = "&Exit"; this.miExit.Click += new System.EventHandler(this.miExit_Click); // // miMainEdit // this.miMainEdit.Index = 1; this.miMainEdit.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.miCut, this.miCopy, this.miPaste, this.miEditSeparator, this.miLassoSelect, this.miInk}); this.miMainEdit.Text = "&Edit"; this.miMainEdit.Popup += new System.EventHandler(this.miMainEdit_Popup); // // miCut // this.miCut.Index = 0; this.miCut.Shortcut = System.Windows.Forms.Shortcut.CtrlX; this.miCut.Text = "Cu&t"; this.miCut.Click += new System.EventHandler(this.miCut_Click); // // miCopy // this.miCopy.Index = 1; this.miCopy.Shortcut = System.Windows.Forms.Shortcut.CtrlC; this.miCopy.Text = "&Copy"; this.miCopy.Click += new System.EventHandler(this.miCopy_Click); // // miPaste // this.miPaste.Index = 2; this.miPaste.Shortcut = System.Windows.Forms.Shortcut.CtrlV; this.miPaste.Text = "&Paste"; this.miPaste.Click += new System.EventHandler(this.miPaste_Click); // // miEditSeparator // this.miEditSeparator.Index = 3; this.miEditSeparator.Text = "-"; // // miLassoSelect // this.miLassoSelect.Index = 4; this.miLassoSelect.RadioCheck = true; this.miLassoSelect.Text = "&Select"; this.miLassoSelect.Click += new System.EventHandler(this.miLassoSelect_Click); // // miInk // this.miInk.Checked = true; this.miInk.Index = 5; this.miInk.RadioCheck = true; this.miInk.Text = "&Ink"; this.miInk.Click += new System.EventHandler(this.miInk_Click); // // miMainFormat // this.miMainFormat.Index = 2; this.miMainFormat.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.miISF, this.miMetafile, this.miEMF, this.miBMP, this.miFormatSeparator, this.miSInk, this.miTInk, this.miNoOLE}); this.miMainFormat.Text = "&Format"; // // miISF // this.miISF.Checked = true; this.miISF.Index = 0; this.miISF.Text = "&Ink Serialized Format (ISF)"; this.miISF.Click += new System.EventHandler(this.miISF_Click); // // miMetafile // this.miMetafile.Checked = true; this.miMetafile.Index = 1; this.miMetafile.Text = "&Metafile"; this.miMetafile.Click += new System.EventHandler(this.miMetafile_Click); // // miEMF // this.miEMF.Checked = true; this.miEMF.Index = 2; this.miEMF.Text = "&Enhanced Metafile"; this.miEMF.Click += new System.EventHandler(this.miEMF_Click); // // miBMP // this.miBMP.Checked = true; this.miBMP.Index = 3; this.miBMP.Text = "&Bitmap"; this.miBMP.Click += new System.EventHandler(this.miBMP_Click); // // miFormatSeparator // this.miFormatSeparator.Index = 4; this.miFormatSeparator.Text = "-"; // // miSInk // this.miSInk.Index = 5; this.miSInk.RadioCheck = true; this.miSInk.Text = "&Sketch Ink"; this.miSInk.Click += new System.EventHandler(this.miSInk_Click); // // miTInk // this.miTInk.Checked = true; this.miTInk.Index = 6; this.miTInk.RadioCheck = true; this.miTInk.Text = "&Text Ink"; this.miTInk.Click += new System.EventHandler(this.miTInk_Click); // // miNoOLE // this.miNoOLE.Index = 7; this.miNoOLE.RadioCheck = true; this.miNoOLE.Text = "&No OLE Object Format"; this.miNoOLE.Click += new System.EventHandler(this.miNoOLE_Click); // // miStyle // this.miStyle.Index = 3; this.miStyle.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.miWidth, this.miColor}); this.miStyle.Text = "&Style"; // // miWidth // this.miWidth.Index = 0; this.miWidth.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.miThin, this.miMedium, this.miThick}); this.miWidth.Text = "&Width"; // // miThin // this.miThin.Index = 0; this.miThin.Text = "Thin"; this.miThin.Click += new System.EventHandler(this.miThin_Click); // // miMedium // this.miMedium.Index = 1; this.miMedium.Text = "Medium"; this.miMedium.Click += new System.EventHandler(this.miMedium_Click); // // miThick // this.miThick.Index = 2; this.miThick.Text = "Thick"; this.miThick.Click += new System.EventHandler(this.miThick_Click); // // miColor // this.miColor.Index = 1; this.miColor.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.miRed, this.miGreen, this.miBlue, this.miBlack}); this.miColor.Text = "&Color"; // // miRed // this.miRed.Index = 0; this.miRed.Text = "Red"; this.miRed.Click += new System.EventHandler(this.miRed_Click); // // miGreen // this.miGreen.Index = 1; this.miGreen.Text = "Green"; this.miGreen.Click += new System.EventHandler(this.miGreen_Click); // // miBlue // this.miBlue.Index = 2; this.miBlue.Text = "Blue"; this.miBlue.Click += new System.EventHandler(this.miBlue_Click); // // miBlack // this.miBlack.Index = 3; this.miBlack.Text = "Black"; this.miBlack.Click += new System.EventHandler(this.miBlack_Click); // // rightBottomHandle // this.rightBottomHandle.BackColor = System.Drawing.Color.Gray; this.rightBottomHandle.Cursor = System.Windows.Forms.Cursors.Default; this.rightBottomHandle.Image = ((System.Drawing.Bitmap)(resources.GetObject("rightBottomHandle.Image"))); this.rightBottomHandle.Location = new System.Drawing.Point(281, 256); this.rightBottomHandle.Name = "rightBottomHandle"; this.rightBottomHandle.Size = new System.Drawing.Size(11, 11); this.rightBottomHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.rightBottomHandle.TabIndex = 20; this.rightBottomHandle.TabStop = false; this.rightBottomHandle.Visible = false; // // centerBottomHandle // this.centerBottomHandle.Cursor = System.Windows.Forms.Cursors.Default; this.centerBottomHandle.Image = ((System.Drawing.Bitmap)(resources.GetObject("centerBottomHandle.Image"))); this.centerBottomHandle.Location = new System.Drawing.Point(137, 256); this.centerBottomHandle.Name = "centerBottomHandle"; this.centerBottomHandle.Size = new System.Drawing.Size(9, 9); this.centerBottomHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.centerBottomHandle.TabIndex = 19; this.centerBottomHandle.TabStop = false; this.centerBottomHandle.Visible = false; // // leftBottomHandle // this.leftBottomHandle.BackColor = System.Drawing.Color.Gray; this.leftBottomHandle.Cursor = System.Windows.Forms.Cursors.Default; this.leftBottomHandle.Image = ((System.Drawing.Bitmap)(resources.GetObject("leftBottomHandle.Image"))); this.leftBottomHandle.Location = new System.Drawing.Point(1, 256); this.leftBottomHandle.Name = "leftBottomHandle"; this.leftBottomHandle.Size = new System.Drawing.Size(11, 11); this.leftBottomHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.leftBottomHandle.TabIndex = 18; this.leftBottomHandle.TabStop = false; this.leftBottomHandle.Visible = false; // // rightCenterHandle // this.rightCenterHandle.Cursor = System.Windows.Forms.Cursors.Default; this.rightCenterHandle.Image = ((System.Drawing.Bitmap)(resources.GetObject("rightCenterHandle.Image"))); this.rightCenterHandle.Location = new System.Drawing.Point(281, 128); this.rightCenterHandle.Name = "rightCenterHandle"; this.rightCenterHandle.Size = new System.Drawing.Size(9, 9); this.rightCenterHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.rightCenterHandle.TabIndex = 17; this.rightCenterHandle.TabStop = false; this.rightCenterHandle.Visible = false; // // leftCenterHandle // this.leftCenterHandle.Cursor = System.Windows.Forms.Cursors.Default; this.leftCenterHandle.Image = ((System.Drawing.Bitmap)(resources.GetObject("leftCenterHandle.Image"))); this.leftCenterHandle.Location = new System.Drawing.Point(1, 128); this.leftCenterHandle.Name = "leftCenterHandle"; this.leftCenterHandle.Size = new System.Drawing.Size(9, 9); this.leftCenterHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.leftCenterHandle.TabIndex = 16; this.leftCenterHandle.TabStop = false; this.leftCenterHandle.Visible = false; // // rightTopHandle // this.rightTopHandle.BackColor = System.Drawing.Color.Gray; this.rightTopHandle.Cursor = System.Windows.Forms.Cursors.Default; this.rightTopHandle.Image = ((System.Drawing.Bitmap)(resources.GetObject("rightTopHandle.Image"))); this.rightTopHandle.Location = new System.Drawing.Point(281, 0); this.rightTopHandle.Name = "rightTopHandle"; this.rightTopHandle.Size = new System.Drawing.Size(11, 11); this.rightTopHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.rightTopHandle.TabIndex = 15; this.rightTopHandle.TabStop = false; this.rightTopHandle.Visible = false; // // centerTopHandle // this.centerTopHandle.Cursor = System.Windows.Forms.Cursors.Default; this.centerTopHandle.Image = ((System.Drawing.Bitmap)(resources.GetObject("centerTopHandle.Image"))); this.centerTopHandle.Location = new System.Drawing.Point(137, 0); this.centerTopHandle.Name = "centerTopHandle"; this.centerTopHandle.Size = new System.Drawing.Size(9, 9); this.centerTopHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.centerTopHandle.TabIndex = 14; this.centerTopHandle.TabStop = false; this.centerTopHandle.Visible = false; // // leftTopHandle // this.leftTopHandle.BackColor = System.Drawing.Color.Gray; this.leftTopHandle.Cursor = System.Windows.Forms.Cursors.Default; this.leftTopHandle.Image = ((System.Drawing.Bitmap)(resources.GetObject("leftTopHandle.Image"))); this.leftTopHandle.Location = new System.Drawing.Point(1, 0); this.leftTopHandle.Name = "leftTopHandle"; this.leftTopHandle.Size = new System.Drawing.Size(11, 11); this.leftTopHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.leftTopHandle.TabIndex = 13; this.leftTopHandle.TabStop = false; this.leftTopHandle.Visible = false; // // InkClipboard // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(292, 266); this.Controls.AddRange(new System.Windows.Forms.Control[] { this.rightBottomHandle, this.centerBottomHandle, this.leftBottomHandle, this.rightCenterHandle, this.leftCenterHandle, this.rightTopHandle, this.centerTopHandle, this.leftTopHandle}); this.Menu = this.mainMenu; this.Name = "InkClipboard"; this.Text = "InkClipboard"; this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.InkClipboard_MouseDown); this.Load += new System.EventHandler(this.InkClipboard_Load); this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.InkClipboard_MouseUp); this.Paint += new System.Windows.Forms.PaintEventHandler(this.InkClipboard_Paint); this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.InkClipboard_MouseMove); this.ResumeLayout(false); } #endregion #region Standard Template Code /// /// The main entry point for the application. /// [STAThread] static void Main() { Application.Run(new InkClipboard()); } #endregion // --------------- Form Events --------------- /// /// Event Handler from Form Load Event /// Setup the ink collector for collection /// /// The control that raised the event. /// The event arguments. private void InkClipboard_Load(object sender, System.EventArgs e) { // Set the application mode to inking applicationMode = ApplicationMode.Ink; // Initialize the selection data lassoPoints = new ArrayList(); selectionHandles = new PictureBox[] {leftTopHandle,centerTopHandle,rightTopHandle,leftCenterHandle,rightCenterHandle,leftBottomHandle,centerBottomHandle,rightBottomHandle}; selectionRect = new Rectangle(); selectedStrokes = null; // Create the pens used to draw the lasso selection connectorPen = new Pen(connectorColor); // Pen used to draw dotted lasso connector line connectorPen.DashStyle = DashStyle.Dash; dotEdgePen = new Pen(dotEdgeColor); // Pen used to draw the outer edge of the lasso dot dotPen = new Pen(dotColor); // Pen used to draw the center of the lasso dot // Create a new ink collector and assign it to this form's window myInkCollector = new InkCollector(this.Handle); // Set the ink collector's pen width myInkCollector.DefaultDrawingAttributes.Width = MediumInkWidth; // Turn the ink collector on myInkCollector.Enabled = true; } /// /// Event Handler from Form Paint Event /// /// When no ink is selected, autoredrawing is turned on and /// this method doesn't need to do anything. However, if /// there is a selection, autoredrawing is turned off and /// it is necessary to draw the strokes manually /// (in order to display the strokes as selected). /// /// /// The control that raised the event. /// The event arguments. private void InkClipboard_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { // If AutoRedraw is turned off, perform the drawing manually. if (!myInkCollector.AutoRedraw) { DrawStrokes(e.Graphics); e.Graphics.DrawRectangle(connectorPen,selectionRect); } // If the user is drawing a lasso, repaint the lasso points. if (ApplicationMode.LassoSelection == applicationMode) { DrawLasso(e.Graphics, 0); } } /// /// Event Handler from Form Mouse Down Event /// /// The control that raised the event. /// The event arguments. private void InkClipboard_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { // If the mouse button was pressed down within the // current selection area, update the application mode // to indicate that the user is moving ink and record // the starting location of the move. This value will // be used in MouseMove to determine how much the selected // ink should be moved. if (selectionRect.Contains(e.X,e.Y)) { applicationMode = ApplicationMode.MoveInk; startMoveLocation = new Point(e.X, e.Y); } // Otherwise, if there is a selection and the mouse // is pressed, clear the selection. else if (HasSelection()) { SetSelection(null); } } /// /// Event Handler from Form Mouse Move Event /// /// The control that raised the event. /// The event arguments. private void InkClipboard_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { // If the selected ink is currently being moved... if (applicationMode == ApplicationMode.MoveInk && HasSelection()) { // Compute the ink space distance the mouse has moved and // move the selected strokes correspondingly. Point moveAmount = new Point(e.X-startMoveLocation.X,e.Y-startMoveLocation.Y); using (Graphics g = CreateGraphics()) { myInkCollector.Renderer.PixelToInkSpace(g, ref moveAmount); } selectedStrokes.Move(moveAmount.X, moveAmount.Y); // Record the location of the mouse since the next // move should be relative to it. startMoveLocation.X = e.X; startMoveLocation.Y = e.Y; // Update the selection rectangle SetSelection(selectedStrokes); } else { // If the mouse has entered the rectangular selection // region, disable ink collection and update the cursor // to show that the user can move this region. It is // necessary to disable ink collection since ink should not // flow from the pen while the user is moving the selection. if ( selectionRect.Contains(e.X,e.Y) ) { if (myInkCollector.Enabled && !myInkCollector.CollectingInk) { myInkCollector.Enabled = false; Cursor = System.Windows.Forms.Cursors.SizeAll; } } // Otherwise, enable ink collection and set the cursor // to its default value (if necessary). else if (!myInkCollector.Enabled) { myInkCollector.Enabled = true; Cursor = System.Windows.Forms.Cursors.Arrow; } } } /// /// Event Handler from Form Mouse Up Event /// /// The control that raised the event. /// The event arguments. private void InkClipboard_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { // If the user was moving selected ink and the // mouse is raised, set the application mode back to // its original value. if (applicationMode == ApplicationMode.MoveInk) { if (miLassoSelect.Checked) { applicationMode = ApplicationMode.LassoSelection; } else { applicationMode = ApplicationMode.Ink; } } } /// /// Event Handler from Ink->Clear Menu Item /// /// The control that raised the event. /// The event arguments. private void miClear_Click(object sender, System.EventArgs e) { // Clear the selection and delete all strokes from the ink myInkCollector.Ink.DeleteStrokes(); SetSelection(null); } /// /// Event Handler from Ink->Exit Menu Item /// /// The control that raised the event. /// The event arguments. private void miExit_Click(object sender, System.EventArgs e) { // Although not required, it is good practice to disable // ink collection before exiting myInkCollector.Enabled = false; Application.Exit(); } /// /// Event Handler from Edit Select Event /// /// If the Edit menu is selected, check whether the object /// that is currently on the clipboard can be pasted into /// the ink. Disable the Paste item if it can't be pasted. /// Also, disable Copy and Cut if no ink is selected. /// /// /// Allowable formats: Ink Serialized Format or Embeddable OLE /// Object (Text Ink or Sketch Ink). /// /// /// The control that raised the event. /// The event arguments. private void miMainEdit_Popup(object sender, System.EventArgs e) { miPaste.Enabled = myInkCollector.Ink.CanPaste(); miCopy.Enabled = HasSelection(); miCut.Enabled = HasSelection(); } /// /// Event Handler from Edit->Cut Menu Item /// /// The control that raised the event. /// The event arguments. private void miCut_Click(object sender, System.EventArgs e) { // Use helper method to cut the selection CopyInkToClipboard(InkClipboardModes.Cut); SetSelection(null); } /// /// Event Handler from Edit->Copy Menu Item /// /// The control that raised the event. /// The event arguments. private void miCopy_Click(object sender, System.EventArgs e) { // Use helper method to copy the selection CopyInkToClipboard(InkClipboardModes.Copy); } /// /// Event Handler from Edit->Paste Menu Item /// /// The control that raised the event. /// The event arguments. private void miPaste_Click(object sender, System.EventArgs e) { if (myInkCollector.Ink.CanPaste()) { // Compute the location where the ink should be pasted; // this location should be shifted from the origin // to account for the width of the selection rectangle's handle. Point offset = new Point(leftTopHandle.Width+1,leftTopHandle.Height+1); using (Graphics g = CreateGraphics()) { myInkCollector.Renderer.PixelToInkSpace(g, ref offset); } // Use Ink API to paste the clipboard data into the Ink Strokes pastedStrokes = myInkCollector.Ink.ClipboardPaste(offset); // If the contents of the clipboard were a valid format // (Ink Serialized Format or Embeddable OLE Object) and at // least one stroke was pasted into the ink, use a helper // method to update the stroke selection. Otherwise, // the result will be null and this paste becomes a no-op. if (null != pastedStrokes) { SetSelection(pastedStrokes); } } else { MessageBox.Show("Unable to paste ink."); } } /// /// Event Handler from Edit->Select Menu Item /// /// The control that raised the event. /// The event arguments. private void miLassoSelect_Click(object sender, System.EventArgs e) { if (ApplicationMode.LassoSelection != applicationMode) { // Set the application mode to lasso selection applicationMode = ApplicationMode.LassoSelection; // Lasso strokes should be transparent myInkCollector.DefaultDrawingAttributes.Transparency = 255; // Use helper method to clear the selection SetSelection(null); // Update menu item state miLassoSelect.Checked = true; miInk.Checked = false; // Hook the new packets and stroke events since // these events are needed to implement lasso selection. // These events are only hooked when they are needed to improve // performance. myInkCollector.NewPackets += new InkCollectorNewPacketsEventHandler(myInkCollector_NewPackets); myInkCollector.Stroke += new InkCollectorStrokeEventHandler(myInkCollector_Stroke); } } /// /// Event Handler from Edit->Ink Menu Item /// /// The control that raised the event. /// The event arguments. private void miInk_Click(object sender, System.EventArgs e) { if (applicationMode != ApplicationMode.Ink) { // Set the application mode to ink collection applicationMode = ApplicationMode.Ink; // Ink strokes should be opaque myInkCollector.DefaultDrawingAttributes.Transparency = 0; // Use helper method to clear the selection SetSelection(null); // Update menu item state miLassoSelect.Checked = false; miInk.Checked = true; // Unhook the new packets and stroke events since // these events are only needed to implement lasso selection. // These events are only hooked when they are needed to improve // performance. myInkCollector.NewPackets -= new InkCollectorNewPacketsEventHandler(myInkCollector_NewPackets); myInkCollector.Stroke -= new InkCollectorStrokeEventHandler(myInkCollector_Stroke); } } /// /// Event Handler from Format->ISF Menu Item /// /// The control that raised the event. /// The event arguments. private void miISF_Click(object sender, System.EventArgs e) { miISF.Checked = !miISF.Checked; } /// /// Event Handler from Metafile->ISF Menu Item /// /// The control that raised the event. /// The event arguments. private void miMetafile_Click(object sender, System.EventArgs e) { miMetafile.Checked = !miMetafile.Checked; } /// /// Event Handler from Format->EMF Menu Item /// /// The control that raised the event. /// The event arguments. private void miEMF_Click(object sender, System.EventArgs e) { miEMF.Checked = !miEMF.Checked; } /// /// Event Handler from Format->BMP Menu Item /// /// The control that raised the event. /// The event arguments. private void miBMP_Click(object sender, System.EventArgs e) { miBMP.Checked = !miBMP.Checked; } /// /// Event Handler from Format->Text Ink Menu Item /// /// The control that raised the event. /// The event arguments. private void miTInk_Click(object sender, System.EventArgs e) { miSInk.Checked = false; miTInk.Checked = true; miNoOLE.Checked = false; } /// /// Event Handler from Format->Sketch Ink Menu Item /// /// The control that raised the event. /// The event arguments. private void miSInk_Click(object sender, System.EventArgs e) { miSInk.Checked = true; miTInk.Checked = false; miNoOLE.Checked = false; } /// /// Event Handler from Format->No OLE Object Menu Item /// /// The control that raised the event. /// The event arguments. private void miNoOLE_Click(object sender, System.EventArgs e) { miSInk.Checked = false; miTInk.Checked = false; miNoOLE.Checked = true; } /// /// Event Handler from Style->Color->Red Menu Item /// /// The control that raised the event. /// The event arguments. private void miRed_Click(object sender, System.EventArgs e) { // Use helper method to set the InkCollector and the // selected strokes to the selected color. SetColor(Color.Red); } /// /// Event Handler from Style->Color->Green Menu Item /// /// The control that raised the event. /// The event arguments. private void miGreen_Click(object sender, System.EventArgs e) { // Use helper method to set the InkCollector and the // selected strokes to the selected color. SetColor(Color.Green); } /// /// Event Handler from Style->Color->Blue Menu Item /// /// The control that raised the event. /// The event arguments. private void miBlue_Click(object sender, System.EventArgs e) { // Use helper method to set the InkCollector and the // selected strokes to the selected color. SetColor(Color.Blue); } /// /// Event Handler from Style->Color->Black Menu Item /// /// The control that raised the event. /// The event arguments. private void miBlack_Click(object sender, System.EventArgs e) { // Use helper method to set the InkCollector and the // selected strokes to the selected color. SetColor(Color.Black); } /// /// Event Handler from Style->Width->Thin Menu Item /// /// The control that raised the event. /// The event arguments. private void miThin_Click(object sender, System.EventArgs e) { // Use helper method to set the InkCollector and the // selected strokes to the selected pen width. SetWidth(ThinInkWidth); } /// /// Event Handler from Style->Width->Medium Menu Item /// /// The control that raised the event. /// The event arguments. private void miMedium_Click(object sender, System.EventArgs e) { // Use helper method to set the InkCollector and the // selected strokes to the selected pen width. SetWidth(MediumInkWidth); } /// /// Event Handler from Style->Width->Thick Menu Item /// /// The control that raised the event. /// The event arguments. private void miThick_Click(object sender, System.EventArgs e) { // Use helper method to set the InkCollector and the // selected strokes to the selected pen width. SetWidth(ThickInkWidth); } // --------------- Ink Collector Events --------------- /// /// Event Handler from Ink Collector's NewPackets event /// /// This event is fired when the Ink Collector receives /// new packet data and the user is drawing a lasso. /// In this case, it is necessary to intercept the new packets /// and use them to draw the selection lasso. /// /// The control that raised the event. /// The event arguments. private void myInkCollector_NewPackets(object sender, InkCollectorNewPacketsEventArgs e) { // retrieve the size of each packet int packetDescriptionLength = e.Stroke.PacketDescription.Length; // the index where the new lasso points will be added int newPacketIndex = lassoPoints.Count; using (Graphics g = CreateGraphics()) { // Add each new packet into the lasso points collection for (int i = 0; i < e.PacketCount; i++) { // retrieve the x and y packet values Point pt = new Point(e.PacketData[i * packetDescriptionLength + XPacketIndex], e.PacketData[i * packetDescriptionLength + YPacketIndex]); myInkCollector.Renderer.InkSpaceToPixel(g, ref pt); // If the x or y packet values fall outside of // the drawing area , update them to the nearest // location on the drawing area. This adjustment // prevents the lasso's boundaries from going // beyond the drawing area. if (pt.X<0) { pt.X = 0; } if (pt.X>ClientSize.Width) { pt.X = ClientSize.Width; } if (pt.Y<0) { pt.Y = 0; } if (pt.Y>this.ClientSize.Height) { pt.Y = ClientSize.Height; } // Add the new point into the lasso points collection lassoPoints.Add(pt); } // Draw the updated lasso. In order to improve performance, // only the new lasso packets will be drawn. DrawLasso(g,newPacketIndex); } } /// /// Event Handler from Ink Collector's Stroke event /// /// This event is fired when a new lasso stroke is drawn. /// In this case, it is necessary to update the selected /// strokes information. /// /// The control that raised the event. /// The event arguments. private void myInkCollector_Stroke(object sender, InkCollectorStrokeEventArgs e ) { // This stroke corresponds to the lasso - // cancel it so that it is not added into the ink e.Cancel = true; Strokes hitStrokes = null; // If there are enough lasso points, perform a hit test // to determine which strokes were selected. if (lassoPoints.Count > 2) { // Convert the lasso points from pixels to ink space Point[] inkLassoPoints = (Point[])lassoPoints.ToArray(typeof(Point)); using (Graphics g = CreateGraphics()) { myInkCollector.Renderer.PixelToInkSpace(g, ref inkLassoPoints); } // Perform a hit test on this ink collector's ink to // determine which points were selected by the lasso stroke. // // Note that there is a slight inefficiency here since the // lasso stroke is part of the ink and, therefore, part of the // hit test - even though we don't need it. It would have // been more efficient to remove the stroke from the ink before // calling HitTest. However, it is not good practice to modify // the stroke inside of its own event handler. hitStrokes = myInkCollector.Ink.HitTest(inkLassoPoints, LassoPercent); hitStrokes.Remove(e.Stroke); } // Reset the lasso points lassoPoints.Clear(); lastDrawnLassoDot = Point.Empty; // Use helper method to set the selection SetSelection(hitStrokes); } // --------------- Helper Methods --------------- /// /// Helper method to convert a rectangle in pixel coordinates /// to ink space coordinates. (1 ink space unit = .01mm) /// /// Note that this method works since there is guaranteed not /// to be any rotation in this sample. /// /// The graphics display /// The rectangle to convert private void PixelToInkSpace(Graphics g, ref Rectangle r) { Point leftTop = new Point(r.X,r.Y); Point widthHeight = new Point (r.Width, r.Height); myInkCollector.Renderer.PixelToInkSpace(g, ref leftTop); myInkCollector.Renderer.PixelToInkSpace(g, ref widthHeight); r.Location = leftTop; r.Size = new Size(widthHeight); } /// /// Helper method to convert a rectangle in ink space /// coordinates to pixel coordinates. (1 ink space unit = .01mm) /// /// Note that this method works since there is guaranteed not /// to be any rotation in this sample. /// /// The graphics display /// The rectangle to convert private void InkSpaceToPixel(Graphics g, ref Rectangle r) { Point leftTop = new Point(r.X,r.Y); Point widthHeight = new Point (r.Width, r.Height); myInkCollector.Renderer.InkSpaceToPixel(g, ref leftTop); myInkCollector.Renderer.InkSpaceToPixel(g, ref widthHeight); r.Location = leftTop; r.Size = new Size(widthHeight); } /// /// This method uses the Ink API to copy ink onto /// the clipboard. /// /// The clipboard modes (such as copy or paste) private void CopyInkToClipboard(InkClipboardModes clipboardModes) { // Declare the ink clipboard formats to put on the clipboard InkClipboardFormats formats = new InkClipboardFormats(); // Use selected format menu items to set the clipboard formats if (miISF.Checked) { formats |= InkClipboardFormats.InkSerializedFormat; } if (miMetafile.Checked) { formats |= InkClipboardFormats.Metafile; } if (miEMF.Checked) { formats |= InkClipboardFormats.EnhancedMetafile; } if (miBMP.Checked) { formats |= InkClipboardFormats.Bitmap; } if (miSInk.Checked) { formats |= InkClipboardFormats.SketchInk; } if (miTInk.Checked) { formats |= InkClipboardFormats.TextInk; } // If at least one format was selected, invoke the Ink // API's ClipboardCopy method. Note that selectedStrokes // could be null, but that this is ok - if selectedStrokes // is null, all of the ink is copied. if (formats != InkClipboardFormats.None) { myInkCollector.Ink.ClipboardCopy(selectedStrokes,formats,clipboardModes); } else { MessageBox.Show("No clipboard formats selected"); } } /// /// Sets the selected strokes and selection rectangle. /// /// The strokes that should be selected. If null, the selection becomes empty private void SetSelection(Strokes strokes) { // Tracks whether the rectangle that bounds the selected // strokes should be displayed bool isSelectionVisible = false; // Update the selected strokes collection selectedStrokes = strokes; // If no strokes are selected, set the selection rectangle // to empty if (!HasSelection()) { selectionRect = Rectangle.Empty; } // Otherwise, at least one stroke is selected and it is necessary // to display the selection rectangle. else { isSelectionVisible = true; // Retrieve the bounding box of the strokes selectionRect = selectedStrokes.GetBoundingBox(); using (Graphics g = CreateGraphics()) { InkSpaceToPixel(g, ref selectionRect); } // Pad the selection rectangle so that the selected ink // doesn't overlap with the selection rectangle's handles. selectionRect.Inflate(SelectionRectBuffer, SelectionRectBuffer); // compute the center of the rectangle that bounds the // selected strokes int xAvg = (selectionRect.Right+selectionRect.Left)/2; int yAvg = (selectionRect.Top+selectionRect.Bottom)/2; // Draw the resize handles // top left SetLocation(selectionHandles[0],selectionRect.Left, selectionRect.Top); // top SetLocation(selectionHandles[1],xAvg, selectionRect.Top); // top right SetLocation(selectionHandles[2],selectionRect.Right, selectionRect.Top); // left SetLocation(selectionHandles[3],selectionRect.Left, yAvg); // right SetLocation(selectionHandles[4],selectionRect.Right, yAvg); // bottom left SetLocation(selectionHandles[5],selectionRect.Left, selectionRect.Bottom); // bottom SetLocation(selectionHandles[6],xAvg, selectionRect.Bottom); // bottom right SetLocation(selectionHandles[7],selectionRect.Right, selectionRect.Bottom); } // Set the visibility of each selection handle in the // selection rectangle. If there is no selection, all // handles should be hidden. Otherwise, all handles should // be visible. foreach(PictureBox pb in selectionHandles) { pb.Visible = isSelectionVisible; } // Turn off autoredrawing if there is a selection - otherwise, // the selected ink will not be displayed as selected. myInkCollector.AutoRedraw = !isSelectionVisible; // Since the selection has changed, repaint the screen. Refresh(); } /// /// Helper method to set the location of the selection rectangle /// handle. /// /// The handle's picture box /// The desired x coordinate of the center /// The desired y coordinate of the center public void SetLocation(PictureBox pb, int xCenter, int yCenter) { Point location = new Point(); location.X = xCenter - pb.Width/2; location.Y = yCenter - pb.Height/2; pb.Location = location; } /// /// Helper method to determine if there is a selection /// /// True if there is a selection public bool HasSelection() { return selectedStrokes!=null && selectedStrokes.Count > 0; } /// /// Draws the selection lasso. /// /// Note that this method does NOT draw a point for every piece /// of packet data. Instead, it draws evenly spaced dots that /// fall along the lasso path. /// /// The graphics device /// The index where the new packets begin public void DrawLasso(Graphics g, int newPacketIndex) { // The distance between the current point and the next point int dx, dy; // The distance and angle of the line connecting the current // point and the next point double lineLength; double angle; // The horizontal and vertical spacing between the dots double segSpaceX, segSpaceY; // The total number of dots to draw int totalSegments; // For each lasso point, draw the corresponding lasso dots. // Note that this method does NOT draw a point for every piece // of packet data. Instead, it draws evenly spaced dots that // fall along the lasso path. for (int i = newPacketIndex; i < lassoPoints.Count; i++) { // Always draw the first packet... if (0 == i) { // Update the last drawn lasso dot to this packet lastDrawnLassoDot.X = ((Point)lassoPoints[i]).X; lastDrawnLassoDot.Y = ((Point)lassoPoints[i]).Y; // use a helper method to draw the lasso dot DrawSelectionInkDot(g, lastDrawnLassoDot.X, lastDrawnLassoDot.Y); } else { // Compute the x and y distance between this point and the last drawn // lasso dot dx = ((Point)lassoPoints[i]).X - lastDrawnLassoDot.X; dy = ((Point)lassoPoints[i]).Y - lastDrawnLassoDot.Y; // Compute length of the line between this point and the last drawn // lasso dot lineLength = Math.Sqrt(dx*dx + dy*dy); // If the spacing between this packet and the last drawn lasso // dot is greater than the desired DotSpacing, draw as many // evently spaced lasso points as needed to fill the distance... if (DotSpacing <= lineLength) { // Calculate the angle of the line connecting this point // to the last drawn lasso point. angle = Math.Atan2(dy, dx); // Calculate the x and y components of the dot spacing segSpaceX = DotSpacing * Math.Cos(angle); segSpaceY = DotSpacing * Math.Sin(angle); // Calculate the number of dots we will need to draw. totalSegments = (int)(lineLength/ (double)DotSpacing); // draw the lasso dots... for (int j = 0; j < totalSegments; j++) { // The coordinates of the lasso dot are determined by the // previous lasso dot plus the x and y components of the // dot spacing (computed above). lastDrawnLassoDot.X = (int)(lastDrawnLassoDot.X + segSpaceX); lastDrawnLassoDot.Y = (int)(lastDrawnLassoDot.Y + segSpaceY); // use a helper method to draw the lasso dot DrawSelectionInkDot(g, lastDrawnLassoDot.X, lastDrawnLassoDot.Y); } } } } } /// /// Helper method to draw a lasso seletion dot /// /// The graphics device /// The x coordinate of the center of the dot /// The y coordinate of the center of the dot private void DrawSelectionInkDot(Graphics g, int x, int y) { // // Offset the x and y by 1/2 the dot size (biasing lower-right) // x = x-(DotSize/2); y = y-(DotSize/2); // Draw the inside of the lasso dot g.FillEllipse(dotPen.Brush,x,y,DotSize,DotSize); // Draw the outside edge of the lasso dot g.DrawEllipse(dotEdgePen, x,y, DotSize, DotSize); } /// /// This method is used to render ink when there is /// a selection. /// /// The graphics device private void DrawStrokes(Graphics g) { // First, draw the strokes that are not part of the // selection. These strokes will appear the same // as the InkCollector would normally draw them. Strokes unselectedStrokes = myInkCollector.Ink.Strokes; unselectedStrokes.Remove(selectedStrokes); myInkCollector.Renderer.Draw(g, unselectedStrokes); // Next, draw each selected stroke. A selected stroke // is rendered in white with a selection border. foreach (Stroke selectedStroke in selectedStrokes) { DrawingAttributes da = selectedStroke.DrawingAttributes.Clone(); da.Color = SystemColors.WindowText; da.Width = da.Width+SelectedInkWidthIncrease; myInkCollector.Renderer.Draw(g, selectedStroke, da); da.Color = Color.White; da.Width = da.Width-SelectedInkWidthIncrease; myInkCollector.Renderer.Draw(g, selectedStroke, da); } } /// /// Helper method to set the InkCollector and the /// selected stroke color. /// /// The new color private void SetColor(Color newColor) { myInkCollector.DefaultDrawingAttributes.Color = newColor; // In addition to updating the ink collector, also update // the drawing attributes of all selected strokes. if (HasSelection()) { foreach (Stroke s in selectedStrokes) { s.DrawingAttributes.Color = newColor; } } Refresh(); } /// /// Helper method to set the InkCollector and the /// selected stroke pen width. /// /// The new pen width private void SetWidth(float newWidth) { myInkCollector.DefaultDrawingAttributes.Width = newWidth; // In addition to updating the ink collector, also update // the drawing attributes of all selected strokes. if (HasSelection()) { foreach (Stroke s in selectedStrokes) { s.DrawingAttributes.Width = newWidth; } } Refresh(); } } }