Coding in Delphi and Beyond - Thoughts from David Cornelius https://corneliusconcepts.tech/ en AI Image Generation https://corneliusconcepts.tech/ai-image-generation <span property="dc:title">AI Image Generation</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/miscellaneous" hreflang="en">Miscellaneous</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2024-09-06T06:20:56+00:00" datatype="xsd:dateTime">Thu, 09/05/2024 - 23:20</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p><a href="/sites/default/files/inline-images/GreekRomanSoldierProgramming.png"><img alt="Roman Programmer" data-entity-type="file" data-entity-uuid="8489dbd5-aa8b-4571-8c96-1718143b6b12" height="247" hspace="10" src="/sites/default/files/inline-images/GreekRomanSoldierProgramming.png" width="247" class="align-left" /></a>The fascinating realm of AI image generation has opened a new playground intersecting art, human creativity, and serious back-end computing. It's also opened questions of how we will tell real from fiction as this technology evolves and becomes even more sophisticated than it is already. For now, I'm just having some fun in my spare time!</p> <p>From the vast store of uploaded images and video that <a href="https://photutorial.com/photos-statistics/">grow by the billions every day</a>, high-end computers using machine learning algorithms combined with large language models (LLMs), can turn a simple textual prompt into an image that is often quite realistic. For example, this image was generated with the prompt, "greek roman soldier wearing helmet and glasses sitting at a computer programming" and was generated using <a href="https://www.artspace.ai/?ref=delphidave">ArtSpace.ai</a>. (I'm pretty sure he's a Delphi programmer, don't you?)</p> <p><a href="/sites/default/files/inline-images/GuyTeeshirtGlassesDesk.png"><img alt="Guy in tee-shirt at desk" data-entity-type="file" data-entity-uuid="56cbb1fe-752c-4e25-9188-381c2d4e9b47" hspace="10" src="/sites/default/files/inline-images/GuyTeeshirtGlassesDesk.png" width="200" class="align-right" /></a>Besides using prompts, AI image generation tools also let you base your new image on a theme or model. These next two images were generated using a similar prompt but different models; the one on the right uses one called, "Dream Studio" while the one on the left uses "Nova Flux 2024" (which is ArtSpace's newest and most realistic model).</p> <p><a href="/sites/default/files/inline-images/OldManExasperated.png"><img alt="Old and exasperated" data-entity-type="file" data-entity-uuid="aad7cdc6-5144-4ce0-90ff-52bd7f4f8dae" hspace="10" src="/sites/default/files/inline-images/OldManExasperated.png" width="200" class="align-left" /></a></p> <p>Another factor at ArtSpace.ai, the tool I used for generating all the images on this page, is the <em>seed</em>. This can either be a number or (on some models), an image. I have not had success using an image to teach it "likeness" yet, I'm still learning the nuances of this technology. What is interesting is how very different images can be generated from the same prompt by simply using a different seed number. The following images were all generated using the same model and prompt but with one digit different in a 10-digit seed number:</p> <p> </p> <table align="center" cellpadding="5"> <tbody> <tr> <td><a href="/sites/default/files/inline-images/BWSpiralColorTinged_Seed3949969084.png"><img alt="Black &amp; White Spiral with Color Tinges #1" data-entity-type="file" data-entity-uuid="10f31403-a6d4-4252-94cd-7c5e7b3a221c" src="/sites/default/files/inline-images/BWSpiralColorTinged_Seed3949969084.png" width="160" class="align-center" /></a></td> <td><a href="/sites/default/files/inline-images/BWSpiralColorTinged_Seed3949969085.png"><img alt="Black &amp; White Spiral with Color Tinges #2" data-entity-type="file" data-entity-uuid="63a788f6-b35a-49fb-89ed-7125968eab7e" src="/sites/default/files/inline-images/BWSpiralColorTinged_Seed3949969085.png" width="160" class="align-center" /></a></td> <td><a href="/sites/default/files/inline-images/BWSpiralColorTinged_Seed3949969086.png"><img alt="Black &amp; White Spiral with Color Tinges #3" data-entity-type="file" data-entity-uuid="c0c16cd1-5c06-41cb-9c97-75a4d9e638c5" src="/sites/default/files/inline-images/BWSpiralColorTinged_Seed3949969086.png" width="160" class="align-center" /></a></td> <td><a href="/sites/default/files/inline-images/BWSpiralColorTinged_Seed3949969087.png"><img alt="Black &amp; White Spiral with Color Tinges #4" data-entity-type="file" data-entity-uuid="26cadeb0-3795-459e-9375-a5fb012b8f44" src="/sites/default/files/inline-images/BWSpiralColorTinged_Seed3949969087.png" width="160" class="align-center" /></a></td> </tr> </tbody> </table> <p><a href="/sites/default/files/inline-images/BikiniGirlOnDock.png"><img alt="Bikini girl on boat dock with third arm" data-entity-type="file" data-entity-uuid="f040d8fe-b296-4ba8-84dd-a2e62d3b0e57" src="/sites/default/files/inline-images/BikiniGirlOnDock.png" width="175" class="align-right" /></a>AI image generators are not perfect. It can sometimes take several iterations of modifying your prompt to get the image you want; and it may actually lead you down a rabbit hole of exploration in a direction you did not expect. If you have a tool that lets you explore without time or cost limits, that can be a fun distraction. But it can be frustrating if you don't have the time to explore and don't know why the computer can't read your mind! I've seen a lot of example prompts that are long and explicit and generate amazing artwork. Learning to talk so the computer generates the image you want is a fine-tuned craft. It's all too easy to confuse the AI with text that seems innocent and clear to us but causes the generator to place an arm or shadow in an impossible position or not give enough fingers on a hand. I generated one of a woman in a bikini on a boat dock but in one of the iterations added the phrase, "motioning to join her in the water" and the AI added a third arm.</p> <p><a href="/sites/default/files/inline-images/LargeBlueMotorcycle.png"><img alt="Large blue motorcycle" data-entity-type="file" data-entity-uuid="d1e7b9ab-322c-456c-a320-d1678044f133" hspace="5" src="/sites/default/files/inline-images/LargeBlueMotorcycle.png" width="300" class="align-left" /></a>AI image generators can also be used to envision products that don't yet exist or describe a home remodel taking shape or how new furniture would look in your living room. Colors and sizes and shapes can be changed and the image regenerated. I saw a video recently how retailers are using AI to help customers visualize themselves in new clothing or a different pair of glasses. To the left is an image created from the prompt, "large, futuristic dark blue motorcycle with chrome exhaust pipes, sitting in a showroom, oversize rear tire, blurry background". With a several more lines of descriptive text, an engineer could see potential problems with a design before spending hours in a CAD program.</p> <p>You may have been playing around with some free tools or perhaps you've subscribed to an AI service to help in your programming and it happens to provide an image generator as well.  If you are still looking, I invite you to check out <strong>ArtSpace.ai</strong> which is one I discovered recently. It seems to be pretty good, has a lot of variety, the limit of 500 images a month is plenty for me, it's fairly quick (about 11 seconds per image), and I can download them from the web browser interface and use them any way I want--even commercially! Most software tools these days have a monthly or annual cost to use them and <a href="https://www.artspace.ai/?ref=delphidave">ArtSpace.ai</a> has both; but they also have a seldom-seen <em><a href="https://www.artspace.ai/lifetime?ref=delphidave">one-time-price</a></em> (which is just a little more than the annual price). For me, it was just too irresistible!</p> <a href="/sites/default/files/inline-images/CastleComputer.png"><img alt="Computer in the shape of a sandcastle" data-entity-type="file" data-entity-uuid="594148b8-b601-433e-9a0c-2b6b95291709" src="/sites/default/files/inline-images/CastleComputer.png" width="600" class="align-center" /></a> <p> </p> </div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-642" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1725840658"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/642#comment-642" class="permalink" rel="bookmark" hreflang="en">Wow! Thanks for sharing this…</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://capecodgunny.blogspot.com/" lang="" typeof="schema:Person" property="schema:name" datatype="">Michael Riley (not verified)</a> Sun, 09/08/2024 - 16:13</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Wow! Thanks for sharing this David.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=642&amp;1=default&amp;2=en&amp;3=" token="1h2tRUUsHHoZdPPIiFRWZDY3oWPVG-jAmDuprIIg6mQ"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-643" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1725844279"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/643#comment-643" class="permalink" rel="bookmark" hreflang="en">Not directly related to Delphi</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Sun, 09/08/2024 - 18:09</span> <p class="visually-hidden">In reply to <a href="/comment/642#comment-642" class="permalink" rel="bookmark" hreflang="en">Wow! Thanks for sharing this…</a> by <a rel="nofollow" href="https://capecodgunny.blogspot.com/" lang="" typeof="schema:Person" property="schema:name" datatype="">Michael Riley (not verified)</a></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>You're welcome, glad you liked it!</p> <p>I debated whether to write about this or not since it's not directly related to Delphi programming but I've been having fun and as soon as I started generating images, ideas came to mind about how these could be used to enhance blogs, to illustrate ideas, or come up with a cool, new avatar (like the one at the top of this page!).</p> <p>But so far, it's just for fun; for example, I play board games with some friends and generated this one yesterday:</p> <a href="/sites/default/files/inline-images/FourFriendsBoardGaming-2sm.png" rel="nofollow"><img alt="Four friends playing a board game" data-entity-type="file" data-entity-uuid="3bcd371a-7507-4ad2-a585-a2c187c22f58" src="/sites/default/files/inline-images/FourFriendsBoardGaming-2sm.png" width="400" class="align-center" /></a> <p> </p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=643&amp;1=default&amp;2=en&amp;3=" token="LxTm7r2u-N98SpPsBwBoCqQ_p_TWWHgvjEn2x0EwYyY"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=187&amp;2=comment_node_blog&amp;3=comment_node_blog" token="XcMmQYM7Q3mYqYC3aQG6ZfroXIoHWKIA_7gz6HJruL8"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Fri, 06 Sep 2024 06:20:56 +0000 david 187 at https://corneliusconcepts.tech https://corneliusconcepts.tech/ai-image-generation#comments GExperts - A Most Awesome Delphi Companion https://corneliusconcepts.tech/gexperts-most-awesome-delphi-companion <span property="dc:title">GExperts - A Most Awesome Delphi Companion</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2024-08-23T23:07:41+00:00" datatype="xsd:dateTime">Fri, 08/23/2024 - 16:07</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p><img alt="GExperts Wizard" data-entity-type="file" data-entity-uuid="39aad722-770f-415a-b374-3be372eadad8" src="/sites/default/files/inline-images/gexperts_wizard.png" class="align-right" />GExperts is one of the oldest and probably the most well-known plug-in for the Delphi IDE. I've been using it since the early versions and still use it in Delphi 12 today. Some of its features have been incorporated into the IDE itself over the years but it continues to evolve and be a useful productivity aid. </p> <p>In case you don't know what GExperts is, the <a href="https://www.gexperts.org/">website</a> describes it as "a free set of tools built to increase the productivity of Delphi and C++Builder programmers by adding several features to the IDE." I agree and would go on to say it has so many features that there must be something beneficial for everyone!</p> <p>Here is a list, in no particular order, of how I personally have used GExperts to improve my productivity over the last 20+ years. And yes, I have used every one of the features listed below at one time or another; and no, this is NOT all the features!</p> <h2>Code Proofreader</h2> <p>When working in early versions of Delphi (e.g. 5 and 7, 2007), the <em>Code Proofreader</em> not only helped keep identifiers cased consistently but can also save lots of time correcting typos with its AutoCorrect list. How many times have you typed <code>;=</code> when you intended to type <code>:=</code> ? Or "<code>ned</code>" when you meant "<code>end</code>"? GExperts will fix that for you on-the-fly. </p> <p>But you can expand that idea to use it as a macro replacement, somewhat like a simplified version of today's <a href="https://corneliusconcepts.tech/delphi-productivity-tips-live-templates">Live Templates</a> in Delphi. For example, I've added "<code>ef</code>" as an auto-correct with its replacement value set to "<code>:= False;</code>" and "<code>et</code>" with a replacement value of "<code>:= True;</code>". This speeds up initializing Boolean variables significantly. I've added several other similar macros for other common code snippets.</p> <h2>Code Librarian</h2> <p>If you find yourself writing a bunch of programs that use similar techniques, such as accessing a plug-in architecture or an obtuse interface or even just a common SQL statement, and it requires writing similar code over and over, you might find yourself opening old programs or saving snippets of code in a text file you keep close by. But what if that code could be organized in a hierarchical list and built into Delphi, accessed with a few clicks and pasted right in your code with a hotkey? Well, GExperts does just that with its <em>Code Librarian</em> feature. What's more, this is shown in a floating window that can be moved to a second monitor for even more convenience and has several sample snippets to jump-start your collection. It has keyword search and color syntax highlighting. Once you find your code snippet, just click <code>Ctrl+Shift+V</code> to insert that block of code right into the Delphi editor.</p> <h2>ASCII Chart</h2> <p>Have you ever needed to look up the ASCII number for a character? With GExperts installed, simply select <em>ASCII Chart</em> from its menu and you'll instantly have a handy reference to all 256 characters, plus you can select what font and size to see them with.</p> <h2>Backup Project</h2> <p>Even with source control projects in place, I occasionally use the <em>Backup Project</em> feature of GExperts. Usually, this is for copying the project and all the units it uses to a temporary folder or to zip up for some reason, like to send to a friend. This feature saves time by collecting all the units referenced in the DPR of the Delphi project, allowing you to modify the list of files, then creating a .zip file of your choice with the files at the click of a button. That's easier than manually selecting the files in Windows Explorer, isn't it?</p> <h2>Editor Experts </h2> <p>There are several features that are only active while working in the code editor. They use configurable shortcut key combinations that, once learned, add several convenient tricks. Here are three of them:</p> <h3>Expert: Insert Date/Time</h3> <p>One company I worked for required all changes to code to be marked with a commented date accompanied by a "doc header" section at the top of the unit with that date and a comment about what ticket this resolved. It got tedious manually typing something like <code>{ 03/15/24 }</code> at the end of every line you where you made changes that day but with GExperts configured to use the date format I need, this became as simple as hitting my configured shortcut key combination, <code>Ctrl+Alt+A</code>!</p> <h3>Expert: Reverse Statement</h3> <p>Have you ever typed up several statements to write configuration settings to an .INI file, then had to retype a bunch more similar ones to read the same settings? Perhaps you've copied, then pasted one set, then manually switched the assignments around. With GExperts, you can save a lot of copy/move/paste operations. Just select the pasted second set of statements and hit <code>Shift+Alt+R</code> (or your assigned shortcut keys) and all those assignment statements are switched around for you. Of course, you'll have to rename the <code>Read</code> statements to <code>Write</code> statements but select the block and use Find/Replace (or better yet, switch to <code>Sync Edit Mode</code> in modern versions of Delphi) to do that.</p> <h3>Expert: Comment/Uncomment</h3> <p>Before Delphi had the comment/uncomment hotkey, GExperts provided this feature, albeit not quite as smart. Instead of a toggle, there was one shortcut to comment (<code>Ctrl+Alt+.</code>) and another to uncomment (<code>Ctrl+Alt+,</code>). It doesn't detect whether or not the lines are already commented, still, this is another nifty time saver when you're in the code in an old version of Delphi.</p> <h2>Replace Components</h2> <p>There have been numerous times when I've wanted to replace one component or set of components with another. Like when changing all buttons on a form to <code>TBitBtn</code> in order to add button icons. You can always manually edit the DFM file and use Find/Replace from one component type to another then acknowledge the changed properties and correct the source's reference when saving the unit but there's a much easier way: select <em>Replace Components</em> from the GExperts menu while one of the button components is selected, select the new component type in the drop-down list of the pop-up window, check the desired Scope (such as "All components on the current form"), then watch as GExperts makes the change for you in a flash! </p> <h2>Components To Code</h2> <p>Most of the time, a component can be simply placed on a form or data module and you can let the framework manage the creation and destruction of the component for you, also allowing you to set properties conveniently at design-time. There have been several instances where I've started down that path, then realized I need to create the component in code, either because I was converting a data module to a component and didn't want to worry about form resources; or when I needed to make multiple copies of the component dynamically.</p> <p>Setting up all the properties at design time is simple, but can be tedious to do it with code when there are a lot of properties. Here again, GExperts can simplify your life; just select the component you've set up on the form or data module, select <em>Components to Code</em> from the GExperts menu, and it will put all the Delphi statements to create the code on the Clipboard so you can then paste the lines into your code wherever you need.</p> <h2>To Do List</h2> <p>I'm pretty sure that many features of GExperts have inspired features now a standard part of Delphi--and I'm pretty sure that the <em>To Do List</em> is one of those. It's an ingenious but simple idea that if a curly-braced comment is started with <code>TODO:</code>, a list can be generated from comments in all the current source files to provide you a centralized view of tasks. This is another feature with a floating window, and this one can be docked or moved to a second monitor. It functions nearly identical to Delphi's TODO list in modern versions of the IDE.</p> <h2>Message Dialog Wizard</h2> <p>Have you ever added a <code>MessageDlg</code> statement in your code and then at runtime realized it didn't look quite right or you inadvertently switched the <code>Caption</code> with the <code>Title</code>? With GExperts <em>Message Dialog</em> wizard, not only can you build your message dialog statement visually but you can also preview it before a line of code is written! But wait there's more--you can also switch to using <code>MessageBox</code> instead or opt to embed the whole thing in an <code>IF</code> or <code>CASE</code> statement that checks its result! How convenient is that?</p> <h2>Set Tab Order</h2> <p>If you have several controls on a form and add and/or rearrange them over time without setting their tab order, running the program at runtime can be frustrating for a user who tries to navigate among them using the Tab key. Paying attention to the tab order of the various components can increase usability significantly. While new versions of Delphi have a feature to reset the ordering for you, the one in GExperts has more features--and has been around a very long time.</p> <h2>Configuration</h2> <p>There are some parts of GExperts that don't write code or provide real-time functionality but simply improve Delphi's IDE in subtle ways. Here are a couple of areas I'd like to point out.</p> <h3>Config: Component Palette</h3> <p>If you use an early version of Delphi and have lots of components on your horizontally positioned component palette, it can be difficult to find the right tab for the component set you're looking for because you have to slowly scroll through the component tabs until you find the right one. GExperts provides an option to add tabs on multiple lines so you can see all the component tabs at once. Of course, this takes more vertical space so you have to adjust (and save) the editor layout but this is a huge convenience when working with forms and data modules in old versions of Delphi with lots of third-party components installed. If you don't like multi-line component tabs, GExperts has another option to add a pop-up window with all the tabs listed (and alphabetically, if you like); no more component tab scrolling! (This feature is not needed in newer versions of Delphi and was removed from GExperts.)</p> <h3>Config: IDE</h3> <p>GExperts has grown and added features over the years to accommodate new capabilities of Delphi. The <em>IDE</em> tab of the Configuration is one area that has changed a lot; some of the new options are simple niceties, like "<em>Enhance IDE dialogs</em>" that lets you resize and save the position of some of the fixed dialogs.</p> <p>Others can save lots of time, like one I've recently discovered, "<em>Enhance Build Events dialog with Favorites</em>". This is a huge time-saver for me because I use Build Events a lot and often copy them from project to project. Before finding this feature, I would often close the current project, open an old project that used the build event I needed, go into <em>Project Properties</em> &gt; <em>Build Events</em> and copy the command, then close and reopen the project I was working on and go back into Build Events to paste the command. Now, if I think I'll ever use a Build Event command again, I can just add it to my Favorites list and then select it later when I'm working on a different project, saving me many steps. This has become another of my new favorite productivity boosters!</p> <p>Look through these options and the other Configuration tabs--you may find some features I have not mentioned that work to solve a tedious task for you!</p> <h2>Copy Your Configurations</h2> <p>Let's say you've added several <em>Code Librarian</em> snippets, added several <em>Dictionary Words</em> to the <em>Code Proofreader</em>, and maybe created a few entries in <em>Favorite Files</em> (another feature of GExperts but not mentioned here), and now you are upgrading Delphi or using a different version of Delphi to work on another project and you want all your GExperts settings you've taken time to customize. The settings for GExperts have had very few changes over the years and are (mostly) compatible with each other (and when the format changes, there's usually an automatic conversion). This means you could copy the configuration files GExperts uses from one installation to another in order to keep your settings.</p> <p>These configuration files are not a secret. Just look in GExperts <em>Configuration</em>; in old versions, go to the <em>File Locations</em> tab for the directories; in newer versions, go to the <em>General</em> tab and the folders will be shown in the <em>File Locations</em> group box. These folders can be changed if you need; in fact, you may experiment with putting them on a network or synchronized folder.</p> <h2>Closing &amp; Acknowledgements</h2> <p>There are many more features of GExperts not mentioned here, like the Class Browser, Clipboard History, PE Information, Component Grid, and especially several Editor Experts I have not taken the time to learn. Look through the menus, read the documentation, and use the capabilities that you find most useful. There may be some tricks you had not thought of that simplify your coding experience in surprising ways. Some features have been removed from GExperts over the years as the IDE has changed and no longer supports functionality in the same way; others have identical functionality in the IDE so I no longer use the GExperts version of that feature. Still, I use old versions of Delphi enough, and there are still enough productivity gems for the most recent version of Delphi, that this plug-in is still the most valuable Delphi plug-in for me.</p> <p>I would like to thank <a href="https://blog.dummzeuch.de/experimental-gexperts-version/">Thomas Müller</a> for continuing the support of GExperts after the original programmer, Erik Berry, stopped supporting the project a few years ago. It's an open-source project and you can contribute suggestions and  ask questions on <a href="https://en.delphipraxis.net/forum/31-gexperts/">Delphi PRAXiS forum</a> dedicated to this product.</p> <h2>See the Webinar</h2> <p>Finally, there was a <a href="https://www.youtube.com/live/4tR95Wi4a18?si=z41oGAPmldit5dUF">webinar on GExperts</a> by Jim McKeeth of GDK Software that demonstrated many of these features--and a few others I have not used. They will be presenting other popular Delphi plug-ins in the near future, so be sure and visit their "<a href="https://mailing.gdksoftware.com/webinars">webinars page</a>" to learn more.</p> </div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-624" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1724469181"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/624#comment-624" class="permalink" rel="bookmark" hreflang="en">Totally Agree.</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">Ian (not verified)</span> Fri, 08/23/2024 - 19:16</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Absolutely concur.  It is always the first Plug-In I install..</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=624&amp;1=default&amp;2=en&amp;3=" token="z8h0Q9_fj0Q2USdvtspLTQNxC_iJZwi4OGxxt9rV_t4"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=186&amp;2=comment_node_blog&amp;3=comment_node_blog" token="FDAKAXAEMxoDiklNGN5VjPJgsoUQrUQE6QW_Lszgu7s"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Fri, 23 Aug 2024 23:07:41 +0000 david 186 at https://corneliusconcepts.tech Take Control of Your Delphi Project https://corneliusconcepts.tech/take-control-your-delphi-project <span property="dc:title">Take Control of Your Delphi Project</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> <li class="taxonomy-term"><a href="/windows" hreflang="en">Windows</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2024-06-22T02:06:03+00:00" datatype="xsd:dateTime">Fri, 06/21/2024 - 19:06</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>You can (and should) have complete control over all aspects of the version information, application icon, operating system compatibility, requested user access, Windows theming dependencies, and more in your Delphi projects!</p> <p>For the better part of a week, I struggled to get some of these aspects configured properly for a suite of programs that are all configured similarly and for which I was building a script to migrate and manage. I had to learn about Microsoft Manifest files and how they are linked into Delphi projects, where to list a common application icon and get it to show up in the Windows TaskBar consistently regardless of individual form settings, and how to manage version info centrally for these programs needing common information such as company and copyright but also per-project data like product name and version number. I've been programming for a long time in Delphi but had just been letting Delphi manage most of this information--sometimes with frustrating consequences.</p> <p><strong>It was past time to take control!</strong></p> <p>This blog lists three different types of resources that can be linked into your Windows application. It serves as both a reminder to my future self and a tutorial for others and pulls together a couple weeks' worth of research, trial and error, and scouring the web for bits of information.</p> <h2>APPLICATION ICON</h2> <p>In many VCL programs, simply selecting and assigning an .ICO file to the main form's Icon property, is all you need to set the icon of the application and to see it show up on the TaskBar when running. There are also project properties to set an icon but a main form's icon will override that; if your main form does not have an icon set, the project's icon will show on both the form and the TaskBar.</p> <p>However, if you need to override any defaults set by the project or a form, you can set the <code>Application.Icon</code> property in code at runtime. The two simplest ways to assign the icon is to call one of the Icon property's methods, such as <code>LoadFromFile</code> or <code>LoadFromResourceName</code>. The latter requires the icon to be stored either in the .EXE or in a loaded resource file such as a DLL. In my applications, I want to store the icon in the EXE itself to keep things simple. A convenient way to do that is to select <em>Project</em> &gt; <em>Resources and Images...</em> from the Delphi menu, click <em>Add</em>, then select your .ICO file, set the <em>Resource Type</em> to ICON, and give it a good <em>Resource Identifier</em> (I chose "APPICON").</p> <p>Now, when your application is running simply pass in <strong>HInstance</strong> (the handle to your application) and <strong>'APPICON'</strong> (or the name you chose for your <em>Resource Identifier</em>) to override the default icon:</p> <p><code>Application.Icon.LoadFromResourceName(HInstatnce, 'APPICON');</code></p> <p>The inclusion of this icon as a resource in your executable is recorded in the .DPROJ file for your project and the icon filename shows in the IDE's project manager alongside your code units; it triggers the resource compiler at compile time to generate and build a special resource file with the same base name as your project and with a ".dres" extension. When resources are added to your project like this, a line is also added to the .DPR file for your project:</p> <p><code>{$R *.dres}</code></p> <h2>VERSION INFO</h2> <p>There have been several blogs over the years mentioning the poor version management in the Delphi IDE. The advice is to never allow Delphi to control the version information but to always manage it yourself externally. How do you do this? First, in the project options, make sure "Include version information in project" is unchecked for all configurations. Then, create a resource source file (.rc) with your version information specified in the right format, call the resource compiler to build this resource, and add a <code>$R</code> compiler directive to link it in.</p> <p>That may sound like a lot of work but I would invite you to read <a href="https://blog.dummzeuch.de/dzprepbuild-for-delphi/">Thomas Müller's blog</a> and download his <strong>dzPrepBuild</strong> utility. Not only will it build your resource file in the right format for you but it is built to be used in automation scripts to keep it updated with incrementing version numbers and copyright years.</p> <p>Following the guidelines for using this tool, my version info resource filename was <code>&lt;ProjectName&gt;_Version.rc</code>. Thus, I added a Pre-Build event to the project to build the resource just before compilation:<br /> <code>"$(BDS)\bin\brcc32.exe" $(OUTPUTNAME)_Version.rc</code></p> <p>Finally, I added one line to the project's .DPR:<br /> <code>{$R *_Version.RES}</code></p> <p>Correction--I actually <em>replaced</em> one line in my project's .DPR: I replaced the <code>{$R *.RES}</code>, incorrectly thinking I no longer needed that line. My belief was that since I cut out the automatic version info and replaced it with my own manually built version info resource, this line was no longer needed. However, that's when I noticed a visual change in my VCL applications and started studying manifest files.</p> <h2>MANIFEST</h2> <p>I had spent a bunch of time researching resources and version info and setting up a script and was feeling pretty good about how projects were being upgraded from Delphi 5 to Delphi 12 with new, more standardized configurations and a modern look and feel. I had been showing progress to my colleagues each week and how much time we would be saving. Then, suddenly, I noticed a visual change in the toolbar of all the programs that had been compiled with these new automated scripts and linked resources.</p> <p>We're using the <code>RzToolbar</code> from the <a href="https://getitnow.embarcadero.com/KonopkaControls-290-7.0_For12.1/">Konopka Signature VCL Controls</a> package and set a gradient style on the toolbar to go from <code>clBtnFace</code> on the left to <code>clBtnShadow</code> on the right. With all the buttons set to transparent, the icons floated on the toolbar with no borders until you hover your mouse over them, giving a nice, clean and responsive look to the application. But now, the buttons had lost their transparency--all the buttons had a white-ish box, the ones on the far left closely matched the toolbar color but it was very obvious on the right side.</p> <img alt="Without Windows themes, buttons are not transparent." data-entity-type="file" data-entity-uuid="6449cb23-67fc-4aa6-83ac-dda0d15b2acf" src="/sites/default/files/inline-images/RzToolbarTest-WhiteBox.png" class="align-center" /> <p>I tried everything I could think of, double-checking the Transparency property of the buttons, checking and unchecking "Enable Runtime Themes" in the manifest section of Delphi's project options, changing DPI awareness, looking through form properties--all to no avail. My research led to a couple of articles about manifest management in Delphi and, like project management, read that I should create my own manifest file--the <a href="https://www.finalbuilder.com/resources/blogs/modifying-xml-manifest-files-with-finalbuilder">blog by Vincent Parrett</a> was especially helpful with explanations and examples.</p> <p>I had written a sample program to test the gradient toolbar and set the project properties to use a custom manifest file that I gleaned from the examples and modified. After lots of trial-and-error testing I finally got to the point where I could break, then fix the transparency issue with the toolbar buttons by simply changing a section of the XML in the manifest. By this time, I had learned the manifest is treated like other resources and linked in--and it uses the default project resource name which I had thought was only for version information. By simply adding that default <code>{$R *.RES}</code> line back in the .DPR, everything was working again. (I wish the <a href="https://docwiki.embarcadero.com/RADStudio/Athens/en/Manifest_File">documentation on Delphi's support for manifest files</a> had mentioned that.)</p> <img alt="With Windows themes supported in the manifest, transparency works fine." data-entity-type="file" data-entity-uuid="823d1958-16b9-4c3c-ab82-3dae35359af0" src="/sites/default/files/inline-images/RzToolbarTest-Transparent.png" class="align-center" /> <p>So, what was the key to getting transparency working in the program? It turns out that <strong>enabling support for Windows Themes</strong> (which is what this transparency feature needed) was simply requiring a dependency on Microsoft Common Controls so that it would use a newer version of the visual controls. This is mentioned on an obscure page in the DocWiki where it shows the <a href="https://docwiki.embarcadero.com/RADStudio/Sydney/en/API_(ApplicationName.manifest)">default manifest</a> provided by Delphi for Windows targets. I would not have had to create a custom manifest if I had simply left that <code>$R</code> directive in place; however, I now know a lot more about what you can do with a manifest file by having read through the Microsoft documentation on <a href="https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests">Application Manifests</a>. For example, you can require admin privileges for a program (in <code>&lt;requestedPrivileges&gt;</code>) and you can require Windows 10 or above (in <code>&lt;compatibility&gt;</code>); there are other fine-grained constraints you can add to your applications if you need by crafting a custom manifest.</p> <h2>Sample Project</h2> <p>To preserve my research and make permanent use of the sample program that helped me figure this out, I put it up on Github. It's called <a href="https://github.com/cascade-software-systems/RzToolbarTest">RzToolbarTest</a> and includes a project file with associated icon, a resource source (.rc) file to specify the version info, and a manifest file, all linked in using three <code>$R</code> directives. </p> <p>I hope to save you--and my future self--some time with better control of Delphi projects!</p> </div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-586" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1719062408"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/586#comment-586" class="permalink" rel="bookmark" hreflang="en">Wow, somebody else is…</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a> Sat, 06/22/2024 - 02:05</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Wow, somebody else is actually using my dzPrepBuild. I was under the impression that I was the only one.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=586&amp;1=default&amp;2=en&amp;3=" token="vRpWVXjOzT1w9z9NzwuhLROJxghWXdd3i01C0JsjC9c"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="0" id="comment-596" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1720831111"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/596#comment-596" class="permalink" rel="bookmark" hreflang="en">@Thomas Müller : I think…</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://www.emploiresto.com" lang="" typeof="schema:Person" property="schema:name" datatype="">Dan (not verified)</a> Fri, 07/12/2024 - 11:34</span> <p class="visually-hidden">In reply to <a href="/comment/586#comment-586" class="permalink" rel="bookmark" hreflang="en">Wow, somebody else is…</a> by <a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>@Thomas Müller : I think congratulations are in order :-)</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=596&amp;1=default&amp;2=en&amp;3=" token="_mRgKizOWz6MUVkpHeOJX8gngIRYYQDHOoPyMSkxJEE"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div> <article data-comment-user-id="1" id="comment-587" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1719063770"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/587#comment-587" class="permalink" rel="bookmark" hreflang="en">Recently Found</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Sat, 06/22/2024 - 06:42</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>I only found it a couple of weeks ago. It all started from a conversation on <a href="https://en.delphipraxis.net/topic/11605-product-delphi-parser-ai-claims-what-does-it-mean/" rel="nofollow">Delphi PRAXiS</a> when Jim McKeeth mentioned the <a href="https://docwiki.embarcadero.com/RADStudio/Athens/en/ReFind.exe,_the_Search_and_Replace_Utility_Using_Perl_RegEx_Expressions" rel="nofollow">reFind</a> utility which got me started down the path of automating Delphi project migration. I had a large script filled with regular expressions and it was going well but there was a hole in the process that required manual editing after converting a Delphi 5 project (with no .dproj) up to Delphi 12. I didn't want to rely on programmers setting up the version info manually as it would surely result in inconsistencies. Besides, it's just text and the script had so much of the project information available already--surely that could be automated as well? So, I went looking for a tool to fill that gap and found the perfect solution with yours! And it will also fit in nicely with plans to set up a build server. I just hadn't gotten around to mentioning it on the forum yet because I just finished it late yesterday. Thanks for writing that utility--and blogging about it!</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=587&amp;1=default&amp;2=en&amp;3=" token="vZ7PSCpYuy14r-l6c9hadw3MoFBZCXhOZOuvroaEoOI"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=185&amp;2=comment_node_blog&amp;3=comment_node_blog" token="X6CQ9qEDJ2_jx__e-ZOBkzi-AY1ygHMBkLuqQQKUbkU"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Sat, 22 Jun 2024 02:06:03 +0000 david 185 at https://corneliusconcepts.tech Delphi as a Build Machine https://corneliusconcepts.tech/delphi-build-machine <span property="dc:title">Delphi as a Build Machine</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2024-05-09T02:07:58+00:00" datatype="xsd:dateTime">Wed, 05/08/2024 - 19:07</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>Building software projects involves, in addition to writing code, repetitive tasks like compiling, testing, signing, and deploying your completed application. Many times, a complicated suite of programs needs to be built in a certain order, copied to multiple locations, or even set up with special parameters for various customers. Large teams will likely have a dedicated DevOps person to handle these tasks, often with special "build machines" and complex scripts. Many development groups will utilize automation software to accomplish these tasks on a continual basis.</p> <p>As a Delphi developer, perhaps your organization has invested in <a href="https://www.finalbuilder.com/finalbuilder">FinalBuilder</a> or <a href="https://www.mitec.cz/pm.html">Project Maker</a> or maybe they set up a build server with <a href="https://codehunter.cc/a/jenkins/how-manage-to-get-an-automated-build-server-with-jenkins-for-projects-in-delphi">Jenkins</a> or even figured out how to get a <a href="https://ideasawakened.com/post/radauthenticator-part-4-build-automation-with-delphi-and-github-by-installing-a-self-hosted-runner">self-hosted runner for GitHub</a> working. These all take time and effort and a little money to establish; and while they pay off in the long-run, they may be just out of reach for your skillset or budget, or simply overkill for the small projects you're working on.</p> <p>But that doesn't mean you're stuck doing everything manually. Did you know you can perform many aspects of build automation right within the Delphi IDE on your own desktop? This is accomplished with three built-in features that work in conjunction with each other:</p> <ol> <li>Build Events,</li> <li>Project Groups, and</li> <li>Build Groups.</li> </ol> <p>Let's discuss each of these.</p> <h2><a href="https://docwiki.embarcadero.com/RADStudio/Athens/en/Build_Events">Build Events</a></h2> <p>A "build event" allows you to execute custom commands automatically as part of your build process. This can include tasks such as copying files, running scripts, or invoking external tools. There are two types of Delphi build events, <em>pre-build</em> and <em>post-build</em> events. As their names imply, pre-build events are commands that are run just before the project is built and post-build events run immediately afterwards. You can set flags so that errors returned from these commands halt further processing. For example, if a pre-build event tries to get the latest version of the code from a server but the server is unavailable, the build could be halted so you're not building something that might be out of date. Similarly, a post-build command could run a series of tests on the compiled project and if it fails, prevent further actions from taking place so that you can address the problem before sending out a faulty application. You can also define different build events for different platforms or configurations. For example, the debug executable might be copied to a network share for others to test while the release might get uploaded to a website. I often use build events to apply code-signing to release versions of executables.</p> <p>To configure build events in Delphi, <em>right</em>+<em>click</em> on your project in the Project Manager, select Options, then select Build Events under the "Building" section. You may also see "Pre-link events"; those are only applicable to C++ projects. </p> <img alt="Delphi's Build Event Editor" data-entity-type="file" data-entity-uuid="10725a11-397b-4158-9d66-f0da6816e6e4" src="/sites/default/files/inline-images/BuildEventEdit-SignTool.png" class="align-center" /> <p>Build event commands are executed by the IDE as if they were run on the DOS command line. You can add more than one command but they're stored concatenated with an ampersand (&amp;) in the project file which, in some older versions of Delphi are not handled properly (it also may cause parsing problems if you use an ampersand in your command). Many programmers write batch scripts that are called by the build event if they need to execute more than one command during a build event.</p> <p>One very nice feature of using Delphi's build events is having access to pre-defined macros in the IDE. For example, <code>$(OUTPUTDIR)</code> gets expanded to the folder where the compiled project will be placed and <code>$(Platform)</code> is the project's active platform name. These macros get replaced by their character values when the command is executed and can be used directly or passed in as a parameter to a script called by the build event. The build event editor shows the list of available macros in the bottom half of the window. </p> <p>In the screenshot above, I call a signing tool to code-sign the release version of a compiled program and pass in <code>$(OUTPUTPATH)</code>, which gets replaced with the completed executable's full path and filename when executed. If I change the project name or the path where compiled executables are placed, this build event command still works; also, I can copy this command to a different project and not have to change anything because I used a macro instead of hard-coding the filename. Note that I enclosed the macro in double-quotes because the path may include spaces or special characters that would cause parsing problems when run on the command-line.</p> <p>Debugging build events can be a little tricky; it's best to test the commands manually on the command line before putting them into a build script. When executed by a build process in Delphi, they appear in the "Output" tab of the "Messages" pane with their macros expanded.</p> <p>These automation capabilities really become useful when several projects are grouped together into a Project Group.</p> <h2><a href="https://docwiki.embarcadero.com/RADStudio/Athens/en/Projects_Window">Project Groups</a></h2> <p>One of the oldest features of Delphi's project management is the ability to group several projects together into a Project Group. A project group allows you to manage multiple related projects together without needing to unload one project, then load another. These projects can be sub-modules of a main application (e.g. DLLs or BPLs) or related programs possibly launched by the main application. In any case, it is useful to navigate between them when they're all visible in the same workspace.</p> <p>To create a project group in Delphi, simply load a first project like usual, then add an existing project to the group by right+clicking on the project group node in the Project Manager and selecting "Add Existing Project." You can add several projects to the list by repeating this process. You can then <em>right</em>+<em>click</em> on the project group name and change the name, compile or build all the projects with a simple mouse click.</p> <p><img alt="Add a project to a Project Group" data-entity-type="file" data-entity-uuid="e55f7a03-e082-43e6-8577-cda701daea72" src="/sites/default/files/inline-images/ProjGrp104.png" class="align-right" /></p> <p>When you select to compile or build all projects in this manner, the order of the projects can be important as compilation order of a "Build All" process always starts with the first project at the top and goes sequentially down the list. You may have projects that depend on others being compiled and available before they will successfully compile. You can adjust the order of the projects in the project group by right+clicking on a project and selecting either "Build Sooner" or "Build Later" from the pop-up menu to move the project up or down in the list.</p> <p>The buttons at the top of the Project Manager provide options for managing your project group. In addition to the convenience of having them lumped together for quick navigation, you can also change the configuration or platform of all the projects in the group at once. </p> <img alt="Setting the platform of all projects in a group at once" data-entity-type="file" data-entity-uuid="eb94998a-089c-4171-a309-a9cc00eaf84b" src="/sites/default/files/inline-images/ProjGrpPlatforms.png" class="align-center" /> <p><br /> <img alt="The button that opens the Build Groups pane" data-entity-type="file" data-entity-uuid="da5e864f-e8fa-4bdf-b7b3-a0c6000a3bcb" src="/sites/default/files/inline-images/BuildGroupsButton.png" class="align-right" />One of the Project Manager buttons takes this convenience and functionality one step further, it activates the Build Groups pane. </p> <h2><a href="https://docwiki.embarcadero.com/RADStudio/Athens/en/Build_Groups">Build Groups</a></h2> <p>When I first noticed Build Groups, I thought it was just another way to compile all the projects and so ignored it for quite some time. In one sense it is but after seeing a couple of articles on it a while back and exploring it further on my own, I realized there's additional value here that can take a developer's build process automation to the next level without leaving the IDE.</p> <p>Basically, a build group allows more flexibility when building all projects in a project group by allowing you to specify which projects to build; but more than that, it also allows you to select multiple configurations <em>and</em> platforms to use during a build process, meaning it can compile each project multiple times in one operation!</p> <blockquote> <p><em>Build Groups can compile each project multiple times in one operation!</em></p> </blockquote> <p>Let's say you need to build your suite of applications and modules for three different platforms; Build Groups can do that with one button click. Let's say you need to build all but one of your projects but you don't want to remove the project from the group; you can unselect any of the projects from the configured Build Group.</p> <p>When you activate the Build Groups pane, it shows all your projects and any defined build groups in a drop-down; initially there are no build groups but you can add, copy, and delete them with the toolbar buttons to the right of the list. When you add a build group, you give it a name and it creates a list of all the projects in your project group selected and with the current configuration and platform for each. You can then click in the space next to the configuration or platform for each listed project and click the button to add or remove various configurations or platforms. </p> <p>In the screenshot below, I've selected all projects in my project group, Debug and Release configurations for all but one of the projects, and various platforms for the different projects.</p> <img alt="Build group ready to generate 20 executables" data-entity-type="file" data-entity-uuid="2827e392-7360-45dd-acbf-b6af5c9a9fd7" src="/sites/default/files/inline-images/BuildGroupsVariousPlatforms20.png" class="align-center" /> <p>Once your build group is configured to your liking, you can click the appropriate button on the Build Groups toolbar to start the compile or build process, along with any defined build events for each selected project, then sit back and watch your computer work. If all goes well, clicking the <em>Compile</em> or <em>Build</em> buttons for this build group will generate 20 executables!</p> <h2>Conclusion</h2> <p>While this approach may not offer the sophistication of dedicated build automation tools, it can significantly reduce manual effort and ensure consistency in the build process. So, whether you're a lone coder or part of a team, consider harnessing the power of Delphi's Build Events and Build Groups to create a basic build automation system on your own desktop. By organizing projects effectively and automating repetitive tasks, you can streamline your development workflow to build better software, even on a shoestring budget.<br />  </p> </div> <section id="node-comment"> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=184&amp;2=comment_node_blog&amp;3=comment_node_blog" token="vj77P6xK5_a-o8LGq2U45hUQ-475PUdfXisUzMdSe8s"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Thu, 09 May 2024 02:07:58 +0000 david 184 at https://corneliusconcepts.tech Flexibility With Runtime Packages https://corneliusconcepts.tech/flexibility-runtime-packages <span property="dc:title">Flexibility With Runtime Packages</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2024-01-23T08:38:13+00:00" datatype="xsd:dateTime">Tue, 01/23/2024 - 00:38</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>Delphi's project options allows you to build a project with or without linking to runtime packages. A runtime package is a special dynamic-link library in the proprietary Delphi (or C++Builder) format that allows an executable to be split into various modules of functionality; these module files have a .BPL extension (for Borland Package Library). In the early days, this was useful to allow large applications to run on computers with limited memory because each module loads into its own memory space (these days, memory limitations are rare unless you're still deploying memory-hungry 32-bit applications). The main advantage today is realized if you have multiple executables that share some common functionality, like reporting or GUI controls or database access. The .EXEs can be smaller because instead of linking the reporting or GUI controls or database access code into each one of the .EXEs, you can ship that functionality in .BPLs and the .EXEs can be much smaller because they load that shared common code at runtime.</p> <p>To change this option, pull up your <em>Project Options</em> and find the "<strong>Link with runtime packages</strong>" option. If that option is not checked, you're building a monolithic executable--all the library code is in one big .EXE. But if you check that box and list the runtime packages for your applicaiton, your .EXE is smaller and you must ship the specified package files along with it. To be automatically found by the .EXE, the .BPLs must be either in the same folder as the .EXE or on the system PATH.</p> <blockquote> <p>What I didn't know is that you don't have to list all the runtime packages your application uses!</p> </blockquote> <p>I've known about this feature for many years and have used it at times for specific scenarios, or when a working with a plug-in system that used the .BPL format. What I didn't know, however, is that you don't have to list all the runtime packages for that option--and what packages you DON'T list, get linked into the .EXE. In fact, if you don't list ANY runtime packages, Delphi builds the application as if you had not checked the "Link with runtime packages" option at all!</p> <p>This means that you can determine how many of the packages to link in and which ones to leave out as extra .BPL files. My assumption has always been that it was all or nothing--either all packages were linked into one giant .EXE or all packages used by the application had to be listed and deployed for it to work. This is not so!</p> <p>By default, the "Link with runtime packages" option is unchecked for new application projects, allowing you to quickly create a stand-alone program. When you first check that option and enable the "Runtime packages" line, all the packages for all components you have installed in Delphi, whether from Embarcadero or open source or commercial products, and both VCL and Firemonkey, are all listed. You obviously don't need all these packages and cannot even build a project with everyone of them listed as they are not all available for all platforms; for example VCL and Firemonkey are incompatible in the same project and the BDE doesn't work on any platform other than Win32.</p> <p>If you're going this route with runtime packages and are deciding which ones to include, it may be prudent to stop periodically and build your project to see how big the .EXE size is. Depending on how many packages and .EXEs you have to ship, there will be a "sweet spot" that makes sense for you. A team I'm working with deploys different sets of several dozen .EXEs to hundreds of customers and while there could be an argument to save hard disk space, it's so cheap these days, it's hardly worth the time. What's more important for us is if we want to to send out a fix to a reporting library or upgrade the security of one of the internet components to support a new protocol or change a style of a GUI control. By keeping some of those types of .BPLs separate, we can update just a few files to affect all programs rather than recompile and redistribute hundreds of applications.</p> <p>After learning about this flexibility in listing runtime packages, I wanted to test it out and see some real numbers. So, I wrote a simple Win32 VCL application in Delphi 10.4 and placed a label, a button, and two third-party components on the main form, 1) a <a href="https://www.digital-metaphors.com/products" rel="nofollow">ReportBuilder</a> component, and 2) a non-visual layout saver from a <a href="https://github.com/corneliusdavid/ccLib" rel="nofollow">small library</a> I wrote. I built this four different ways, noting the size of the .EXE and the list of packages to deploy for each scenario:</p> <p>OPTION<strong>: NOT linked with runtime packages</strong><br /> EXE Size: <strong>26,690 KB</strong><br /> Deploy: single .EXE</p> <p>OPTION<strong>: Linked with runtime packages: only ccLib_R</strong><br /> EXE Size:<strong> 14,581 KB</strong><br /> Deploy: .EXE plus...</p> <ul> <li>rtl270.bpl</li> <li>vcl270.bpl</li> <li>dbrtl270.bpl</li> <li>cclib_r270.bpl</li> </ul> <p>OPTION<strong>: Linked with runtime packages, only dclRB2227 (ReportBuilder)</strong><br /> EXE Size:<strong> 4,224 KB</strong><br /> Deploy: .EXE plus...</p> <ul> <li>rtl270.bpl</li> <li>vcl270.bpl</li> <li>vclx270.bpl</li> <li>vclWinX270.bpl</li> <li>vclimg270.bpl</li> <li>vclie270.bpl</li> <li>vcledge270.bpl</li> <li>bindengine270.bpl</li> <li>rbRTL2227.bpl</li> <li>rbRCL2227.bpl</li> </ul> <p>OPTION<strong>: Linked with runtime packages, both dclRB227 and ccLib_R</strong><br /> EXE Size: <strong>4,210 KB</strong><br /> Deploy: .EXE plus...</p> <ul> <li>rtl270.bpl</li> <li>vcl270.bpl</li> <li>vclx270.bpl</li> <li>vclWinX270.bpl</li> <li>vclimg270.bpl</li> <li>vclie270.bpl</li> <li>vcledge270.bpl</li> <li>bindengine270.bpl</li> <li>rbRTL2227.bpl</li> <li>rbRCL2227.bpl</li> <li>ccLib_R.bpl</li> </ul> <p>Several things I noticed in this test: 1) removing my small library from the stand-alone .EXE dropped its size by quite a bit but only because it also separated out the RTL and VCL packages that had been previously linked in; 2) simply listing a single ReportBuilder package required <em>eight</em> additional .BPLs to also be shipped; 3) the real size of the "ccLib" package was realized in the last test as it only reduced the .EXE size by 14 KB from the previous test.</p> <p>A final consideration when selecting your package list is the hassle to package and deploy the various .BPL files; is it worth the extra management required to make sure all the .BPLs are deployed in order to save a few MB? The questions you have to answer will be different for your software and customer environment. But with modern build tools and automation, a lot of the details can be tucked away, never seen or dealt with after the configuration is tested so that is probably not a big deal.</p> <p>Well, if you didn't know this trick about varying the selected packages to include before, you now have a little extra flexibility for how you build your Delphi projects.</p></div> <section id="node-comment"> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=183&amp;2=comment_node_blog&amp;3=comment_node_blog" token="7xKjMFhrQjG3YRM83t3BpwnCllSoMXFWRhGdAPdJDB4"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Tue, 23 Jan 2024 08:38:13 +0000 david 183 at https://corneliusconcepts.tech First Drupal-to-Hugo Conversion https://corneliusconcepts.tech/first-drupal-hugo-conversion <span property="dc:title">First Drupal-to-Hugo Conversion</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/cloud" hreflang="en">Cloud Computing</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2024-01-05T07:33:01+00:00" datatype="xsd:dateTime">Thu, 01/04/2024 - 23:33</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>Back in <a href="/major-drupal-upgrade-woes" rel="nofollow">May of 2022</a>, I wrote about a predicament I found myself in with multiple websites built in Drupal and the chore of upgrading in the right sequence to avoid incompatibilities with the installed version of PHP on the web server. I began to look around at my options and discovered others that also had <a href="https://andrewmarsh.com/blog/2016/02/08/moving-from-drupal-to-hugo/" rel="nofollow">fallen out of love with Drupal</a> were <a href="https://www.researchut.com/post/Drupal-7_to_Hugo/#gsc.tab=0" rel="nofollow">turning to static web page builders</a>, several of them <a href="https://tonisagrista.com/blog/2016/moving-to-hugo/" rel="nofollow">to Hugo</a>.</p> <p>This sounded very much like the adage, "What's Old is New Again!"  <em>But this time it's different.</em></p> <p>In the beginning, we all built our sites by hand with HTML. It wasn't long before that became quite tedious and we turned to scripting to add content to our sites, using such languages as Perl or PHP or maybe ASP.NET. This was, of course, much better than manually updating HTML pages, especially if you also maintained a table of contents, an index, and a site map. HTML editing tools proliferated.</p> <p>Then some people productized their scripting and <a href="https://kinsta.com/knowledgebase/content-management-system/" rel="nofollow">Content Management Systems</a> became a thing. This was even better because it allowed non-programmers to maintain their site and encouraged more features and multiple people and separation of code and data and design. All these were good things but as web sites got more complex and security issues crept in, these simple frameworks blossomed in size. Some of my early Drupal sites were less than 500 KB; the latest version of Drupal is nearly 120 MB and includes almost 20,000 files--and that's just the core install. After that, there are modules and themes and profiles and customizations--then security updates and bug fixes! IT NEVER STOPS!</p> <p>So, now I want to throw all that away and go back to hand-editing my content? Well, yes, actually!</p> <p>My <a href="https://nuez.io/posts/hugo-vs-drupal/" rel="nofollow">research</a> and testing led me to try out <a href="https://gohugo.io/" rel="nofollow">Hugo</a>; a year ago I wrote about this <a href="/new-direction-web-development" rel="nofollow">new direction</a> and during 2023, built a couple of small sites in Hugo for a client, <a href="https://modularbuildingsolutions.com" rel="nofollow">Modular Building Solutions</a>, and a personal photo album for our family. I wrote some utilities and really liked editing the simple <a href="https://www.howtogeek.com/448323/what-is-markdown-and-how-do-you-use-it/" rel="nofollow">Markdown</a> pages, running Hugo to rebuild the HTML pages, then uploading. In fact, with GitHub Actions, simply pushing updates to a remote repository can kick off an <a href="/automating-hugo-github-actions-raspberry-pi" rel="nofollow">automatic site update</a>.</p> <p>I was having fun again!</p> <p>Now the task of converting the content in my Drupal sites to Markdown pages lay in front of me. The first one I wanted to tackle was the <strong>Oregon Delphi User Group</strong> for which I am the coordinator. It's version of Drupal was fairly old and there were a couple of minor issues whenever I added meeting notices that I had to work around.  Plus, I didn't really like the theme but didn't feel like customizing it. You can see what it looked like on the <a href="http://web.archive.org/web/20230207021735/https://odug.org/" rel="nofollow">Wayback Machine</a>.</p> <p>So, over the holidays, I took some time, found a <a href="https://gist.github.com/amoutiers/991da535cc2afef8c1bb0c56326a378e" rel="nofollow">script</a> (or was it <a href="https://git.herzbube.ch/gitweb.cgi/tools.git/blob/HEAD:/drupal2hugo/drupal2hugo.php" rel="nofollow">this one</a>?), exported the content, and got to work. </p> <p>And yes, it <a href="https://waddles.org/2015/12/19/issues-converting-from-drupal-to-hugo/" rel="nofollow">took some work</a> to get all the content converted because the Drupal content editor allows different formats of text, including Full HTML, which I often used so I could customize parts of the entries or attach images and align them properly. The problem with that is it doesn't just store the text, it stores the HTML that I entered as well. So what I exported had to have its HTML tags removed and replaced with Markdown. There was probably an easier or more automated way but I had other edits to make as well. Instead of using Drupal's "Taxonomy" system to set up events by year, I just associated "year" tags to the various posts. Then, since I was adding tags anyway, I added a few other tags for speakers and topics. As I kept building and reviewing the finished site (which is nearly instantaneous with <a href="https://gohugo.io/commands/hugo_server/" rel="nofollow">Hugo's in-memory server</a> feature), I made other tweaks. Adding a theme was just as fun because I found <a href="https://gitlab.com/gabmus/hugo-ficurinia" rel="nofollow">one</a> that was easily modified to make the posts look like Pascal procedures!</p> <p>I unveiled it yesterday and so far, I've gotten positive feedback. I think it looks better than the old one, and it's MUCH faster because it's no longer running through a PHP interpreter to generate HTML, it just serves the pre-built HTML files. There's so much less to configure and, of course, there are NEVER any security concerns--but then again, it's just a static site that gets updated about once a month.</p> <p>Check it out: <strong><a href="https://odug.org" rel="nofollow">ODUG.ORG</a></strong>. And if you maintain a static site, you should try Hugo for yourself!</p></div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-546" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1704505362"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/546#comment-546" class="permalink" rel="bookmark" hreflang="en">Good move. I also use Hugo…</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">Andre (not verified)</span> Fri, 01/05/2024 - 17:40</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Good move. I also use Hugo and it feels good to have full control of your website. The website is also so lightweight and loads extreme fast compared to all bloated websites.</p> <p>Nice work and the website looks great 👍</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=546&amp;1=default&amp;2=en&amp;3=" token="tl2ctT-mqRRDksqi8vrPNJAYpam6DBtYiL9aE4fRubk"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-547" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1704505406"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/547#comment-547" class="permalink" rel="bookmark" hreflang="en">Thanks!</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Fri, 01/05/2024 - 17:43</span> <p class="visually-hidden">In reply to <a href="/comment/546#comment-546" class="permalink" rel="bookmark" hreflang="en">Good move. I also use Hugo…</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">Andre (not verified)</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>Thanks!</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=547&amp;1=default&amp;2=en&amp;3=" token="UC-VqWiQNp_sfdDEkuvwWK_xd0aaqhOccVTNVRI71Gk"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div> <article data-comment-user-id="0" id="comment-548" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1705083658"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/548#comment-548" class="permalink" rel="bookmark" hreflang="en">David, this is a fascinating…</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://capecodgunny.blogspot.com/" lang="" typeof="schema:Person" property="schema:name" datatype="">Michael Riley (not verified)</a> Wed, 01/10/2024 - 03:44</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>David, this is a fascinating article. After reading this I will definitely look into using Hugo.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=548&amp;1=default&amp;2=en&amp;3=" token="1nWieVkSqtXH2OH2OWPcDEYKWI3AIlRlZ8PAc0uTjlg"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=182&amp;2=comment_node_blog&amp;3=comment_node_blog" token="Cag3Q6Y2mLFN98X_SyM1qQa2otoYClqpngqRkpEZ6I4"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Fri, 05 Jan 2024 07:33:01 +0000 david 182 at https://corneliusconcepts.tech ChatGPT can write Delphi code https://corneliusconcepts.tech/how-chatgpt-helped-me-write-delphi-function <span property="dc:title">ChatGPT can write Delphi code</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2023-12-29T17:46:55+00:00" datatype="xsd:dateTime">Fri, 12/29/2023 - 09:46</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>The company I work for has hundreds of small accounting programs written many years ago in Delphi 5 where they have been maintained ever since. As a new member of the team, my primary task is to help them upgrade to newer versions of Delphi. But I also assist in supporting their legacy code for dozens of county offices that rely on them every day. Sometimes, these customers need features that, while are simple to implement in newer versions of Delphi, were not yet added to the toolset in the late 1990s. One such request came through a few months ago and I was surprised to discover that it had never been requested--nor did the rest of the team know how to handle it.</p> <p>The need was to be able to import data through a SOAP webservice. I didn't learn about this until another programmer had the XML packet, complete with SOAP header he had pulled from the example sent to him, all ready packaged and ready to hand off to some other function. I knew I would be writing a DLL in Delphi 11 that would be called by his Delphi 5 application but I had never called a SOAP service from Delphi with raw XML--I had always used its WSDL importer, filled in properties and called a method of the class. As I looked further at this, trying to untangle what the end result of what I needed to do and thinking it couldn't be that hard, a thought struck me: why not try this new AI tool, ChatGPT, that others have said had been helping them with coding? I already had a free account and had used it a few times for research and to help explain a few technical things but this was a chance to see if it could actually help me save some time as a professional programmer.</p> <p>So I opened up a new ChatGPT session and typed in:</p> <blockquote> <p><strong>How do I call a SOAP web service from Delphi without using the WSDL importer?</strong></p> </blockquote> <p>The response was a great explanation of the process:</p> <blockquote> <p>Calling a SOAP web service from Delphi without using the WSDL importer involves manually crafting the SOAP request and handling the SOAP response using the Indy components (TIdHTTP) or other HTTP libraries. This approach requires a good understanding of SOAP XML structure and the HTTP protocol. Here's a basic outline of the steps you need to follow:</p> <ol> <li> <p><strong>Create the SOAP Request XML:</strong> Manually construct the SOAP request XML using the appropriate SOAP envelope, headers, and body for your web service method. The structure of the XML depends on the specific web service you're interacting with.</p> </li> <li> <p><strong>Set Up the HTTP Request:</strong> Use Delphi's HTTP library (e.g., TIdHTTP from Indy) to create an HTTP POST request to the web service endpoint. Set the appropriate headers, such as "Content-Type" and "SOAPAction" header.</p> </li> <li> <p><strong>Send the SOAP Request:</strong> Send the SOAP request XML as the content of the HTTP POST request. Make sure to encode the request XML properly.</p> </li> <li> <p><strong>Receive and Process the Response:</strong> Receive the HTTP response from the server. Extract the SOAP response XML from the response body.</p> </li> <li> <p><strong>Parse the SOAP Response:</strong> Manually parse the SOAP response XML to extract the relevant data. This may involve using XML parsing libraries like Delphi's built-in XML components or other third-party XML parsing libraries.</p> </li> <li> <p><strong>Handle Errors:</strong> Implement error handling logic to deal with various possible error scenarios, both at the HTTP level and the SOAP level.</p> </li> </ol> <p>Here's a simplified example of what the code might look like using Indy's TIdHTTP component:</p> <code>uses<br />   IdHTTP, IdSSLOpenSSL;</code><br /> <code>procedure CallSoapWebService;<br /> var<br />   HTTPClient: TIdHTTP;<br />   RequestXML: TStringStream;<br />   ResponseXML: string;<br />   EndpointURL: string;<br /> begin<br />   HTTPClient := TIdHTTP.Create(nil);<br />   RequestXML := TStringStream.Create('YourSOAPRequestXMLHere', TEncoding.UTF8);<br />   EndpointURL := 'YourWebServiceEndpointURLHere';<br /> <br />   try<br />     HTTPClient.Request.ContentType := 'text/xml;charset=UTF-8';<br />     HTTPClient.Request.CustomHeaders.Add('SOAPAction: YourSOAPActionHere');<br /> <br />     ResponseXML := HTTPClient.Post(EndpointURL, RequestXML);<br /> <br />     // Process the SOAP response XML here<br />   except<br />     on E: Exception do<br />     begin<br />       // Handle exceptions<br />     end;<br />   end;<br /> <br />   HTTPClient.Free;<br />   RequestXML.Free;<br /> end;</code> <p>Remember, this is a very basic example and may not cover all the complexities of real-world SOAP interactions. Additionally, manually dealing with SOAP can be error-prone and time-consuming, so using the WSDL importer is generally recommended for more robust and maintainable code.</p> </blockquote> <p>I was very impressed but since I'm using Delphi 11, I wanted to know if I could use the newer internet client components instead of Indy; so, I typed this in next:</p> <blockquote> <p><strong>Can I do this with NetHTTPClient instead?</strong></p> </blockquote> <p>The sample code was reworked to use this component instead and even acknowledged it provides an easier and more modern way to work with HTTP requests and responses.</p> <p>After playing around with the updated code for a bit, I noticed it had used the property <code>DefaultHeaders</code> which is not part of the <code>HTTPClient</code> component. I asked ChatGPT about that and it immediately apologized and reworked the code using <code>CustomHeaders</code> instead (why it couldn't have used the correct property in the first place is a mystery to me):</p> <pre> uses System.Net.HttpClient, System.Net.URLClient, System.Classes; procedure CallSoapWebService; var HTTPClient: TNetHTTPClient; RequestContent: TStringStream; ResponseContent: string; EndpointURL: string; begin HTTPClient := TNetHTTPClient.Create(nil); RequestContent := TStringStream.Create('YourSOAPRequestXMLHere', TEncoding.UTF8); EndpointURL := 'YourWebServiceEndpointURLHere'; try HTTPClient.CustomHeaders['Content-Type'] := 'text/xml;charset=UTF-8'; HTTPClient.CustomHeaders['SOAPAction'] := 'YourSOAPActionHere'; ResponseContent := HTTPClient.Post(EndpointURL, RequestContent).ContentAsString; // Process the SOAP response XML here except on E: Exception do begin // Handle exceptions end; end; HTTPClient.Free; RequestContent.Free; end; </pre> <p>I asked a few more clarifying questions and got really good answers, turned the procedure into a function, added a <code>try/finally</code> block around the bulk of the function to properly free the components, created an interface to convert strings to PChars to work inside the DLL (I suppose I could've asked ChatGPT to do this for me!), and ended up with a useful tool in our library to support our legacy code a little longer.</p> <pre> function CallSOAP(const RequestURL, RequestStr: string; var ResponseStr: string): Boolean; var RequestContent: TStringStream; begin NetHTTPClient := TNetHTTPClient.Create(nil); RequestContent := TStringStream.Create(RequestStr, TEncoding.UTF8); try Result := True; try NetHTTPClient.CustomHeaders['Content-Type'] := 'text/xml;charset=UTF-8'; ResponseStr := NetHTTPClient.Post(RequestURL, RequestContent).ContentAsString; except on E: Exception do begin ResponseStr := E.Message; Result := False; end; end; finally RequestContent.Free; end; end; </pre> <p>This is a really simple example of how AI can enhance programmer productivity.</p> </div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-543" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1704231537"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/543#comment-543" class="permalink" rel="bookmark" hreflang="en">My experience was a lot worse</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a> Sat, 12/30/2023 - 09:25</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>I tried something similar several times but always got code that had some compile errors and / or was using some classes or methods that simply didn't exist. chatGPT always aplogized when I told it about these errors and generated new code, that again had the same kind of errors. After a few iterations of that I got bored and wrote the code myself.</p> <p>And after trying this several times with the same kind of results, I finally gave up.</p> <p>You seem to have had more luck or maybe your prompts were better than mine.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=543&amp;1=default&amp;2=en&amp;3=" token="oA8jCcM6B8oWb5ueyMESS9u6KweI2IXUq8CXMXqHEns"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <article data-comment-user-id="1" id="comment-545" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1704232070"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/545#comment-545" class="permalink" rel="bookmark" hreflang="en">To be fair...</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Tue, 01/02/2024 - 13:47</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>To be fair, I encountered a compiler error as well after I switched to <code>NetHTTPClient</code>--it used <code>DefaultHeaders</code> instead of <code>CustomHeaders</code> but it was pretty quick to spot and fix. There may have been a couple other things as well, but the overall experience was that it increased my productivity in this instance by jump-starting the coding process--which is what I was hoping.</p> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=545&amp;1=default&amp;2=en&amp;3=" token="EuuYZuxnZKU0F0rWW43LDSlp25EfXBsIziQWmGnpUqg"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=181&amp;2=comment_node_blog&amp;3=comment_node_blog" token="r-jVbrYCWXuqe8Z7p4_mzNbFjTkrc4aDBXVsXBhiWiw"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Fri, 29 Dec 2023 17:46:55 +0000 david 181 at https://corneliusconcepts.tech Moving to Delphi 12 https://corneliusconcepts.tech/moving-delphi-12 <span property="dc:title">Moving to Delphi 12</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2023-11-30T15:53:44+00:00" datatype="xsd:dateTime">Thu, 11/30/2023 - 07:53</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p><a href="https://blogs.embarcadero.com/announcing-the-availability-of-rad-studio-12-athens/" rel="nofollow">Delphi 12 Athens</a> has been out for nearly a month now and there are many fixes and improvements over its predecessor. It's still not perfect but I'm moving all projects I had in Delphi 11 up to the latest version. I like the extended <a href="/delphi-12-more-syntax-highlighting" rel="nofollow">syntax coloring</a> throughout the IDE, many new <a href="https://blog.marcocantu.com/blog/2023-november-vcl-delphi-12.html" rel="nofollow">VCL enhancements</a>, and the added <a href="https://blogs.embarcadero.com/3-x-12-firemonkey-and-android-enhancements-in-delphi-12/" rel="nofollow">features for cross-platform development</a> such as the <a href="/delphi-12-all-icons-once" rel="nofollow">Single App Icon Wizard</a> and several <a href="https://docwiki.embarcadero.com/RADStudio/Athens/en/What%27s_New#FireMonkey" rel="nofollow">FireMonkey additions</a>, especially the simple enabling of <a href="https://docwiki.embarcadero.com/RADStudio/Athens/en/Skia4Delphi" rel="nofollow">Skia4Delphi with just a menu option</a>.</p> <p>There are a few small open-source programs and libraries I maintain on GitHub; here are the ones for which I've added support for Delphi 12:</p> <ul> <li><a href="https://github.com/corneliusdavid/AutoGetIt" rel="nofollow">AutoGetIt</a> - a program that automates the GetIt command-line tool that comes with Delphi to save/load a list of checked GetIt packages and install or uninstall them en masse.</li> <li><a href="https://github.com/corneliusdavid/WP.ProjectTreePlugin" rel="nofollow">WP.ProjectTreePlugin</a> - a Welcome Page plug-in for Delphi 11 or 12 that lists Delphi projects in a TreeView grouped by the first two letters of their project name and allows a double-click to open the selected project.</li> <li><a href="https://github.com/corneliusdavid/DelphiVersions" rel="nofollow">DelphiVersions</a> - 4 apps that can be compiled in different versions of Delphi and on different platforms to highlight various compiler directives in each.</li> <li><a href="https://github.com/corneliusdavid/encryptor3" rel="nofollow">Encryptor3</a> - a program to generate/test various encryption/decryption strings using <a href="https://github.com/TurboPack/LockBox3" rel="nofollow">LockBox3</a>.</li> <li><a href="https://github.com/corneliusdavid/LiveBindingsBeyond" rel="nofollow">LiveBindingsBeyond</a> - several methods added to LiveBindings in Delphi.</li> </ul> <p>One small problem I encountered is after installing the Parnassus plug-ins for Delphi 12, they stopped working in Delphi 11, raising an error when Delphi first started up. However, I'd like to thank <a href="https://en.delphipraxis.net/topic/10407-delphi-12-is-available/?do=findComment&amp;comment=83115" rel="nofollow">Uwe Raabe on Delphi-PRAXiS</a> for sharing the simple solution to fix that.</p> <p>Delphi 12 Athens takes a big step forward in continuing to provide tools and productivity enhancements for programmers. Check out for yourself: <a href="https://blogs.embarcadero.com/rad-studio-12-every-new-and-enhanced-feature/" rel="nofollow">Every New and Enhanced Feature</a>!</p></div> <section id="node-comment"> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=180&amp;2=comment_node_blog&amp;3=comment_node_blog" token="h-stXHK3scsM6_vCBgM4Bc82maazQ9mQGaC7DJb9VmM"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Thu, 30 Nov 2023 15:53:44 +0000 david 180 at https://corneliusconcepts.tech Delphi 12: All the Icons at Once! https://corneliusconcepts.tech/delphi-12-all-icons-once <span property="dc:title">Delphi 12: All the Icons at Once!</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2023-10-10T07:26:27+00:00" datatype="xsd:dateTime">Tue, 10/10/2023 - 00:26</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>Coming in Delphi 12 is very cool feature that will save cross-platform developers a lot of time.  But first, a reminder:  This blog post is based on a pre-release version of the RAD Studio software and it has been written with specific permission by Embarcadero. No feature is committed until the product GA release.</p> <p>Windows and Mac desktop apps have a single icon file associated with the compiled executable that shows in a file list; for Windows, it’s an <code>.ico</code> file, for Mac, it’s an <code>.icns</code> file); if you support Universal Windows Platform, you’ll have a couple more. Desktop application programmers can add bitmaps and splash screens as they wish but there is no standard and it’s up to the programmer to decide if and how these should be implemented. When you look at the Project Options page for a VCL project or a FireMonkey project that only supports the Windows and Mac platforms, you’ll see there are only one or two files you need to specify:</p> <figure role="group" class="caption"> <img alt="Delphi Project Options page with default icons for Windows" data-entity-type="file" data-entity-uuid="b53a453f-ffa0-463b-80d3-cce0ddf0f8d0" src="/sites/default/files/inline-images/ArtworkGenerator-FMWin-Default.png" /> <figcaption>Delphi Project options page with default icons for Windows</figcaption> </figure> <p>Mobile apps changed all that and not only require a different bitmap for each device size but they also support both light and dark mode <em>and </em>splash screen images. The number of images you need to create suddenly becomes overwhelming. For example, here’s a list of images needed for an iOS 64-bit app on an iPhone; note that there’s another tab for iPad images (Android projects require nearly as many as the iPhone does):</p> <figure role="group" class="caption"> <img alt="Delphi Project Options page with default icons for an iPhone" data-entity-type="file" data-entity-uuid="662a72ae-9987-4d42-b035-3a703653bc0a" src="/sites/default/files/inline-images/ArtworkGenerator-iOS-default.png" /> <figcaption>Delphi Project Options page with default icons for an iPhone</figcaption> </figure> <p>A new feature built into the IDE in Delphi 12 Yukon is the “Single App Icon Wizard” and is accessed via the <strong>Artwork Generator </strong>button seen in the screenshots above. Launching this takes you through a few steps, starting with creating an image that will be automatically scaled to all the sizes you need for multiple platforms and device sizes, including both the <code>.ico</code> and <code>.icns</code> files, then specifying a splash screen, and finally saving all the generated files in sub-folders under your project folder. It allows you to put text over solid colors or bitmaps of your choice.</p> <p>I tried this on a small, open-source cross-platform suite of apps I wrote a few years ago called, <a href="https://github.com/corneliusdavid/DelphiVersions" rel="nofollow">DelphiVersions</a>, which can be compiled on different platforms and under different versions of Delphi to show which compiler directives are active. Since this is just a test app for developers, I never took the time to create the plethora of icons and bitmaps needed for all the different permuations of compiled apps, so this was a perfect test case.</p> <p>When you first click the Artwork Generator button, you start by defining the main icon of the app as shown below:</p> <figure role="group" class="caption"> <img alt="Delphi Single App Wizard - specify main icon" data-entity-type="file" data-entity-uuid="ce6dff75-70e9-45b1-8606-52d3153db237" src="/sites/default/files/inline-images/ArtworkGenerator-Win-Vr.png" /> <figcaption>Delphi Single App Wizard - specify main icon</figcaption> </figure> <p>I chose to set the text to “Vr” (for Versions) and used Embarcadero’s “red orb” image as the background. You can set the font and color of the text, make it bigger or smaller, and set the shape of the generated icon to rectangle, slightly rounded, rounded, circle, squircle (a shape that can’t decide whether it’s a square or a circle), and a teardrop. Here’s an example of the letter “D” on a teardrop-shapped yellow background:</p> <figure role="group" class="caption"> <img alt="Delphi Single App Wizard - text icon" data-entity-type="file" data-entity-uuid="0ae491bc-2f90-4c18-8079-b28b154411c4" src="/sites/default/files/inline-images/ArtworkGenerator-Text.png" /> <figcaption>Delphi Single App Wizard - text icon</figcaption> </figure> <p>You may also be prompted for the "Android Adaptive Icon" which creates scalable and responsive icons and splash screens specifically for Android devices in the Vector Drawable format. The “image” option on this screen requires an <code>.SVG</code> file and since I didn’t have one prepared, I opted to use a solid background.</p> <figure role="group" class="caption"> <img alt="Delphi Single App Wizard - Android Adaptive Icon" data-entity-type="file" data-entity-uuid="8c423e7a-4822-4dca-873e-8df65411c5fd" src="/sites/default/files/inline-images/ArtworkGenerator-Android-Vr.png" /> <figcaption>Delphi Single App Wizard - Android Adaptive Icon</figcaption> </figure> <p>The last step is specifying a splash screen. I opted to use a saved version of the “Vr orb” on a white background for light themes and the same on a black background for dark themes.</p> <figure role="group" class="caption"> <img alt="Delphi Single App Wizard - splash screen" data-entity-type="file" data-entity-uuid="08191c26-cc87-4a97-bb7b-174708e2806d" src="/sites/default/files/inline-images/ArtworkGenerator-SplashScreens-Vr.png" /> <figcaption>Delphi Single App Wizard - splash screen</figcaption> </figure> <p>It should be noted that when selecting images for icon or splash screen backgrounds, you should select an <code>.SVG</code> (<a href="https://blog.hubspot.com/website/what-is-an-svg-file#what-is-svg" rel="nofollow">scaler vector graphic</a>) file instead of a bitmap to allow the generated images to scale up and down much better. In fact, if you don’t, you’ll see the little warning triangle next to the selected file path as can be seen in the screenshots above.</p> <p>After the splash screens, click the Finish button and it will pause for a second, then tell you all the images have been generated. There will be a folder for each platform with multiple files in each and if you look at the icons for various platforms, you’ll see they’re all set for you:</p> <figure role="group" class="caption"> <img alt="Delphi Project Options - Android Icons all set" data-entity-type="file" data-entity-uuid="4e74eeb3-4f77-4378-a152-b280f3719060" src="/sites/default/files/inline-images/ArtworkGenerator-Android-Finished.png" /> <figcaption>Delphi Project Options - Android Icons all set</figcaption> </figure> <p>You can then replace any of these or regenerate them all by simply going through the wizard again.</p> <p>This is one of my new favorite productivity tools and lessens the burden of preparing cross-platform apps for a wide audience.</p></div> <section id="node-comment"> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=179&amp;2=comment_node_blog&amp;3=comment_node_blog" token="p6f-yHhiXNlpSBYvxl7UyaOgwPZoiCd6HBcxaQBOd_g"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Tue, 10 Oct 2023 07:26:27 +0000 david 179 at https://corneliusconcepts.tech Delphi 12: More Syntax Highlighting https://corneliusconcepts.tech/delphi-12-more-syntax-highlighting <span property="dc:title">Delphi 12: More Syntax Highlighting</span> <div class="node-taxonomy-container"> <h3 class="term-title"><i class="icon-hashtag theme-color"></i> Category</h3> <ul class="taxonomy-terms"> <li class="taxonomy-term"><a href="/programming" hreflang="en">Programming</a></li> </ul> </div> <!--/.node-taxonomy-container --> <span rel="sioc:has_creator"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span></span> <span property="dc:date dc:created" content="2023-10-02T04:51:25+00:00" datatype="xsd:dateTime">Sun, 10/01/2023 - 21:51</span> <div property="content:encoded" class="field field--name-body field--type-text-with-summary field--label-hidden field-item"><p>As I've been going through the Delphi 12 beta, I'm delighted to see many fixes and enhancements in the IDE to help the programming experience feel more fluid and intuitive. As with all beta versions being discussed, this blog post is based on a pre-release version of the RAD Studio software and it has been written with specific permission by Embarcadero. No feature is committed until the product GA release.</p> <p>Delphi, like all modern IDE tools, has had syntax highlighting for its code editor since--well, forever, I think. But several other parts of the IDE are used to help search or debug--and they also display code but just use plain text.</p> <p>Coming in Delphi 12, three areas of the IDE are being enhanced with syntax highlighting:</p> <ul> <li>Structure Pane</li> <li>Navigation Drop-Down</li> <li>Call Stack</li> </ul> <p>These may not at first seem all that big of a deal but I am happy that in addition to major features being added, interest and attention is not lost on the little details. Let's go over each of these.</p> <h3>Structure Pane</h3> <p>The Structure Pane is a handy view of elements that changes depending on the context. When you're in the code editor, it shows the list of classes, methods, and used units in the current unit. When you're in the form designer, it shows a tree view of the components on the form that can be rearranged by dragging them with the mouse to a different place on the hierarchy. I pulled up the same unit in both Delphi 12 and Delphi 10.4 to show the difference (I was having High-DPI issues in Delphi 11 and the Structure Pane looks similar between 10.4 and 11):</p> <p>Delphi 10.4 Structure Pane:</p> <p><img alt="Delphi 10.4 Structure Pane" data-entity-type="file" data-entity-uuid="cbedfe72-0e8e-4118-8e3b-2a74c653bd10" src="/sites/default/files/inline-images/StructurePaneOld.png" width="500" /></p> <p>Delphi 12 Structure Pane:</p> <p><img alt="Delphi 12 Structure Pane" data-entity-type="file" data-entity-uuid="d1bbeeb5-93f6-4cb9-9ccf-a00bdd123c8b" src="/sites/default/files/inline-images/StructurePaneNew.png" width="500" /></p> <p>To generate these screen shots, I pulled up the same project in both versions of Delphi, went to the same procedure and created a typo, then compiled it so we could see not only what is normally listed but also errors.</p> <h3>Navigation Drop-Down</h3> <p>The Navigation toolbar in Delphi's IDE is very handy for jumping to different places in your code. There are a couple of enhancements coming in Delphi 12 I'd like to point out. First, if your cursor is currently inside a type declaration or method, that type or method name is listed in the navigation drop-down; however, if it is outside of either of these contexts, that drop down area in current versions of Delph is blank. Delphi 12 adds a subtle enhancement that simply says "Search for a type" or "Search for a method" in that blank space. That may not seem like much but if you're not paying attention, the bar above your code just looks empty--but in Delphi 12, an unobtrusive reminder is there of the functionality close at hand. Here's a sneak peek at what it looks like:</p> <p><img alt="Delphi 12 Navigator search area" data-entity-type="file" data-entity-uuid="756370cf-6473-4257-b29f-0ba69bde3123" src="/sites/default/files/inline-images/NavNew.png" /></p> <p>Additionally, when the list of methods is displayed, the list is enhanced with color syntax highlighting. Here's a comparison:</p> <p>Delphi 11 Navigation Drop-Down:</p> <p><img alt="Delphi 11 Navigation Drop-Down" data-entity-type="file" data-entity-uuid="c7feda70-696d-46bb-8327-e4141e7a8e40" src="/sites/default/files/inline-images/NavDropDownOld.png" width="500" /></p> <p>Delphi 12 Navigation Drop-Down:</p> <p><img alt="Delphi 12 Navigation Drop-Down" data-entity-type="file" data-entity-uuid="2ff2e347-ec8f-448f-9200-1e339083a624" src="/sites/default/files/inline-images/NavDropDownNew.png" /></p> <p>Again, a subtle yet delightful enhancement!</p> <h3>Call Stack</h3> <p>A similar enhancement is now in the Call Stack pane when debugging--color syntax highlighting.</p> <p>Delphi 11 Call Stack:</p> <p><img alt="Delphi 11 Call Stack" data-entity-type="file" data-entity-uuid="3a933cd5-b349-49af-9a93-5bc6e7ac26ca" src="/sites/default/files/inline-images/CallStackOld2.png" width="550" /></p> <p>Delphi 12 Call Stack:</p> <p><img alt="Delphi 12 Call Stack" data-entity-type="file" data-entity-uuid="1f00d9cf-728c-4304-8712-b984f0d0b310" src="/sites/default/files/inline-images/CallStackNew.png" width="550" /></p> <p>There are many other fixes and enhancements to the IDE coming in Delphi 12. I am very much looking forward to it.</p> </div> <section id="node-comment"> <article data-comment-user-id="0" id="comment-535" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1696431094"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/535#comment-535" class="permalink" rel="bookmark" hreflang="en">Yukon Dropping UML Tools?</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">Navid (not verified)</span> Mon, 10/02/2023 - 15:39</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>I heard that Yukon completely drops UML modelling tools. We use them frequently, as there is no better way to understand the inner workings of complex projects. Can you confirm?</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=535&amp;1=default&amp;2=en&amp;3=" token="bPijW3CQIbo6QtFCav-OJpRP4DXFCEv7VqGWiDZmZsI"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="indented"> <article data-comment-user-id="1" id="comment-537" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/1"> </article> <mark class="hidden" data-comment-timestamp="1696431276"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/537#comment-537" class="permalink" rel="bookmark" hreflang="en">Just not installed</a></h3> <div class="single-comment-meta"> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">david</span> Wed, 10/04/2023 - 07:54</span> <p class="visually-hidden">In reply to <a href="/comment/535#comment-535" class="permalink" rel="bookmark" hreflang="en">Yukon Dropping UML Tools?</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">Navid (not verified)</span></p> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>If I understand correctly, they're not being "dropped" but simply not being installed by default.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=537&amp;1=default&amp;2=en&amp;3=" token="3aboCN-KfAijl5Cwh2X9DkLgDvGtg-piyisP82vkgP8"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> </div> <article data-comment-user-id="0" id="comment-536" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1696430900"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/536#comment-536" class="permalink" rel="bookmark" hreflang="en">&quot;Delphi, like all modern IDE…</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://blog.dummzeuch.de" lang="" typeof="schema:Person" property="schema:name" datatype="">Thomas Müller (not verified)</a> Tue, 10/03/2023 - 05:50</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>"Delphi, like all modern IDE tools, has had syntax highlighting for its code editor since--well, forever, I think."</p> <p>You are right: Delphi has had syntax highlighting since Version 1, back in the 1990ies, and it goes back even further to at least Turbo Pascal 5, possibly even earlier.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=536&amp;1=default&amp;2=en&amp;3=" token="4FI7egW_r6Wc7jSAyO7_koap4sVkDzs9hZvcx--EuOs"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <article data-comment-user-id="0" id="comment-538" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1696464905"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/538#comment-538" class="permalink" rel="bookmark" hreflang="en">stable version</a></h3> <div class="single-comment-meta"> <span><a rel="nofollow" href="https://gabrielmoraru.com/" lang="" typeof="schema:Person" property="schema:name" datatype="">Gabriel Moraru (not verified)</a> Wed, 10/04/2023 - 12:24</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p>I hope they fixed the bugs THIS TIME.</p> <p>For each release, Emba keeps listing hundreds of bug fixes, but then they introduce EXACTLY the same number fo bugs in the new version.</p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=538&amp;1=default&amp;2=en&amp;3=" token="wyJUpn7m4Tquk_YczixArslwrreggz5Ha_KpKvZDdYY"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <article data-comment-user-id="0" id="comment-544" class="js-comment single-comment"> <header class="comment-user-picture"> <article typeof="schema:Person" about="/user/0"> </article> <mark class="hidden" data-comment-timestamp="1704231521"></mark> </header> <div class="single-comment-content-body"> <h3 class="single-comment-title"><a href="/comment/544#comment-544" class="permalink" rel="bookmark" hreflang="en">more improvements</a></h3> <div class="single-comment-meta"> <span><span lang="" typeof="schema:Person" property="schema:name" datatype="">Alejandro G (not verified)</span> Sun, 12/31/2023 - 11:20</span> </div> <!-- /.single-comment-meta --> <div class="single-comment-content"> <div class="field field--name-comment-body field--type-text-long field--label-hidden field-item"><p><span lang="en" xml:lang="en" xml:lang="en">I recognize that the IDE is growing a lot, but it is missing some things that in my opinion would make it easier to detect errors. For example: at the bottom where the number of errors, warnings, and hits is shown, you should open a window to be able to go directly to one of those alerts. Many times there are several in a code with many lines and searching for one is uncomfortable and frustrating.</span></p></div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=544&amp;1=default&amp;2=en&amp;3=" token="LXpHSZ3MkxQEMrptjaN-vJGa-MXQXBGZ8O_f9GKiiaA"></drupal-render-placeholder> </div> </div> <!-- /.single-comment-content --> </article> <div class="comment-form-wrap"> <h2 class="add-comment-title"><i class="icon-add_comment theme-color"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=178&amp;2=comment_node_blog&amp;3=comment_node_blog" token="UHxrIgav-Wt4wvomEc5xli2jD6WomvXCG-Os8YZvGLs"></drupal-render-placeholder> </div> <!--/.comment-form --> </section> Mon, 02 Oct 2023 04:51:25 +0000 david 178 at https://corneliusconcepts.tech