TweetFollow Us on Twitter

Java Components
Volume Number:12
Issue Number:10
Column Tag:Java Workshop

Your Own Java Components

Extending the Canvas class

By Andrew Downs

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

This article assumes you know what Java is and how object-oriented programming works at a conceptual level. You should also be familiar with C syntax, as that is the basis of Java syntax. This project was developed with Roaster™, the first Macintosh Java integrated development environment (IDE). Roaster is a stable product, easy to use, and priced competitively.

Java provides the Canvas class for developers creating their own Graphical User Interface (GUI) objects. We extend the Canvas class to create a GUI design tool applet. It shows how to create objects that function primarily as data containers, similar to structs in C. This is a useful intermediate step for programmers who are learning object-oriented programming. This article also shows the addition of methods that set and return the object variable values. We will use the data and methods to draw an outline of our object, and move it around inside a window. Our project will run as a Java applet.

Stretching Canvas

When you hear the word canvas, you probably think of what painters paint on. In this article, we will build upon that metaphor. First, some background information is in order...

Java’s Abstract Windows Toolkit consists of classes. (See Figure 1.) The Component class defines a generic GUI primitive; classes like Button, Choice, and Canvas are subclassed from Component. (Component is an abstract class, which is a class that is used not to instatiate objects, but to group similar classes together.)

The Component class defines many useful properties (that’s Java for class variables) for a generic object, and methods (Java for functions) for getting and setting the values of those properties. These properties include the bounds, color, text, and numerous others. When you create a class that extends a class, your new class inherits the properties and methods of the parent class. (The terms “extend” and “subclass” are synonymous.) You can override (redefine) methods inherited from the parent class, and you can also define new methods and properties. (The exception to this inheritance rule are classes and methods declared using the final keyword, which indicates that the class cannot be inherited or the method overridden. )

Figure 1. Class Hierarchy

Canvas is a subclass of Component (which is itself a subclass of Object, the topmost Java class), provided for programmers who wish to subclass from Component. If you want to create and manipulate your own components, do not subclass Component; extend Canvas instead. A Canvas is a Component that does no default drawing or event handling on its own. By subclassing Canvas, you will have access to Component’s properties and methods.

In our project, we extend the Canvas class with two subclasses. First, we create a backdrop to draw on, called the BackdropCanvas class. Unlike many drawing surfaces, this backdrop plays an active role in our project; it has its own properties and methods. Second, we create a generic object class, called the DecafObject class, which forms the basis of the GUI objects we will create and draw on the backdrop. This generic object class will also have its own properties and methods.

We need to determine how to construct our applet, and what it will include. The general steps we will follow are:

• define the frame (window) that will hold our backdrop

• create/initialize an instance of our backdrop

• define the objects to display on our backdrop (both properties and methods)

• create/initialize the objects we will draw on our backdrop

To make things more interesting, we will also create a separate frame that will function as our “tool palette”. This tool palette will contain buttons that let us control objects on our backdrop, popup menus that let us set the colors of objects, and a button to let us gracefully exit our applet.

Figure 2 shows the relationships between the Java source code files that comprise our project. Each file contains one class definition. Demo.java, the file in the center of the diagram, is our actual applet definition; the Demo class extends the Applet class. The arrows indicate that most activity flows through the Demo class. The ToolPalette class houses the definition for our tool palette. When a user clicks on one of the buttons or popup menus on the tool palette, the ToolPalette class gets the selected item, then passes control to Demo. Demo then takes appropriate action by passing information to methods in other classes.

BackdropCanvas is our backdrop class. When the user requests that a new object be created (by clicking on “Add Button...” or “Add Panel”), ToolPalette informs Demo, which in turn informs BackdropCanvas. BackdropCanvas then creates a new instance of DecafObject, the class that contains our generic object information.

BackdropCanvas also handles drawing DecafObjects and maintains information about the drawing environment. The DecafObjects exist as a doubly-linked list, which means that each object contains a reference to the object before and after it. BackdropCanvas maintains a reference to the first object created. When the backdrop need redrawing, it retrieves a reference to the first object, it obtains that object’s properties, draws the outline, then gets the reference to the next object. Many of the methods in the BackdropCanvas class use this approach to walk the list of objects.

Figure 2. Class/file relationships for this project.

