1 module main;
2 
3 /**
4  * Imports.
5  */
6 import std.algorithm;
7 import std.array;
8 import std.datetime;
9 import std.file;
10 import tkd.tkdapplication;
11 
12 /**
13  * Sample application.
14  */
15 class Application : TkdApplication
16 {
17 	/**
18 	 * Widgets we need access to on the class level.
19 	 */
20 	private Entry _entry;
21 	private ProgressBar _progressBar;
22 	private Canvas _canvas;
23 	private Entry _fontEntry;
24 	private Entry _colorEntry;
25 	private Entry _directoryEntry;
26 	private Entry _openFileEntry;
27 	private Entry _saveFileEntry;
28 	private Entry _messageEntry;
29 
30 	/**
31 	 * Open the font dialog.
32 	 *
33 	 * Params:
34 	 *     args = The callback args.
35 	 */
36 	private void openFontDialog(CommandArgs args)
37 	{
38 		auto dialog = new FontDialog("Choose a font")
39 			.setCommand(delegate(CommandArgs args){
40 				this._fontEntry.setValue(args.dialog.font);
41 			})
42 			.show();
43 	}
44 
45 	/**
46 	 * Open the color dialog.
47 	 *
48 	 * Params:
49 	 *     args = The callback args.
50 	 */
51 	private void openColorDialog(CommandArgs args)
52 	{
53 		auto dialog = new ColorDialog("Choose a color")
54 			.setInitialColor(Color.beige)
55 			.show();
56 		this._colorEntry.setValue(dialog.getResult());
57 	}
58 
59 	/**
60 	 * Open the directory dialog.
61 	 *
62 	 * Params:
63 	 *     args = The callback args.
64 	 */
65 	private void openDirectoryDialog(CommandArgs args)
66 	{
67 		auto dialog = new DirectoryDialog("Choose a directory")
68 			.setDirectoryMustExist(true)
69 			.show();
70 		this._directoryEntry.setValue(dialog.getResult());
71 	}
72 
73 	/**
74 	 * Open the open file dialog.
75 	 *
76 	 * Params:
77 	 *     args = The callback args.
78 	 */
79 	private void openOpenFileDialog(CommandArgs args)
80 	{
81 		auto dialog = new OpenFileDialog()
82 			.setMultiSelection(true)
83 			.setDefaultExtension(".dmo")
84 			.addFileType("{{All files} {*}}")
85 			.addFileType("{{D language files} {.d .di}}")
86 			.addFileType("{{HTML files} {.htm .html}}")
87 			.addFileType("{{Text files} {.txt}}")
88 			.setInitialDirectory("~")
89 			.setInitialFile("file-to-open.dmo")
90 			.show();
91 		this._openFileEntry.setValue(dialog.getResults().join(" "));
92 	}
93 
94 	/**
95 	 * Open the save file dialog.
96 	 *
97 	 * Params:
98 	 *     args = The callback args.
99 	 */
100 	private void openSaveFileDialog(CommandArgs args)
101 	{
102 		auto dialog = new SaveFileDialog()
103 			.setConfirmOverwrite(true)
104 			.setDefaultExtension(".dmo")
105 			.addFileType("{{All files} {*}}")
106 			.addFileType("{{D language files} {.d .di}}")
107 			.addFileType("{{HTML files} {.htm .html}}")
108 			.addFileType("{{Text files} {.txt}}")
109 			.setInitialDirectory("~")
110 			.setInitialFile("file-to-save.dmo")
111 			.show();
112 		this._saveFileEntry.setValue(dialog.getResult());
113 	}
114 
115 	/**
116 	 * Open the message dialog.
117 	 *
118 	 * Params:
119 	 *     args = The callback args.
120 	 */
121 	private void openMessageDialog(CommandArgs args)
122 	{
123 		auto dialog = new MessageDialog()
124 			.setMessage("Lorem ipsum dolor sit amet.")
125 			.setDetailMessage("Nunc at aliquam arcu. Sed eget tellus ligula.\nSed egestas est et tempus cursus.")
126 			.setType(MessageDialogType.okcancel)
127 			.show();
128 		this._messageEntry.setValue(dialog.getResult());
129 	}
130 
131 	/**
132 	 * Mark a point in the canvas to drag from.
133 	 *
134 	 * Params:
135 	 *     args = The callback args.
136 	 */
137 	private void mark(CommandArgs args)
138 	{
139 		this._canvas.setScanMark(args.event.x, args.event.y);
140 	}
141 
142 	/**
143 	 * Drag the canvas to reposition the contents.
144 	 *
145 	 * Params:
146 	 *     args = The callback args.
147 	 */
148 	private void drag(CommandArgs args)
149 	{
150 		this._canvas.scanDragTo(args.event.x, args.event.y);
151 	}
152 
153 	/**
154 	 * Show the about box.
155 	 */
156 	private void showAbout(CommandArgs args)
157 	{
158 		auto dialog = new MessageDialog("About")
159 			.setMessage("Tkd Showcase")
160 			.setDetailMessage("An showcase Tkd application demonstrating menus, widgets and dialogs.\n\nThe possiblities are endless.")
161 			.show();
162 	}
163 
164 	/**
165 	 * Create the menu.
166 	 */
167 	private void createMenu()
168 	{
169 		auto menuBar = new MenuBar(this.mainWindow);
170 
171 		auto fileMenu = new Menu(menuBar, "File", 0)
172 			.addEntry(new EmbeddedPng!("cancel.png"), "Quit", &this.exitApplication, ImagePosition.left, "Ctrl-Q");
173 
174 		auto helpMenu = new Menu(menuBar, "Help", 0)
175 			.addEntry(new EmbeddedPng!("help.png"), "About...", &this.showAbout, ImagePosition.left, "F1");
176 	}
177 
178 	/**
179 	 * Create the widget pane for the note book.
180 	 *
181 	 * Returns:
182 	 *     The widget pane.
183 	 */
184 	private Frame createWidgetPane()
185 	{
186 		auto widgetPane = new Frame();
187 
188 		auto entryLabelFrame = new LabelFrame(widgetPane, "Text Entry")
189 			.pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true);
190 			auto entry1 = new Text(entryLabelFrame)
191 				.setWidth(0)
192 				.setHeight(3)
193 				.appendText("Nullam sapien lectus, aliquet sit amet quam et, sagittis porttitor dolor.")
194 				.pack(5, 0, GeometrySide.bottom, GeometryFill.both, AnchorPosition.northWest, true);
195 			this._entry = new Entry(entryLabelFrame)
196 				.pack(5, 0, GeometrySide.left, GeometryFill.x, AnchorPosition.northWest, true);
197 			auto entry3 = new SpinBox(entryLabelFrame)
198 				.setData(["foo", "bar", "baz", "qux"])
199 				.setWrap(true)
200 				.setWidth(5)
201 				.pack(5, 0, GeometrySide.left);
202 			auto entry4 = new ComboBox(entryLabelFrame)
203 				.setData(["Option 1", "Option 2", "Option 3"])
204 				.setValue("Option 1")
205 				.pack(5, 0, GeometrySide.left, GeometryFill.x, AnchorPosition.northWest, true);
206 
207 		auto rangeLabelFrame = new LabelFrame(widgetPane, "Progress & Scale")
208 			.pack(10, 0, GeometrySide.bottom, GeometryFill.both, AnchorPosition.center);
209 			this._progressBar = new ProgressBar(rangeLabelFrame)
210 				.setMaxValue(10)
211 				.setValue(4)
212 				.pack(5, 0, GeometrySide.top, GeometryFill.x, AnchorPosition.center, true);
213 			auto scale = new Scale(rangeLabelFrame)
214 				.setFromValue(10)
215 				.setToValue(0)
216 				.setCommand(delegate(CommandArgs args){
217 					auto scale = cast(Scale)args.element;
218 					this._progressBar.setValue(scale.getValue());
219 				})
220 				.setValue(4)
221 				.pack(5, 0, GeometrySide.top, GeometryFill.x, AnchorPosition.center, true);
222 
223 		auto buttonLabelFrame = new LabelFrame(widgetPane, "Buttons")
224 			.pack(10, 0, GeometrySide.left, GeometryFill.both, AnchorPosition.center, true);
225 			auto button1 = new Button(buttonLabelFrame, "Text button")
226 				.pack(5);
227 			auto button2 = new Button(buttonLabelFrame, new EmbeddedPng!("thumbnail.png"), "Image button", ImagePosition.left)
228 				.pack(5);
229 			auto buttonMenu = new Menu()
230 				.addEntry("Option 1", delegate(CommandArgs args){})
231 				.addEntry("Option 2", delegate(CommandArgs args){})
232 				.addEntry("Option 3", delegate(CommandArgs args){});
233 			auto button3 = new MenuButton(buttonLabelFrame, "Menu button", buttonMenu)
234 				.pack(5);
235 
236 		auto checkBoxLabelFrame = new LabelFrame(widgetPane, "Check buttons")
237 			.pack(10, 0, GeometrySide.left, GeometryFill.both, AnchorPosition.center, true);
238 			auto checkButton1 = new CheckButton(checkBoxLabelFrame, "Option 1")
239 				.check()
240 				.pack(5);
241 			auto checkButton2 = new CheckButton(checkBoxLabelFrame, "Option 2")
242 				.pack(5);
243 			auto checkButton3 = new CheckButton(checkBoxLabelFrame, "Option 3")
244 				.pack(5);
245 
246 		auto radioLabelFrame = new LabelFrame(widgetPane, "Radio buttons")
247 			.pack(10, 0, GeometrySide.left, GeometryFill.both, AnchorPosition.center, true);
248 			auto radioButton1 = new RadioButton(radioLabelFrame, "Option 1")
249 				.setSelectedValue("1")
250 				.select()
251 				.pack(5);
252 			auto radioButton2 = new RadioButton(radioLabelFrame, "Option 2")
253 				.setSelectedValue("2")
254 				.pack(5);
255 			auto radioButton3 = new RadioButton(radioLabelFrame, "Option 3")
256 				.setSelectedValue("3")
257 				.pack(5);
258 
259 		return widgetPane;
260 	}
261 
262 	/**
263 	 * Create the paned pane for the note book.
264 	 *
265 	 * Returns:
266 	 *     The paned pane.
267 	 */
268 	private Frame createPanedPane()
269 	{
270 		auto panedPane = new Frame();
271 
272 			auto panedWindow = new PanedWindow(panedPane);
273 
274 				auto rows = new TreeViewRow(["Computer"], true, ["computer"]);
275 				rows.children ~= new TreeViewRow(["Documents"], true, ["folder"]);
276 					rows.children[0].children ~= new TreeViewRow(["Important notes.txt"], true, ["file"]);
277 					rows.children[0].children ~= new TreeViewRow(["The D Programming Language.pdf"], true, ["pdf"]);
278 				rows.children ~= new TreeViewRow(["Pictures"], true, ["folder"]);
279 					rows.children[1].children ~= new TreeViewRow(["Gary and Tessa.jpg"], true, ["image"]);
280 				rows.children ~= new TreeViewRow(["Videos"], true, ["folder"]);
281 					rows.children[2].children ~= new TreeViewRow(["Carlito's Way (1993).mpg"], true, ["video"]);
282 					rows.children[2].children ~= new TreeViewRow(["Aliens (1986).mpg"], true, ["video"]);
283 
284 				auto tree1 = new TreeView(panedWindow)
285 					.setHeading("Directory listing")
286 					.setTag("computer", new EmbeddedPng!("computer.png"))
287 					.setTag("folder", new EmbeddedPng!("folder.png"))
288 					.setTag("file", new EmbeddedPng!("page.png"))
289 					.setTag("pdf", new EmbeddedPng!("page_white_acrobat.png"))
290 					.setTag("video", new EmbeddedPng!("film.png"))
291 					.setTag("image", new EmbeddedPng!("images.png"))
292 					.addRow(rows);
293 
294 				auto tree2 = new TreeView(panedWindow)
295 					.setHeading("Film")
296 					.setWidth(250)
297 					.addColumn(new TreeViewColumn("Year").setWidth(20))
298 					.addColumn(new TreeViewColumn("IMDB ranking").setWidth(50))
299 					.addRow(new TreeViewRow(["The Shawshank Redemption", "1994", "1"]))
300 					.addRow(new TreeViewRow(["The Godfather", "1972", "2"]))
301 					.addRow(new TreeViewRow(["The Godfather: Part II", "1974", "3"]))
302 					.addRow(new TreeViewRow(["The Dark Knight", "2008", "4"]))
303 					.addRow(new TreeViewRow(["Pulp Fiction", "1994", "5"]))
304 					.addRow(new TreeViewRow(["The Good, the Bad and the Ugly", "1966", "6"]))
305 					.addRow(new TreeViewRow(["Schindler's List", "1993", "7"]))
306 					.addRow(new TreeViewRow(["Angry Men", "1957", "8"]))
307 					.addRow(new TreeViewRow(["The Lord of the Rings: The Return of the King", "2003", "9"]))
308 					.addRow(new TreeViewRow(["Fight Club", "1999", "10"]));
309 
310 		panedWindow
311 			.addPane(tree1).setPaneWeight(0, 1)
312 			.addPane(tree2).setPaneWeight(1, 1)
313 			.pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true);
314 
315 		return panedPane;
316 	}
317 
318 	/**
319 	 * Create the canvas pane for the note book.
320 	 *
321 	 * Returns:
322 	 *     The canvas pane.
323 	 */
324 	private Frame createCanvasPane()
325 	{
326 		auto canvasPane = new Frame();
327 
328 			auto container = new Frame(canvasPane)
329 				.pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true);
330 
331 			container.configureGeometryColumn(0, 1);
332 			container.configureGeometryRow(0, 1);
333 
334 				this._canvas = new Canvas(container, Color.bisque)
335 					.setCursor(Cursor.hand1)
336 					.setScrollRegion(-300, -250, 610, 500)
337 					.addItem(new CanvasRectangle([10, 10, 200, 100]).addTag("tagged"))
338 					.addItem(new CanvasArc([10, 110, 110, 210], CanvasArcStyle.pie, Color.paleGreen))
339 					.addItem(new CanvasImage([210, 10], new EmbeddedPng!("thumbnail.png")))
340 					.addItem(new CanvasImage([260, 10], new EmbeddedGif!("thumbnail.gif")))
341 					.addItem(new CanvasLine([120, 110, 200, 110, 200, 160]).setArrowPosition(CanvasLineArrow.last))
342 					.addItem(new CanvasOval([10, 170, 200, 245], Color.rosyBrown3))
343 					.addItem(new CanvasPolygon([220, 80, 320, 80, 300, 120, 240, 120], Color.mediumPurple))
344 					.addItem(new CanvasText([30, 192], "Tkd Canvas", Color.white).setFont("{Times New Roman} 22 bold"))
345 					.addItem(new CanvasWidget([220, 140], new Button("Embedded\nWidget", new EmbeddedPng!("error.png"))).setWidth(100).setHeight(100))
346 					.addTagConfig(new CanvasTagConfig("tagged").setFillColor(Color.salmon))
347 					.setXView(0.25)
348 					.setYView(0.24)
349 					.bind("<ButtonPress-1>", &this.mark)
350 					.bind("<B1-Motion>", &this.drag);
351 
352 				auto xscroll = new XScrollBar(container)
353 					.attachWidget(this._canvas)
354 					.grid(0, 1, 0, 0, 1, 1, "esw");
355 
356 				auto yscroll = new YScrollBar(container)
357 					.attachWidget(this._canvas)
358 					.grid(1, 0, 0, 0, 1, 1, "nes");
359 
360 				this._canvas
361 					.attachXScrollBar(xscroll)
362 					.attachYScrollBar(yscroll)
363 					.grid(0, 0, 0, 0, 1, 1, "nesw");
364 
365 		return canvasPane;
366 	}
367 
368 	/**
369 	 * Create the dialog pane for the note book.
370 	 *
371 	 * Returns:
372 	 *     The dialog pane.
373 	 */
374 	private Frame createDialogPane()
375 	{
376 		auto dialogPane = new Frame();
377 
378 			auto modalLabelFrame = new LabelFrame(dialogPane, "Modal")
379 				.configureGeometryColumn(1, 1)
380 				.pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true);
381 
382 				auto colorButton = new Button(modalLabelFrame, new EmbeddedPng!("color_swatch.png"), "Color", ImagePosition.left)
383 					.setCommand(&this.openColorDialog)
384 					.grid(0, 0, 10);
385 
386 				this._colorEntry = new Entry(modalLabelFrame)
387 					.grid(1, 0, 10, 0, 1, 1, "ew");
388 
389 				auto directoryButton = new Button(modalLabelFrame, new EmbeddedPng!("chart_organisation.png"), "Directory", ImagePosition.left)
390 					.setCommand(&this.openDirectoryDialog)
391 					.grid(0, 1, 10);
392 
393 				this._directoryEntry = new Entry(modalLabelFrame)
394 					.setWidth(40)
395 					.grid(1, 1, 10, 0, 1, 1, "ew");
396 
397 				auto fileOpenButton = new Button(modalLabelFrame, new EmbeddedPng!("folder_page.png"), "Open File", ImagePosition.left)
398 					.setCommand(&this.openOpenFileDialog)
399 					.grid(0, 2, 10);
400 
401 				this._openFileEntry = new Entry(modalLabelFrame)
402 					.setWidth(40)
403 					.grid(1, 2, 10, 0, 1, 1, "ew");
404 
405 				auto fileSaveButton = new Button(modalLabelFrame, new EmbeddedPng!("disk.png"), "Save File", ImagePosition.left)
406 					.setCommand(&this.openSaveFileDialog)
407 					.grid(0, 3, 10);
408 
409 				this._saveFileEntry = new Entry(modalLabelFrame)
410 					.setWidth(40)
411 					.grid(1, 3, 10, 0, 1, 1, "ew");
412 
413 				auto messageButton = new Button(modalLabelFrame, new EmbeddedPng!("comment.png"), "Message", ImagePosition.left)
414 					.setCommand(&this.openMessageDialog)
415 					.grid(0, 4, 10);
416 
417 				this._messageEntry = new Entry(modalLabelFrame)
418 					.setWidth(40)
419 					.grid(1, 4, 10, 0, 1, 1, "ew");
420 
421 			auto nonModalLabelFrame = new LabelFrame(dialogPane, "Non Modal")
422 				.configureGeometryColumn(1, 1)
423 				.pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true);
424 
425 				auto fontButton = new Button(nonModalLabelFrame, new EmbeddedPng!("style.png"), "Font", ImagePosition.left)
426 					.setCommand(&this.openFontDialog)
427 					.grid(0, 0, 10);
428 
429 				this._fontEntry = new Entry(nonModalLabelFrame)
430 					.setWidth(40)
431 					.grid(1, 0, 10, 0, 1, 1, "ew");
432 
433 		return dialogPane;
434 	}
435 
436 	/**
437 	 * Set up the key bindings for the application.
438 	 */
439 	private void setUpKeyBindings()
440 	{
441 		this.mainWindow.bind("<F1>", &this.showAbout);
442 		this.mainWindow.bind("<Control-q>", &this.exitApplication);
443 	}
444 
445 	/**
446 	 * Initialise the user interface.
447 	 */
448 	override protected void initInterface()
449 	{
450 		this.mainWindow.setTitle("Tkd Showcase");
451 		this.mainWindow.setMinSize(550, 560);
452 		this.mainWindow.setDefaultIcon([new EmbeddedPng!("tkicon.png")]);
453 
454 		this.mainWindow.setProtocolCommand(WindowProtocol.deleteWindow, delegate(CommandArgs args){
455 			this.showAbout(args);
456 			this.exitApplication(args);
457 		});
458 
459 		this.mainWindow.setIdleCommand(delegate(CommandArgs args){
460 			this._entry.setValue(Clock.currTime().toLocalTime().toSimpleString().findSplitBefore(".")[0]);
461 			this.mainWindow.setIdleCommand(args.callback, 1000);
462 		});
463 
464 		this.createMenu();
465 
466 		auto noteBook   = new NoteBook();
467 		auto widgetPane = this.createWidgetPane();
468 		auto panedPane  = this.createPanedPane();
469 		auto canvasPane = this.createCanvasPane();
470 		auto dialogPane = this.createDialogPane();
471 
472 		noteBook
473 			.addTab("Widgets", widgetPane).setTabImage(0, new EmbeddedPng!("layout_content.png"), ImagePosition.left)
474 			.addTab("Panes", panedPane).setTabImage(1, new EmbeddedPng!("application_tile_horizontal.png"), ImagePosition.left)
475 			.addTab("Canvas", canvasPane).setTabImage(2, new EmbeddedPng!("shape_ungroup.png"), ImagePosition.left)
476 			.addTab("Dialogs", dialogPane).setTabImage(3, new EmbeddedPng!("application_double.png"), ImagePosition.left)
477 			.pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true);
478 
479 		auto exitButton = new Button("Exit")
480 			.setCommand(&this.exitApplication)
481 			.pack(5);
482 
483 		auto sizeGrip = new SizeGrip(this.mainWindow)
484 			.pack(0, 0, GeometrySide.bottom, GeometryFill.none, AnchorPosition.southEast);
485 
486 		this.setUpKeyBindings();
487 	}
488 
489 	/**
490 	 * Exit the application.
491 	 *
492 	 * Params:
493 	 *     args = The command arguments.
494 	 */
495 	private void exitApplication(CommandArgs args)
496 	{
497 		this.exit();
498 	}
499 
500 }
501 
502 /**
503  * Main entry point.
504  *
505  * Params:
506  *     args = An array of command line arguments passed to this program.
507  */
508 void main(string[] args)
509 {
510 	auto app = new Application();
511 	app.run();
512 }