1 /* DrawOfPages: Take notes with touchscreen input.
2  * Copyright (C) 2019  Marko Semet
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Affero General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Affero General Public License for more details.
13  * 
14  * You should have received a copy of the GNU Affero General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 module dop.gtk3.mainwindow;
18 
19 private
20 {
21     import cairo.Context;
22 
23     import dop.authorship;
24     import dop.processing.mainsystem;
25     import dop.structures.base;
26 
27     import gdk.RGBA;
28 
29     import gtk.AboutDialog;
30     import gtk.Application;
31     import gtk.ApplicationWindow;
32     import gtk.Builder;
33     import gtk.ColorChooserIF;
34     import gtk.ColorChooserWidget;
35     import gtk.DrawingArea;
36     import gtk.Image;
37     import gtk.Label;
38     import gtk.MenuButton;
39     import gtk.MenuItem;
40     import gtk.Range;
41     import gtk.Scale;
42     import gtk.SpinButton;
43     import gtk.ToggleButton;
44     import gtk.Widget;
45 
46     import gobject.ObjectG;
47     import gobject.ParamSpec;
48 
49     import std.algorithm : min;
50     import std.format;
51     import std.math : PI;
52 }
53 
54 public
55 {
56     /++
57      + Main window of draw of pages
58      +/
59     class DOP_MainWindow
60     {
61         private
62         {
63             Builder __builder;
64             shared MainSystem __mainSystem;
65             
66             ApplicationWindow __aw;
67 
68             Image __colorInfoSizeInfo;
69             DrawingArea __colorInfo;
70             ColorChooserWidget __colorChooser;
71             MenuButton __colorHelper;
72 
73             Scale __sizeChooser1;
74             SpinButton __sizeChooser2;
75             Label __sizeInfo;
76             MenuButton __sizeHelper;
77 
78             AboutDialog __menuAboutDialog;
79             MenuItem __menuAbout;
80             MenuItem __menuQuit;
81 
82             //
83             // Colors
84             //
85             void __updateColor(Context context)
86             {
87                 // Center position
88                 double posX;
89                 double posY;
90                 double size;
91                 GtkAllocation alloc;
92                 {
93                     int requestWidth;
94                     int requestHeight;
95                     int baseLine;
96 
97                     this.__colorInfo.getSizeRequest(requestWidth, requestHeight);
98                     this.__colorInfo.getAllocatedSize(alloc, baseLine);
99                     size = min(requestHeight, requestWidth) * 0.4;
100                     posX = alloc.width / 2.;
101                     posY = alloc.height / 2.;
102                 }
103 
104                 ColorRGBA currentColor;
105                 if(this.__mainSystem !is null)
106                 {
107                     currentColor = this.__mainSystem.currentColor;
108                 }
109                 { // Draw outer circle
110                     context.save();
111                     context.arc(posX, posY, size, 0, 2 * PI);
112                     if(currentColor.getLuma() >= 0.5)
113                     {
114                         context.setSourceRgba(0, 0, 0, 1);
115                     }
116                     else
117                     {
118                         context.setSourceRgba(1, 1, 1, 1);
119                     }
120                     context.fill();
121                     context.restore();
122                 }
123 
124                 { // Draw inner circle
125                     context.save();
126                     context.arc(posX, posY, size * 0.8, 0, 2 * PI);
127                     context.setSourceRgba(currentColor.r, currentColor.g, currentColor.b, currentColor.a);
128                     context.fill();
129                     context.restore();
130                 }
131             }
132             void __updateColorInfoSize(int width, int height)
133             {
134                 int tmpW = 0;
135                 int tmpH = 0;
136                 this.__colorInfo.getSizeRequest(tmpW, tmpH);
137                 if((width != tmpW) || (height != tmpH))
138                 {
139                     this.__colorInfo.setSizeRequest(width, height);
140                     this.__colorInfo.queueResize();
141                     this.updateColor();
142                 }
143             }
144         }
145 
146         public
147         {
148             /++
149              + Create main window.
150              + Params:
151              +     app = The app to use
152              +/
153             this(Application app)
154             {
155                 // Find and show main window
156                 this.__builder = new Builder();
157                 this.__builder.setApplication(app);
158                 assert(this.__builder.addFromString(import("MainWindow.glade")) != 0);
159                 this.__aw = cast(ApplicationWindow) this.__builder.getObject("MainApplication");
160                 assert(this.__aw !is null);
161                 this.__aw.setApplication(app);
162                 this.__aw.showAll();
163 
164                 { // Manage color info
165                     this.__colorInfo = cast(DrawingArea) this.__builder.getObject("ColorInfo");
166                     assert(this.__colorInfo !is null);
167                     this.__colorInfoSizeInfo = cast(Image) this.__builder.getObject("ColorInfoSizeInfo");
168                     assert(this.__colorInfoSizeInfo !is null);
169                     this.__colorChooser = cast(ColorChooserWidget) this.__builder.getObject("ColorChooser");
170                     assert(this.__colorChooser !is null);
171                     this.__colorHelper = cast(MenuButton) this.__builder.getObject("ColorHelper");
172                     assert(this.__colorHelper !is null);
173 
174                     // Color blob size
175                     this.__colorInfoSizeInfo.addOnDraw(delegate bool(Context cr, Widget widget) {
176                         if(widget == this.__colorInfoSizeInfo)
177                         {
178                             this.__updateColorInfoSize(this.__colorInfoSizeInfo.getAllocatedWidth(), this.__colorInfoSizeInfo.getAllocatedHeight());
179                         }
180                         return false;
181                     });
182 
183                     // Render color blob
184                     this.__colorInfo.addOnDraw(delegate bool(Context cr, Widget widget) {
185                         if(widget == this.__colorInfo)
186                         {
187                             this.__updateColor(cr);
188                         }
189                         return false;
190                     });
191 
192                     // Chooser color
193                     this.__colorChooser.addOnNotify((delegate void(ParamSpec ps, ObjectG go) {
194                         RGBA rgba;
195                         this.__colorChooser.getRgba(rgba);
196                         ColorRGBA newColor = {rgba.red, rgba.green, rgba.blue, rgba.alpha};
197                         this.__mainSystem.currentColor = newColor;
198                     }), "rgba");
199                     this.__colorHelper.addOnToggled(delegate void(ToggleButton button) {
200                         if(button == this.__colorHelper && button.getActive())
201                         {
202                             this.__colorChooser.setProperty("show-editor", false);
203                             RGBA rgba = new RGBA;
204                             {
205                                 ColorRGBA current = this.__mainSystem.currentColor;
206                                 rgba.red = current.r;
207                                 rgba.green = current.g;
208                                 rgba.blue = current.b;
209                                 rgba.alpha = current.a;
210                             }
211                             this.__colorChooser.setRgba(rgba);
212                         }
213                     });
214                 }
215 
216                 { // Manage point size
217                     this.__sizeChooser1 = cast(Scale) this.__builder.getObject("SizeChooser1");
218                     assert(this.__sizeChooser1 !is null);
219                     this.__sizeChooser2 = cast(SpinButton) this.__builder.getObject("SizeChooser2");
220                     assert(this.__sizeChooser2 !is null);
221                     this.__sizeInfo = cast(Label) this.__builder.getObject("SizeInfo");
222                     assert(this.__sizeInfo !is null);
223                     this.__sizeHelper = cast(MenuButton) this.__builder.getObject("SizeHelper");
224                     assert(this.__sizeHelper !is null);
225 
226                     // Value change callback
227                     this.__sizeChooser1.addOnValueChanged(delegate void(Range range) {
228                         if(this.__sizeChooser1 == range)
229                         {
230                             this.__mainSystem.currentSize = this.__sizeChooser1.getValue();
231                         }
232                     });
233 
234                     // When open reset value
235                     this.__sizeHelper.addOnToggled(delegate void(ToggleButton button) {
236                         if(button == this.__sizeHelper && button.getActive())
237                         {
238                             this.__sizeChooser1.setValue(this.__mainSystem.currentSize);
239                             this.__sizeChooser2.setValue(this.__mainSystem.currentSize);
240                         }
241                     });
242                 }
243 
244                 { // Manage menu entries
245                     this.__menuAboutDialog = cast(AboutDialog) this.__builder.getObject("AboutDialog");
246                     assert(this.__menuAboutDialog !is null);
247                     this.__menuAbout = cast(MenuItem) this.__builder.getObject("MenuInfo");
248                     assert(this.__menuAbout !is null);
249                     this.__menuQuit = cast(MenuItem) this.__builder.getObject("MenuQuit");
250                     assert(this.__menuQuit !is null);
251 
252                     // Set infos
253                     this.__menuAboutDialog.setArtists(authorship_artists);
254                     this.__menuAboutDialog.setAuthors(authorship_authors);
255                     this.__menuAboutDialog.setDocumenters(authorship_documenters);
256                     {
257                         string trans = format!("%(%s\n%)")(authorship_translators);
258                         if(trans.length == 0)
259                         {
260                             this.__menuAboutDialog.setTranslatorCredits(null);
261                         }
262                         else
263                         {
264                             this.__menuAboutDialog.setTranslatorCredits(trans[0..$-1]);
265                         }
266                     }
267                     {
268                         string copyright = authorship_copyright_infos;
269                         if(copyright.length > 0)
270                         {
271                             copyright ~= "\n\n";
272                         }
273                         copyright ~= "Used library: GtkD(http://gtkd.org)";
274                         this.__menuAboutDialog.setCopyright(copyright);
275                     }
276                     this.__menuAboutDialog.setVersion(programm_version);
277 
278                     // About
279                     this.__menuAbout.addOnActivate(delegate void(MenuItem mi) {
280                         if(mi == this.__menuAbout)
281                         {
282                             this.__menuAboutDialog.showAll();
283                         }
284                     });
285 
286                     // Quit
287                     this.__menuQuit.addOnActivate(delegate void(MenuItem mi) {
288                         if(mi == this.__menuQuit)
289                         {
290                             this.__mainSystem.quit();
291                         }
292                     });
293                 }
294             }
295 
296             /++
297              + Returns the main System
298              + Returns: The main system.
299              +/
300             @property
301             shared(MainSystem) mainSystem()
302             {
303                 return this.__mainSystem;
304             }
305             /++
306              + Sets the main system. It have to be not null
307              + Params:
308              +     ms = The main system to set
309              + Returns: The main system
310              +/
311             @property
312             shared(MainSystem) mainSystem(shared MainSystem ms)
313             in
314             {
315                 assert(ms !is null);
316             }
317             do
318             {
319                 this.__mainSystem = ms;
320                 return ms;
321             }
322 
323             /++
324              + Request to update the color
325              +/
326             void updateColor()
327             {
328                 this.__colorInfo.queueDraw();
329             }
330 
331             /++
332              + Update size output
333              +/
334             void updateSize(float size)
335             {
336                 this.__sizeInfo.setLabel(format!("%1.2f")(size));
337             }
338         }
339     }
340 }