Figure 3 shows our project definition within Roaster. The files listed are the source files, each ending in a .java extension, that contain the classes our applet uses. Each file contains one class declared with the keyword public.

The AppletViewer.class file is supplied with Roaster. The arrow next to AppletViewer.class indicates that it is selected as the startup file. The startup file must contain a main() method. When you select “Run”, Roaster automatically launches Roaster Applet Runner, which then reads the selected startup file

Figure 3. Our project definition within Roaster.

(AppletViewer.class), using its main() method to setup the display and input/output environment for our applet.

The startup file can be selected through the “Startup class name” field in the Preferences dialog. Setting this field to “mac/applet/AppletViewer” assigns AppletViewer.class as the default startup file for new projects.

Applet Initialization

Our applet is defined in the class named Demo. Listing 1 shows the entire Demo class definition (contained in the source file Demo.java). At the top of the file we list the Java classes we want to import, which is similar to #include-ing header files in C. We then define our applet class, which is called Demo. It is declared public, and is an extension of the Java Applet class. (All applets must extend the Applet class.) Within the Demo class, we reserve space for several variables which refer to instances of our other classes. We also create the variable myDemo, with which our applet can refer to itself.

Once our class variables are declared, we call the init() method to set up the class. In init(), we create a frame (our applet’s main window, for you non-Java types out there). In this frame (or over it), we will stretch our backdrop on which we draw. We also set up our tool palette, and the “Add Button” dialog, which remains hidden until called for. I will describe these additional class definitions shortly.

Following init() are our applet’s other methods. When looking through the methods, remember that the Demo class serves as the focal point for requests made from our ToolPalette. User requests get passed from the Tool Palette to Demo, which then calls the appropriate methods in theBackdropCanvas to draw and change objects. Such an architecture gives us the flexibility to reuse these classes in other programs. You may wish to refer back to Figure 2 to clarify the class relationships.

To give you an idea of what our applet looks like, Figure 4 shows our applet running in Roaster Applet Runner. The method Demo.init() has just finished, so each of the Demo class variables that refer to other classes has been instantiated (assigned values). The tool palette is shown to the left of our backdrop frame (which contains theBackdropCanvas). Both frames are selectable and moveable.


Figure 4. Our applet running in the Roaster Applet Runner.

The BackdropCanvas class

We now need to define our BackdropCanvas class, which functions as our backdrop. Let us start with some properties. What do we need? Here is a short list:

• a way to reference the objects we will create later

• a way to keep track of the colors for our new objects and our BackdropCanvas

• the state of the mouse

• the cursor location, relative to the bounding rectangle of any selected object

In addition to properties, we need to add some methods for controlling the painting of objects on our backdrop and for handling mouse-clicks.

Listing 2 shows our BackdropCanvas class. The import statements at the top of the file are the same for all our class files. We then define our BackdropCanvas class as public, and as an extension of the Java Canvas class. Within BackdropCanvas we reserve space for several variables which refer to instances of our objects. (I will describe these objects later in this article.) We also keep track of the background color for our BackdropCanvas, and the state of the mouse (up or down, within a grow region, etc.).

If you remember, the init() method from our Demo class creates a new instance of our BackdropCanvas class and stores the reference in the variable theBackdropCanvas. Creating a new BackdropCanvas is accomplished through a constructor method called BackdropCanvas(). If you look through the constructor methods for our other classes, you will notice that the constructor method has the same name as the class, and may or may not take arguments. The constructor method for BackdropCanvas sets the default background color for the backdrop.

Following BackdropCanvas() are the other methods for this class. When looking through the methods, remember that BackdropCanvas serves as a relay point for object actions requested through our Demo class. These methods are primarily used to draw and change objects.

The DecafObject class

Keep in mind that we intend to paint on our BackdropCanvas. Naturally, we need something to paint. Let’s create another subclass of Canvas, this time called DecafObject. The DecafObject class will be our object class. When we want to create a new object, we create a new instance of the DecafObject class.

Listing 3 shows our DecafObject class. We need to make sure our variables get initialized properly for each new object. We also add two methods for each variable: one for getting its value, one for setting its value. This getting and setting code can get long, it’s true, but it gives you some flexibility to change variable names and determine what actually goes on “behind the scenes” in the DecafObject class. The alternative, accessing the DecafObject variables directly from other classes without going through methods, becomes difficult to maintain as your code grows and your data structures change.

Purists may ask why we did not use the Component method Component.bounds() to get the bounding rectangle of our DecafObject. The answer is that I had some trouble screening mouseDown events within the BackdropCanvas and sending the events to individual DecafObjects for handling. The object coordinates did not relate properly to the BackdropCanvas.

Instead, I decided to handle the mouseDown events only within the BackdropCanvas. To accomplish this, we maintain a separate bounding rectangle for each DecafObject. The coordinates of this bounding rectangle are always calculated in relation to the origin (upper-left corner) of the BackdropCanvas. The BackdropCanvas retrieves this bounding rectangle for each DecafObject to pin down the recipient of the mouseDown. This may not be the best solution, but it works.

The ToolPalette class

[For a tutorial on layouts and panels, see this month’s Getting Started.]

The tool palette is used to control our applet. Refer to Listing 4 for the class definition, and to Figure 5 to see it running. We will use a simple frame containing four buttons and two popups: the buttons Add Panel, Add Button, Delete Item, and Quit, a popup to select an item, and a popup to select an item’s color.

We use panels to group interface components. Each panel has an associated layout. The Java Layout class organizes interface elements within the panel, based on the layout you specify.

We create two panels. On the first, we place our buttons; on the second, we place our popups. We then create the appropriate buttons and popups and add them to the appropriate panels. We then add the panels to the frame, theToolPalette.

We use a GridLayout for both of the panels. The GridLayout allows us to specify the number of rows and columns the layout contains, making it easy to align the buttons and popup menus. This gives a neat, if rigid, appearance. We can add space between items in the grid by either: a) adding a blank label instead of a button or popup menu, or b) specifying hgap and vgap parameters when creating the GridLayout. (The terms hgap and vgap refer to the horizontal and vertical gaps between items in the grid.) We use the first method for pColorPalette to add a blank row between the popup menus.

ToolPalette contains many string and label variables with hardcoded values. Macintosh programmers know how to use resources for text-based items. Text resources can be changed easily when modifying the application or porting it to another language. No such mechanism currently exists in Java; there are not even the C language #define macros that allow you to define all your string constants in one place. To translate a Java applet into another language, someone needs to go through your sources, manually editing each text string in you code.

There is a solution. We could create a class comprising variables declared static final to use as constants throughout our applet. The keyword static indicates that one copy of the variable will be shared by all instances of the class, and final indicates that the variable cannot be overridden by a subclass (and thus modified). Another benefit: we would not have to actually create an instance of such a class in order to reference its variables. An example of this kind of class is the Java System class, which defines variables and methods that can be used by an applet without explicitly creating an instance of the System class (Anuff, 1996, and Ritchey, 1995).

ToolPalette has a method for handling action() events. In our applet, action() events are generated when the user selects one of the buttons or popup items on our tool palette. The parent class, Frame, has an action() method, which the programmer is supposed to override with his own code.

Inside our action() method, we check to see if a button was clicked or a popup item selected, and if so, call the appropriate method in the Demo class. If you look at the Demo methods called from our action() method, you will observe that they often call methods in the BackdropCanvas class. Remember from earlier in this article that the Demo class serves as a focal point for handling user interaction: control passes from the tool palette to the applet (myDemo), then to theBackdropCanvas or another frame if appropriate.

Aside from ToolPalette(), there is one other method in this class that gets called externally (from other classes): doGetColor(). This method returns the values associated with the popup menus (which are stored in theItemColorArray). It takes as a parameter an integer specifying the item (of the object). It returns an integer representing the color of the item. The actual matching of the integer to the corresponding color (i.e. a 1 corresponds to Color.green) is performed by the calling method.

Figure 5. Our applet after adding a panel.

The NewButtonFrame class

We need a way to prompt for a button label when the user selects “Add Button...” from the tool palette. We will use a simple frame containing a text entry field and two buttons: OK and Cancel. This frame will serve as our new button dialog. We need only the label text because:

• the default location for the new button is hardcoded in the DecafObject class definition

• the color is selectable using the popup menu on the tool palette

NewButtonFrame is shown in Listing 5 and in Figure 6. In this class, we first create a text field and label, then a panel on which to place them. Next, we create our OK and Cancel buttons, and a panel to contain them. Once the panels are created and the appropriate components (text field, label, and buttons) added, we add the panels to theNewButtonFrame.

This class also has a method for handling action() events. Once again, we override the action() method of the parent class, Frame. Inside our action() method, we check to see if a button was clicked. In response to Cancel, we simply hide the Frame. In response to OK, we retrieve the contents of the text field, send the text off to Demo for adding to the button, then hide the Frame.

There is one method in NewButtonFrame that gets called externally: doSetupDefaults(). This method clears the text string in the text input field. It is called from Demo just before displaying the Frame.

Figure 6. NewButtonFrame in action.

Figure 7. Our applet with two panels and two buttons.

Improvements

There are several ways to improve our code. One way is to put more of the initialization and drawing methods into the DecafObject class, and let each object handle everything relating to itself.

A second way involves limiting the scope of our object data and methods. Currently, most of the methods within each class are declared public, but some of them could be private. Private data and methods would better protect a class against inappropriate calls from outside it.

A third way involves managing our objects using existing classes. We can eliminate the need for our doubly-linked list by making use of the Vector class, which uses a list to keep track of objects. If we create an instance of the Vector class, we can use the methods Vector.addElement(), Vector.removeElement(), and Vector.elementAt() to add, remove, and retrieve objects from this list. Of course, there are several variations of these methods for finer control.

Since access to our DecafObjects is straightforward (thanks to our doubly-linked list), we can also support writing each object’s properties to a file. Maybe in a future article...

Using Roaster

This article was written using Developer Release 1.1, a stable but incomplete version of Roaster. Roaster provides a familiar project-based environment for developers to work in. You create a new project, then add the appropriate files to it. You can select one or more files for compiling, and/or make the project to produce runnable code. There are also menu choices for clearing and updating the method list for each class file. Also, Roaster has a Message Window which displays compile errors. Double-clicking on an error takes you to the offending line of code.

Roaster preferences include the categories of Editor, Text Styles, Compiler, and Project. You can use font, style, size and/or color to distinguish between comments, reserved words, and the remainder of your code. Roaster includes both the javac (Sun) and Roaster compilers, although NI recommends using the javac compiler for now, since it is more stable than the current Roaster compiler. This should soon change; NI is working to remove the bugs from its compiler, which is faster than the javac compiler.

Occasionally I found it necessary to compile the files within projects based on their hierarchical relationship. That is, source files containing classes that were imported into other classes, but not vice versa, needed to be compiled first. Other times I found it necessary to remove the class files from the project directory, then remake the project.

The more RAM you have, the better. I was very happy with the performance of both Roaster and Applet Runner on a PowerMac 7100/90 with 16 MB of RAM. It also performs reasonably well on a PowerMac 6100/60 with 8 MB of RAM.

I look forward to being able to create standalone (self-contained, double-clickable) applications with Roaster Pro. It is possible to make our applet into an application by adding a main() method to our Demo class, then selecting Demo as the startup file. This eliminates the need to include AppletViewer.class in our project. The Roaster FAQ discusses in more detail how to do this. You can obtain the FAQ using the Roaster URL listed at the end of this article.

Writing applets is slightly simpler, although Roaster Applet Runner requires that we create a simple HTML document that contains a reference to our applet (refer to Listing 6). This information is provided in the Roaster documentation. As noted previously, we also need to set AppletViewer.class as our startup file in the project definition.

Natural Intelligence has provided a stable, easy-to-use product which should set the standard for Java development environments on the Macintosh.

Credits

Thank you to John Dhabolt of Natural Intelligence for proofreading this article and suggesting several enhancements.

Bibliography and References

Anuff, Ed. Java Sourcebook. Wiley Computer Publishing (1996), pp. 89-92.

Ritchey, Tim. Programming with Java! New Riders Publishing (1995), pp. 183-4.

URLs

Class files: http://www1.omi.tulane.edu/adowns/MacTech/source/

Demo applet: http://www1.omi.tulane.edu/adowns/MacTech/demo/

Current version of the Decaf applet:

http://www1.omi.tulane.edu/adowns/decaf/

Roaster product information: http://www.roaster.com/

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Tidy Up 5.3.7 - Find duplicate files and...
Tidy Up is a full-featured duplicate finder and disk-tidiness utility. Features: Supports Lightroom: it is now possible to search and collect duplicates directly in the Lightroom library. Multiple... Read more
Pinegrow 5.97 - Mockup and design web pa...
Pinegrow (was Pinegrow Web Designer) is desktop app that lets you mockup and design webpages faster with multi-page editing, CSS and LESS styling, and smart components for Bootstrap, Foundation,... Read more
BlueStacks 4.210.0 - Run Android applica...
BlueStacks App Player lets you run your Android apps fast and fullscreen on your Mac. Feature comparison chart How to install Bluestacks on your Mac Go to MacUpdate and click the green "Download"... Read more
WhatsApp 2.2027.10 - Desktop client for...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
Art Text 4.0.1 - $29.99
Art Text is graphic design software specifically tuned for lettering, typography, text mockups and various artistic text effects. Supplied with a great variety of ready to use styles and materials,... Read more
Adobe Dreamweaver CC 2020 20.2 - Build w...
Dreamweaver CC 2020 is available as part of Adobe Creative Cloud for as little as $20.99/month (or $9.99/month if you're a previous Dreamweaver customer). Adobe Dreamweaver CC 2020 allows you to... Read more
Adobe Acrobat DC 20.009.20074 - Powerful...
Acrobat DC is available only as a part of Adobe Creative Cloud, and can only be installed and/or updated through Adobe's Creative Cloud app. Adobe Acrobat DC with Adobe Document Cloud services is... Read more
beaTunes 5.2.10 - Organize your music co...
beaTunes is a full-featured music player and organizational tool for music collections. How well organized is your music library? Are your artists always spelled the same way? Any R.E.M. vs REM?... Read more
DiskCatalogMaker 8.1.5 - Catalog your di...
DiskCatalogMaker is a simple disk management tool which catalogs disks. Simple, light-weight, and fast Finder-like intuitive look and feel Super-fast search algorithm Can compress catalog data for... Read more
Meteorologist 3.4.1 - Popular weather ap...
Meteorologist is a simple interface to weather provided by weather.com. It provides the ability to show the weather in the main menu bar, displaying more detail in a pop-up menu, whose contents are... Read more

Latest Forum Discussions

See All

Steam Link Spotlight - Disco Elysium
Steam Link Spotlight is a feature where we look at PC games that play exceptionally well using the Steam Link app. Our last entry was Signs of the Sojourner Read about how it plays using Steam Link over here. | Read more »
Distract Yourself With These Great Mobil...
There’s a lot going on right now, and I don’t really feel like trying to write some kind of pithy intro for it. All I’ll say is lots of people have been coming together and helping each other in small ways, and I’m choosing to focus on that as I... | Read more »
Pokemon Go's July Community Day wil...
Pokemon Go developers have announced the details concerning the upcoming Gastly Community Day. This particular event was selected by the players of the game after the Gas Pokemon came in second place after a poll that decided which Pokemon would... | Read more »
Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Detective Di is a point-and-click murder...
Detective Di is a point-and-click murder mystery set in Tang Dynasty-era China. You'll take on the role of China's best-known investigator, Di Renjie, as he solves a series of grisly murders that will ultimately lead him on a collision course with... | Read more »
Dissidia Final Fantasy Opera Omnia is se...
Dissidia Final Fantasy Opera Omnia, one of Square Enix's many popular mobile RPGs, has announced a plethora of in-game events that are set to take place over the summer. This will include several rewards, Free Multi Draws and more. [Read more] | Read more »
Sphaze is a neat-looking puzzler where y...
Sphaze is a neat-looking puzzler where you'll work to guide robots through increasingly elaborate mazes. It's set in a visually distinct world that's equal parts fantasy and sci-fi, and it's finally launched today for iOS and Android devices. [... | Read more »
Apple Arcade is in trouble
Yesterday, Bloomberg reported that Apple is disappointed in the performance of Apple Arcade and will be shifting their approach to the service by focusing on games that can retain subscribers and canceling other upcoming releases that don't fit... | Read more »
Pixel Petz, an inventive platform for de...
Pixel Petz has built up a sizeable player base thanks to its layered, easy-to-understand creative tools and friendly social experience. It revolves around designing, trading, and playing with a unique collection of pixel art pets, and it's out now... | Read more »
The King of Fighters Allstar's late...
The King of Fighters ALLSTAR, Netmarble's popular action RPG, has once again been updated with a plethora of new content. This includes battle cards, events and 21 new fighters, which increases the already sizeable roster even more. [Read more] | Read more »

Price Scanner via MacPrices.net

$219 Apple AirPods Pro are back at Verizon, s...
Verizon has Apple AirPods Pro on sale again for a limited time for $219.99 on their online store. Their price is $30 off Apple’s MSRP, and it’s the lowest price we’ve seen for AirPods Pro. Available... Read more
Apple’s $779 13″ MacBook Air deal returns to...
Apple has clearance, Certified Refurbished, 2019 13″ MacBook Airs available again starting at $779. Each MacBook features a new outer case, comes with a standard Apple one-year warranty, and is... Read more
$200 13″ MacBook Pro discounts are back at Am...
Amazon has 2020 13″ 2.0GHz MacBook Pros on sale again today for $150-$200 off Apple’s MSRP. Shipping is free. Be sure to purchase the MacBook Pro from Amazon, rather than a third-party seller, and... Read more
Deal Alert! Apple AirPods with Wireless Charg...
Sams Club has Apple AirPods with Wireless Charging Case on sale on their online store for only $149.98 from July 6, 2020 to July 9, 2020. Their price is $50 off Apple’s MSRP, and it’s the lowest... Read more
Xfinity Mobile promo: Apple iPhone XS models...
Take $300 off the purchase of any Apple iPhone XS model at Xfinity Mobile while supplies last. Service plan required: – 64GB iPhone XS: $599.99 save $300 – 256GB iPhone XS: $749.99 save $300 – 512GB... Read more
New July 2020 promo at US Cellular: Switch an...
US Cellular has introduced a new July 2020 deal offering free 64GB Apple iPhone 11 smartphones to customers opening a new line of service. No trade-in required, and discounts are applied via monthly... Read more
Apple offers up to $400 Education discount on...
Apple has launched their Back to School promotion for 2020. They will include one free pair Apple AirPods (with charging case) with the purchase of a MacBook Air, MacBook Pro, iMac, or iMac Pro (Mac... Read more
July 4th Sale: Woot offers wide range of Macs...
Amazon-owned Woot is blowing out a wide range of Apple Macs and iPads for July 4th staring at $279 and ranging up to just over $1000. Models vary from older iPads and 11″ MacBook Airs to some newer... Read more
Apple Pro Display XDR with Nano-Texture Glass...
Abt Electronics has Apple’s new 32″ Pro Display XDR model with the nano-texture glass in stock and on sale today for up to $144 off MSRP. Shipping is free: – Pro Display XDR (nano-texture glass): $... Read more
New 2020 Mac mini on sale for up to $100 off...
Amazon has Apple’s new 2020 Mac minis on sale today for $40-$100 off MSRP with prices starting at $759. Shipping is free: – 2020 4-Core Mac mini: $759 $40 off MSRP – 2020 6-Core Mac mini: $998.99 $... Read more

Jobs Board

Physical Therapist Assistant - *Apple* Hill...
Physical Therapist Assistant - Apple Hill Rehab - Full Time Tracking Code 62519 Job Description General Summary: Under the direct supervision of a licensed Physical Read more
Operating Room Assistant, *Apple* Hill Surg...
Operating Room Assistant, Apple Hill Surgical Center - Full Time, Day Shift, Monday - Saturday availability required Tracking Code 62363 Job Description Operating Read more
Perioperative RN - ( *Apple* Hill Surgical C...
Perioperative RN - ( Apple Hill Surgical Center) Tracking Code 60593 Job Description Monday - Friday - Full Time Days Possible Saturdays General Summary: Under the Read more
Product Manager, *Apple* Commercial Sales -...
Product Manager, Apple Commercial Sales Austin, TX, US Requisition Number:77652 As an Apple Product Manager for the Commercial Sales team at Insight, you Read more
*Apple* Mac Product Engineer - Barclays (Uni...
Apple Mac EngineerWhippany, NJ Support the development and delivery of solutions, products, and capabilities into the Barclays environment working across technical Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.