]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - LinuxGUI/Ventoy2Disk/GTK/ventoy_gtk.c
Add Linux native GUI program for Ventoy2Disk.
[Ventoy.git] / LinuxGUI / Ventoy2Disk / GTK / ventoy_gtk.c
1 /******************************************************************************
2 * ventoy_gtk.c
3 *
4 * Copyright (c) 2021, longpanda <admin@ventoy.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <errno.h>
27 #include <time.h>
28 #include <unistd.h>
29
30 #include <ventoy_define.h>
31 #include <ventoy_json.h>
32 #include <ventoy_util.h>
33 #include <ventoy_disk.h>
34 #include <ventoy_http.h>
35
36 #include <gtk/gtk.h>
37 #include "ventoy_gtk.h"
38
39 int g_secure_boot_support = 0;
40 GtkWidget *g_topWindow = NULL;
41 GtkWidget *g_partCfgWindow = NULL;
42 GtkBuilder *g_pXmlBuilder = NULL;
43 GtkComboBoxText *g_dev_combobox = NULL;
44 GtkButton *g_refresh_button = NULL;
45 GtkButton *g_install_button = NULL;
46 GtkButton *g_update_button = NULL;
47 GtkMenu *g_lang_menu = NULL;;
48 GtkCheckMenuItem *g_menu_item_secure_boot = NULL;
49 GtkCheckMenuItem *g_menu_item_mbr = NULL;
50 GtkCheckMenuItem *g_menu_item_gpt = NULL;
51 GtkCheckMenuItem *g_menu_item_show_all = NULL;
52 GtkLabel *g_device_title = NULL;
53 GtkLabel *g_label_local_part_style = NULL;
54 GtkLabel *g_label_dev_part_style = NULL;
55 GtkLabel *g_label_local_ver = NULL;
56 GtkLabel *g_label_disk_ver = NULL;
57 GtkLabel *g_label_status = NULL;
58 GtkImage *g_image_secure_local = NULL;
59 GtkImage *g_image_secure_device = NULL;
60 GtkToggleButton *g_part_align_checkbox = NULL;
61 GtkToggleButton *g_part_preserve_checkbox = NULL;
62 GtkEntry *g_part_reserve_space_value = NULL;
63 GtkComboBoxText *g_part_space_unit_combox = NULL;
64 GtkProgressBar *g_progress_bar = NULL;
65 VTOY_JSON *g_languages_json = NULL;
66 int g_languages_toggled_proc = 0;
67 int g_dev_changed_proc = 0;
68 gboolean g_align_part_with_4k = TRUE;
69 gboolean g_preserve_space_check = FALSE;
70 int g_preserve_space_unit = 1;
71 int g_preserve_space_number = 0;
72 gboolean g_thread_run = FALSE;
73
74 const char *language_string(const char *id)
75 {
76 const char *pName = NULL;
77 VTOY_JSON *node = NULL;
78 const char *pCurLang = ventoy_code_get_cur_language();
79
80 for (node = g_languages_json->pstChild; node; node = node->pstNext)
81 {
82 pName = vtoy_json_get_string_ex(node->pstChild, "name");
83 if (0 == g_strcmp0(pName, pCurLang))
84 {
85 break;
86 }
87 }
88
89 if (NULL == node)
90 {
91 return "xxx";
92 }
93
94 return vtoy_json_get_string_ex(node->pstChild, id);
95 }
96
97 int msgbox(GtkMessageType type, GtkButtonsType buttons, const char *strid)
98 {
99 int ret;
100 GtkWidget *pMsgBox = NULL;
101
102 pMsgBox = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, type, buttons, "%s", language_string(strid));
103
104 ret = gtk_dialog_run(GTK_DIALOG(pMsgBox));
105 gtk_widget_destroy(pMsgBox);
106
107 return ret;
108 }
109
110
111 static void set_item_visible(const char *id, int visible)
112 {
113 GtkWidget *pWidget = NULL;
114
115 pWidget = GTK_WIDGET(gtk_builder_get_object(g_pXmlBuilder, id));
116 if (visible)
117 {
118 gtk_widget_show(pWidget);
119 }
120 else
121 {
122 gtk_widget_hide(pWidget);
123 }
124 }
125
126 static void init_part_style_menu(void)
127 {
128 int style;
129
130 style = ventoy_code_get_cur_part_style();
131 gtk_check_menu_item_set_active(g_menu_item_mbr, (0 == style));
132 gtk_check_menu_item_set_active(g_menu_item_gpt, (1 == style));
133 gtk_label_set_text(g_label_local_part_style, style ? "GPT" : "MBR");
134 }
135
136 static void select_language(const char *lang)
137 {
138 const char *pName = NULL;
139 const char *pPos = NULL;
140 const char *pDevice = NULL;
141 VTOY_JSON *node = NULL;
142 char device[256];
143
144 for (node = g_languages_json->pstChild; node; node = node->pstNext)
145 {
146 pName = vtoy_json_get_string_ex(node->pstChild, "name");
147 if (0 == g_strcmp0(pName, lang))
148 {
149 break;
150 }
151 }
152
153 if (NULL == node)
154 {
155 return;
156 }
157
158 pDevice = gtk_label_get_text(g_device_title);
159 if (pDevice && (pPos = strchr(pDevice, '[')) != NULL)
160 {
161 g_snprintf(device, sizeof(device), "%s %s", vtoy_json_get_string_ex(node->pstChild, "STR_DEVICE"), pPos);
162 gtk_label_set_text(g_device_title, device);
163 }
164 else
165 {
166 gtk_label_set_text(g_device_title, vtoy_json_get_string_ex(node->pstChild, "STR_DEVICE"));
167 }
168
169 LANG_LABEL_TEXT("label_local_ver", "STR_LOCAL_VER");
170 LANG_LABEL_TEXT("label_device_ver", "STR_DISK_VER");
171
172 LANG_LABEL_TEXT("label_status", "STR_STATUS");
173
174 LANG_BUTTON_TEXT("button_install", "STR_INSTALL");
175 LANG_BUTTON_TEXT("button_update", "STR_UPDATE");
176
177 LANG_MENU_ITEM_TEXT("menu_option", "STR_MENU_OPTION");
178 LANG_MENU_ITEM_TEXT("menu_item_secure", "STR_MENU_SECURE_BOOT");
179 LANG_MENU_ITEM_TEXT("menu_part_style", "STR_MENU_PART_STYLE");
180 LANG_MENU_ITEM_TEXT("menu_item_part_cfg", "STR_MENU_PART_CFG");
181 LANG_MENU_ITEM_TEXT("menu_item_clear", "STR_MENU_CLEAR");
182 LANG_MENU_ITEM_TEXT("menu_item_show_all", "STR_SHOW_ALL_DEV");
183
184 LANG_BUTTON_TEXT("space_check_btn", "STR_PRESERVE_SPACE");
185 LANG_BUTTON_TEXT("space_align_btn", "STR_PART_ALIGN_4KB");
186 LANG_BUTTON_TEXT("button_partcfg_ok", "STR_BTN_OK");
187 LANG_BUTTON_TEXT("button_partcfg_cancel", "STR_BTN_CANCEL");
188
189 gtk_window_set_title(GTK_WINDOW(g_partCfgWindow), vtoy_json_get_string_ex(node->pstChild, "STR_MENU_PART_CFG"));
190
191 /*
192 * refresh screen
193 */
194
195 gtk_widget_hide(g_topWindow);
196 gtk_widget_show(g_topWindow);
197 }
198
199
200 void on_secure_boot_toggled(GtkMenuItem *menuItem, gpointer data)
201 {
202 g_secure_boot_support = 1 - g_secure_boot_support;
203
204 vlog("on_secure_boot_toggled %d\n",g_secure_boot_support );
205
206 if (g_secure_boot_support)
207 {
208 gtk_widget_show((GtkWidget *)g_image_secure_local);
209 }
210 else
211 {
212 gtk_widget_hide((GtkWidget *)g_image_secure_local);
213 }
214 }
215
216 void on_devlist_changed(GtkWidget *widget, gpointer data)
217 {
218 int active;
219 ventoy_disk *cur = NULL;
220 char version[512];
221
222 if (g_dev_changed_proc == 0)
223 {
224 return;
225 }
226
227 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), FALSE);
228
229 gtk_widget_hide((GtkWidget *)g_image_secure_device);
230 gtk_label_set_markup(g_label_disk_ver, "");
231 gtk_label_set_text(g_label_dev_part_style, "");
232
233 active = gtk_combo_box_get_active((GtkComboBox *)g_dev_combobox);
234 if (active < 0 || active >= g_disk_num)
235 {
236 vlog("invalid active combox id %d\n", active);
237 return;
238 }
239
240 cur = g_disk_list + active;
241 if (cur->vtoydata.ventoy_valid)
242 {
243 if (cur->vtoydata.secure_boot_flag)
244 {
245 gtk_widget_show((GtkWidget *)g_image_secure_device);
246 }
247 else
248 {
249 gtk_widget_hide((GtkWidget *)g_image_secure_device);
250 }
251
252 if (g_secure_boot_support != cur->vtoydata.secure_boot_flag)
253 {
254 gtk_check_menu_item_set_active(g_menu_item_secure_boot, 1 - g_secure_boot_support);
255 }
256
257 g_snprintf(version, sizeof(version), VTOY_VER_FMT, cur->vtoydata.ventoy_ver);
258 gtk_label_set_markup(g_label_disk_ver, version);
259 gtk_label_set_text(g_label_dev_part_style, cur->vtoydata.partition_style ? "GPT" : "MBR");
260
261 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), TRUE);
262 }
263 else
264 {
265 if (g_secure_boot_support)
266 {
267 gtk_check_menu_item_set_active(g_menu_item_secure_boot, 1 - g_secure_boot_support);
268 }
269 }
270 }
271
272
273 void on_language_toggled(GtkMenuItem *menuItem, gpointer data)
274 {
275 const char *cur_lang = NULL;
276
277 if (g_languages_toggled_proc == 0)
278 {
279 return;
280 }
281
282 cur_lang = ventoy_code_get_cur_language();
283 if (g_strcmp0(cur_lang, (char *)data) != 0)
284 {
285 ventoy_code_set_cur_language((char *)data);
286 select_language((char *)data);
287 }
288 }
289
290 void on_part_style_toggled(GtkMenuItem *menuItem, gpointer data)
291 {
292 int style;
293
294 style = ventoy_code_get_cur_part_style();
295 ventoy_code_set_cur_part_style(1 - style);
296
297 gtk_label_set_text(g_label_local_part_style, style ? "MBR" : "GPT");
298 }
299
300 static ventoy_disk *select_active_dev(const char *select, int *activeid)
301 {
302 int i;
303 ventoy_disk *cur = NULL;
304
305 /* find the match one */
306 if (select)
307 {
308 for (i = 0; i < g_disk_num; i++)
309 {
310 cur = g_disk_list + i;
311 if (strcmp(cur->disk_name, select) == 0)
312 {
313 *activeid = i;
314 return cur;
315 }
316 }
317 }
318
319 /* find the first one that installed with Ventoy */
320 for (i = 0; i < g_disk_num; i++)
321 {
322 cur = g_disk_list + i;
323 if (cur->vtoydata.ventoy_valid)
324 {
325 *activeid = i;
326 return cur;
327 }
328 }
329
330 /* find the first USB interface device */
331 for (i = 0; i < g_disk_num; i++)
332 {
333 cur = g_disk_list + i;
334 if (cur->type == VTOY_DEVICE_USB)
335 {
336 *activeid = i;
337 return cur;
338 }
339 }
340
341 /* use the first one */
342 *activeid = 0;
343 return g_disk_list;
344 }
345
346 static void fill_dev_list(const char *select)
347 {
348 int i;
349 int alldev;
350 int activeid;
351 int count = 0;
352 char line[512];
353 ventoy_disk *cur = NULL;
354 ventoy_disk *active = NULL;
355 GtkListStore *store = NULL;
356
357 g_dev_changed_proc = 0;
358
359 alldev = ventoy_code_get_cur_show_all();
360
361 vlog("fill_dev_list total disk: %d showall:%d\n", g_disk_num, alldev);
362
363 /* gtk_combo_box_text_remove_all */
364 store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(g_dev_combobox)));
365 gtk_list_store_clear(store);
366
367 for (i = 0; i < g_disk_num; i++)
368 {
369 cur = g_disk_list + i;
370
371 if (alldev == 0 && cur->type != VTOY_DEVICE_USB)
372 {
373 continue;
374 }
375
376 g_snprintf(line, sizeof(line), "%s [%s] %s", cur->disk_name, cur->human_readable_size, cur->disk_model);
377 gtk_combo_box_text_append_text(g_dev_combobox, line);
378 count++;
379 }
380
381 active = select_active_dev(select, &activeid);
382 if (active)
383 {
384 vlog("combox count:%d, active:%s id:%d\n", count, active->disk_name, activeid);
385 gtk_combo_box_set_active((GtkComboBox *)g_dev_combobox, activeid);
386 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), TRUE);
387 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), active->vtoydata.ventoy_valid);
388 }
389 else
390 {
391 vlog("combox count:%d, no active id\n", count);
392 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), FALSE);
393 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), FALSE);
394 }
395
396 g_dev_changed_proc = 1;
397 on_devlist_changed(NULL, NULL);
398 }
399
400
401 void on_show_all_toggled(GtkMenuItem *menuItem, gpointer data)
402 {
403 int show_all = ventoy_code_get_cur_show_all();
404
405 ventoy_code_set_cur_show_all(1 - show_all);
406 fill_dev_list(NULL);
407 }
408
409 void on_button_refresh_clicked(GtkWidget *widget, gpointer data)
410 {
411 if (g_thread_run || ventoy_code_is_busy())
412 {
413 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
414 return;
415 }
416
417 ventoy_code_refresh_device();
418 fill_dev_list(NULL);
419 }
420
421 static void set_progress_bar_percent(int percent)
422 {
423 char *pos = NULL;
424 const char *text = NULL;
425 char tmp[128];
426
427 gtk_progress_bar_set_fraction(g_progress_bar, percent * 1.0 / 100);
428 vlog("set percent %d\n", percent);
429
430 text = language_string("STR_STATUS");
431 if (percent == 0)
432 {
433 gtk_label_set_text(g_label_status, text);
434 }
435 else
436 {
437 g_snprintf(tmp, sizeof(tmp), "%s", text);
438 pos = strchr(tmp, '-');
439 if (pos)
440 {
441 g_snprintf(pos + 2, sizeof(tmp), "%d%%", percent);
442 gtk_label_set_text(g_label_status, tmp);
443 }
444 }
445 }
446
447 void on_clear_ventoy(GtkMenuItem *menuItem, gpointer data)
448 {
449 int ret;
450 int active;
451 char buf[1024];
452 char out[256];
453 char disk_name[32];
454 ventoy_disk *cur = NULL;
455
456 if (g_thread_run || ventoy_code_is_busy())
457 {
458 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
459 return;
460 }
461
462 active = gtk_combo_box_get_active((GtkComboBox *)g_dev_combobox);
463 if (active < 0 || active >= g_disk_num)
464 {
465 vlog("invalid active combox id %d\n", active);
466 return;
467 }
468
469 if (GTK_RESPONSE_CANCEL == msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, "STR_INSTALL_TIP"))
470 {
471 return;
472 }
473
474 if (GTK_RESPONSE_CANCEL == msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, "STR_INSTALL_TIP2"))
475 {
476 return;
477 }
478
479 gtk_widget_set_sensitive (GTK_WIDGET(g_refresh_button), FALSE);
480 gtk_widget_set_sensitive (GTK_WIDGET(g_install_button), FALSE);
481 gtk_widget_set_sensitive (GTK_WIDGET(g_update_button), FALSE);
482 g_thread_run = TRUE;
483
484
485 cur = g_disk_list + active;
486 g_snprintf(disk_name, sizeof(disk_name), "%s", cur->disk_name);
487 g_snprintf(buf, sizeof(buf), "{\"method\":\"clean\",\"disk\":\"%s\"}", disk_name);
488
489 out[0] = 0;
490 ventoy_func_handler(buf, out, sizeof(out));
491 vlog("func handler clean <%s>\n", out);
492
493 if (strstr(out, "success"))
494 {
495 ret = ventoy_code_get_result();
496 ventoy_code_refresh_device();
497 cur = NULL;
498 }
499 else
500 {
501 ret = 1;
502 }
503
504 if (ret == 0)
505 {
506 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_CLEAR_SUCCESS");
507 }
508 else
509 {
510 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_CLEAR_FAILED");
511 }
512
513 set_progress_bar_percent(0);
514 gtk_widget_set_sensitive(GTK_WIDGET(g_refresh_button), TRUE);
515 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), TRUE);
516 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), TRUE);
517
518 fill_dev_list(disk_name);
519 g_thread_run = FALSE;
520 }
521
522 static int install_proc(ventoy_disk *cur)
523 {
524 int ret = 0;
525 int pos = 0;
526 int buflen = 0;
527 int percent = 0;
528 char buf[1024];
529 char dec[64];
530 char out[256];
531 char disk_name[32];
532 long long space;
533
534 vlog("install_thread ...\n");
535
536 g_snprintf(disk_name, sizeof(disk_name), "%s", cur->disk_name);
537
538 buflen = sizeof(buf);
539 VTOY_JSON_FMT_BEGIN(pos, buf, buflen);
540 VTOY_JSON_FMT_OBJ_BEGIN();
541 VTOY_JSON_FMT_STRN("method", "install");
542 VTOY_JSON_FMT_STRN("disk", disk_name);
543
544 if (g_preserve_space_check)
545 {
546 space = g_preserve_space_number;
547 if (g_preserve_space_unit == 1)
548 {
549 space = space * 1024 * 1024 * 1024LL;
550 }
551 else
552 {
553 space = space * 1024 * 1024LL;
554 }
555
556 snprintf(dec, sizeof(dec), "%lld", space);
557 VTOY_JSON_FMT_STRN("reserve_space", dec);
558 }
559 else
560 {
561 VTOY_JSON_FMT_STRN("reserve_space", "0");
562 }
563
564 VTOY_JSON_FMT_UINT("partstyle", ventoy_code_get_cur_part_style());
565 VTOY_JSON_FMT_UINT("secure_boot", g_secure_boot_support);
566 VTOY_JSON_FMT_UINT("align_4kb", g_align_part_with_4k);
567 VTOY_JSON_FMT_OBJ_END();
568 VTOY_JSON_FMT_END(pos);
569
570 out[0] = 0;
571 ventoy_func_handler(buf, out, sizeof(out));
572 vlog("func handler install <%s>\n", out);
573
574 if (strstr(out, "success"))
575 {
576 while (percent != 100)
577 {
578 percent = ventoy_code_get_percent();
579 set_progress_bar_percent(percent);
580 GTK_MSG_ITERATION();
581 usleep(50 * 1000);
582 }
583
584 ret = ventoy_code_get_result();
585 ventoy_code_refresh_device();
586 cur = NULL;
587 }
588 else
589 {
590 ret = 1;
591 }
592
593 if (ret)
594 {
595 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_INSTALL_FAILED");
596 }
597 else
598 {
599 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_INSTALL_SUCCESS");
600 }
601
602 set_progress_bar_percent(0);
603 gtk_widget_set_sensitive(GTK_WIDGET(g_refresh_button), TRUE);
604 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), TRUE);
605 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), TRUE);
606
607 fill_dev_list(disk_name);
608 g_thread_run = FALSE;
609
610 return 0;
611 }
612
613 void on_button_install_clicked(GtkWidget *widget, gpointer data)
614 {
615 int active;
616 ventoy_disk *cur = NULL;
617
618 if (g_thread_run || ventoy_code_is_busy())
619 {
620 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
621 return;
622 }
623
624 active = gtk_combo_box_get_active((GtkComboBox *)g_dev_combobox);
625 if (active < 0 || active >= g_disk_num)
626 {
627 vlog("invalid active combox id %d\n", active);
628 return;
629 }
630
631 cur = g_disk_list + active;
632
633 if (ventoy_code_get_cur_part_style() == 0 && cur->size_in_byte > 2199023255552ULL)
634 {
635 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_DISK_2TB_MBR_ERROR");
636 return;
637 }
638
639 if (GTK_RESPONSE_CANCEL == msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, "STR_INSTALL_TIP"))
640 {
641 return;
642 }
643
644 if (GTK_RESPONSE_CANCEL == msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, "STR_INSTALL_TIP2"))
645 {
646 return;
647 }
648
649 gtk_widget_set_sensitive (GTK_WIDGET(g_refresh_button), FALSE);
650 gtk_widget_set_sensitive (GTK_WIDGET(g_install_button), FALSE);
651 gtk_widget_set_sensitive (GTK_WIDGET(g_update_button), FALSE);
652
653 g_thread_run = TRUE;
654
655 install_proc(cur);
656 }
657
658
659 static int update_proc(ventoy_disk *cur)
660 {
661 int ret = 0;
662 int percent = 0;
663 char buf[1024];
664 char out[256];
665 char disk_name[32];
666
667 g_snprintf(disk_name, sizeof(disk_name), "%s", cur->disk_name);
668 g_snprintf(buf, sizeof(buf), "{\"method\":\"update\",\"disk\":\"%s\",\"secure_boot\":%d}",
669 disk_name, g_secure_boot_support);
670
671 out[0] = 0;
672 ventoy_func_handler(buf, out, sizeof(out));
673 vlog("func handler update <%s>\n", out);
674
675 if (strstr(out, "success"))
676 {
677 while (percent != 100)
678 {
679 percent = ventoy_code_get_percent();
680 set_progress_bar_percent(percent);
681 GTK_MSG_ITERATION();
682 usleep(50 * 1000);
683 }
684
685 ret = ventoy_code_get_result();
686 ventoy_code_refresh_device();
687 cur = NULL;
688 }
689 else
690 {
691 ret = 1;
692 }
693
694 if (ret)
695 {
696 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_UPDATE_FAILED");
697 }
698 else
699 {
700 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_UPDATE_SUCCESS");
701 }
702
703 set_progress_bar_percent(0);
704 gtk_widget_set_sensitive(GTK_WIDGET(g_refresh_button), TRUE);
705 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), TRUE);
706 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), TRUE);
707
708 fill_dev_list(disk_name);
709 g_thread_run = FALSE;
710
711 return 0;
712 }
713
714
715 void on_button_update_clicked(GtkWidget *widget, gpointer data)
716 {
717 int active;
718 ventoy_disk *cur = NULL;
719
720 if (g_thread_run || ventoy_code_is_busy())
721 {
722 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
723 return;
724 }
725
726 active = gtk_combo_box_get_active((GtkComboBox *)g_dev_combobox);
727 if (active < 0 || active >= g_disk_num)
728 {
729 vlog("invalid active combox id %d\n", active);
730 return;
731 }
732 cur = g_disk_list + active;
733
734 if (cur->vtoydata.ventoy_valid == 0)
735 {
736 vlog("invalid ventoy version.\n");
737 return;
738 }
739
740 if (GTK_RESPONSE_CANCEL == msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK_CANCEL, "STR_UPDATE_TIP"))
741 {
742 return;
743 }
744
745 gtk_widget_set_sensitive (GTK_WIDGET(g_refresh_button), FALSE);
746 gtk_widget_set_sensitive (GTK_WIDGET(g_install_button), FALSE);
747 gtk_widget_set_sensitive (GTK_WIDGET(g_update_button), FALSE);
748
749 g_thread_run = TRUE;
750
751 update_proc(cur);
752 }
753
754 static gint lang_compare(gconstpointer a, gconstpointer b)
755 {
756 const char *name1 = (const char *)a;
757 const char *name2 = (const char *)b;
758
759 if (strncmp(name1, "Chinese Simplified", 18) == 0)
760 {
761 return -1;
762 }
763 else if (strncmp(name2, "Chinese Simplified", 18) == 0)
764 {
765 return 1;
766 }
767 else
768 {
769 return g_strcmp0(name1, name2);
770 }
771 }
772
773 static int load_languages(void)
774 {
775 int size = 0;
776 char *pBuf = NULL;
777 char *pCur = NULL;
778 const char *pCurLang = NULL;
779 const char *pName = NULL;
780 VTOY_JSON *json = NULL;
781 VTOY_JSON *node = NULL;
782 VTOY_JSON *lang = NULL;
783 GSList *pGroup = NULL;
784 GList *pNameNode = NULL;
785 GList *pNameList = NULL;
786 GtkRadioMenuItem *pItem = NULL;
787
788 vlog("load_languages ...\n");
789
790 pCurLang = ventoy_code_get_cur_language();
791 if (pCurLang[0] == 0)
792 {
793 pName = getenv("LANG");
794 if (pName && strncmp(pName, "zh_CN", 5) == 0)
795 {
796 ventoy_code_set_cur_language("Chinese Simplified (简体中文)");
797 }
798 else
799 {
800 ventoy_code_set_cur_language("English (English)");
801 }
802 pCurLang = ventoy_code_get_cur_language();
803 }
804
805 vlog("current language <%s>\n", pCurLang);
806
807 ventoy_read_file_to_buf("./tool/languages.json", 1, (void **)&pBuf, &size);
808
809 json = vtoy_json_create();
810 vtoy_json_parse(json, pBuf);
811 g_languages_json = json;
812
813 for (node = json->pstChild; node; node = node->pstNext)
814 {
815 pName = vtoy_json_get_string_ex(node->pstChild, "name");
816 if (pName)
817 {
818 pNameList = g_list_append(pNameList, (gpointer)pName);
819 }
820
821 for (lang = node->pstChild; lang; lang = lang->pstNext)
822 {
823 if (lang->enDataType == JSON_TYPE_STRING)
824 {
825 pCur = lang->unData.pcStrVal;
826 while (*pCur)
827 {
828 if (*pCur == '#')
829 {
830 *pCur = '\r';
831 }
832 else if (*pCur == '@')
833 {
834 *pCur = '\n';
835 }
836 pCur++;
837 }
838 }
839 }
840 }
841
842 pNameList = g_list_sort(pNameList, lang_compare);
843
844 for (pNameNode = g_list_first(pNameList); pNameNode; pNameNode = g_list_next(pNameNode))
845 {
846 pName = (char *)(pNameNode->data);
847 pItem = (GtkRadioMenuItem *)gtk_radio_menu_item_new_with_label(pGroup, pName);
848 pGroup = gtk_radio_menu_item_get_group(pItem);
849
850 if (strcmp(pCurLang, pName) == 0)
851 {
852 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(pItem), TRUE);
853 }
854
855 g_signal_connect(pItem, "toggled", G_CALLBACK(on_language_toggled), (gpointer)pName);
856 gtk_widget_show((GtkWidget *)pItem);
857 gtk_menu_shell_append(GTK_MENU_SHELL(g_lang_menu), (GtkWidget *)pItem);
858 }
859
860 g_list_free(pNameList);
861 free(pBuf);
862
863 select_language(pCurLang);
864 g_languages_toggled_proc = 1;
865
866 return 0;
867 }
868
869 void on_part_cfg_ok(GtkWidget *widget, gpointer data)
870 {
871 const char *pos = NULL;
872 const char *input = NULL;
873 char device[256];
874 gboolean checked = gtk_toggle_button_get_active(g_part_preserve_checkbox);
875
876 if (checked)
877 {
878 input = gtk_entry_get_text(g_part_reserve_space_value);
879 if (input == NULL || input[0] == 0)
880 {
881 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_SPACE_VAL_INVALID");
882 return;
883 }
884
885 for (pos = input; *pos; pos++)
886 {
887 if (*pos < '0' || *pos >= '9')
888 {
889 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_SPACE_VAL_INVALID");
890 return;
891 }
892 }
893
894 g_preserve_space_unit = gtk_combo_box_get_active((GtkComboBox *)g_part_space_unit_combox);
895 g_preserve_space_number = (int)strtol(input, NULL, 10);
896
897 g_snprintf(device, sizeof(device), "%s [ -%d%s ]",
898 language_string("STR_DEVICE"), g_preserve_space_number,
899 g_preserve_space_unit ? "GB" : "MB");
900 gtk_label_set_text(g_device_title, device);
901 }
902 else
903 {
904 gtk_label_set_text(g_device_title, language_string("STR_DEVICE"));
905 }
906
907 g_preserve_space_check = checked;
908 g_align_part_with_4k = gtk_toggle_button_get_active(g_part_align_checkbox);
909 gtk_widget_hide(g_partCfgWindow);
910 }
911
912 void on_part_cfg_cancel(GtkWidget *widget, gpointer data)
913 {
914 gtk_widget_hide(g_partCfgWindow);
915 }
916
917 int on_part_cfg_close(GtkWidget *widget, gpointer data)
918 {
919 gtk_widget_hide(g_partCfgWindow);
920 return TRUE;
921 }
922
923 void on_part_config(GtkMenuItem *menuItem, gpointer data)
924 {
925 char value[64];
926
927 gtk_toggle_button_set_active(g_part_align_checkbox, g_align_part_with_4k);
928 gtk_toggle_button_set_active(g_part_preserve_checkbox, g_preserve_space_check);
929 gtk_widget_set_sensitive(GTK_WIDGET(g_part_reserve_space_value), g_preserve_space_check);
930 gtk_widget_set_sensitive(GTK_WIDGET(g_part_space_unit_combox), g_preserve_space_check);
931
932 if (g_preserve_space_check)
933 {
934 g_snprintf(value, sizeof(value), "%d", g_preserve_space_number);
935 gtk_entry_set_text(g_part_reserve_space_value, value);
936 gtk_combo_box_set_active((GtkComboBox *)g_part_space_unit_combox, g_preserve_space_unit);
937 }
938
939 gtk_widget_show_all(g_partCfgWindow);
940 }
941
942 void on_reserve_space_toggled(GtkMenuItem *menuItem, gpointer data)
943 {
944 gboolean checked = gtk_toggle_button_get_active(g_part_preserve_checkbox);
945
946 gtk_widget_set_sensitive(GTK_WIDGET(g_part_reserve_space_value), checked);
947 gtk_widget_set_sensitive(GTK_WIDGET(g_part_space_unit_combox), checked);
948 }
949
950 static void init_part_cfg_window(GtkBuilder *pBuilder)
951 {
952 #if GTK_CHECK_VERSION(3, 0, 0)
953 GtkWidget *pHeader = NULL;
954
955 pHeader = gtk_header_bar_new();
956 gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(pHeader), FALSE);
957 gtk_window_set_titlebar (GTK_WINDOW(g_partCfgWindow), pHeader);
958 gtk_window_set_title(GTK_WINDOW(g_partCfgWindow), "Partition Configuration");
959 #endif
960
961 gtk_combo_box_set_active((GtkComboBox *)g_part_space_unit_combox, g_preserve_space_unit);
962 gtk_widget_set_sensitive (GTK_WIDGET(g_part_reserve_space_value), FALSE);
963 gtk_widget_set_sensitive (GTK_WIDGET(g_part_space_unit_combox), FALSE);
964
965 SIGNAL("space_check_btn", "toggled", on_reserve_space_toggled);
966
967 SIGNAL("button_partcfg_ok", "clicked", on_part_cfg_ok);
968 SIGNAL("button_partcfg_cancel", "clicked", on_part_cfg_cancel);
969 SIGNAL("part_cfg_dlg", "delete_event", on_part_cfg_close);
970 }
971
972 void on_init_window(GtkBuilder *pBuilder)
973 {
974 GSList *pGroup = NULL;
975 char version[512];
976
977 vlog("on_init_window ...\n");
978
979 g_pXmlBuilder = pBuilder;
980 g_topWindow = BUILDER_ITEM(GtkWidget, "window");
981 g_partCfgWindow = BUILDER_ITEM(GtkWidget, "part_cfg_dlg");
982
983 g_dev_combobox = BUILDER_ITEM(GtkComboBoxText, "combobox_devlist");
984 g_refresh_button = BUILDER_ITEM(GtkButton, "button_refresh");
985 g_install_button = BUILDER_ITEM(GtkButton, "button_install");
986 g_update_button = BUILDER_ITEM(GtkButton, "button_update");
987
988 g_lang_menu = BUILDER_ITEM(GtkMenu, "submenu_language");
989 g_menu_item_secure_boot = BUILDER_ITEM(GtkCheckMenuItem, "menu_item_secure");
990 g_menu_item_mbr = BUILDER_ITEM(GtkCheckMenuItem, "menu_item_mbr");
991 g_menu_item_gpt = BUILDER_ITEM(GtkCheckMenuItem, "menu_item_gpt");
992 g_menu_item_show_all = BUILDER_ITEM(GtkCheckMenuItem, "menu_item_show_all");
993
994 g_device_title = BUILDER_ITEM(GtkLabel, "label_device");
995 g_label_local_part_style = BUILDER_ITEM(GtkLabel, "label_local_part_style");
996 g_label_dev_part_style = BUILDER_ITEM(GtkLabel, "label_dev_part_style");
997
998 g_label_local_ver = BUILDER_ITEM(GtkLabel, "label_local_ver_value");
999 g_label_disk_ver = BUILDER_ITEM(GtkLabel, "label_dev_ver_value");
1000
1001 g_label_status = BUILDER_ITEM(GtkLabel, "label_status");
1002
1003 g_image_secure_local = BUILDER_ITEM(GtkImage, "image_secure_local");
1004 g_image_secure_device = BUILDER_ITEM(GtkImage, "image_secure_dev");
1005
1006 g_part_preserve_checkbox = BUILDER_ITEM(GtkToggleButton, "space_check_btn");
1007 g_part_align_checkbox = BUILDER_ITEM(GtkToggleButton, "space_align_btn");
1008
1009 g_part_reserve_space_value = BUILDER_ITEM(GtkEntry, "entry_reserve_space");
1010 g_part_space_unit_combox = BUILDER_ITEM(GtkComboBoxText, "comboboxtext_unit");
1011
1012 g_progress_bar = BUILDER_ITEM(GtkProgressBar, "progressbar1");
1013
1014 init_part_cfg_window(pBuilder);
1015
1016 /* for gtk2 */
1017 gtk_frame_set_shadow_type(BUILDER_ITEM(GtkFrame, "frame_dummy1"), GTK_SHADOW_NONE);
1018 gtk_frame_set_shadow_type(BUILDER_ITEM(GtkFrame, "frame_dummy2"), GTK_SHADOW_NONE);
1019
1020 /* join group */
1021 pGroup = gtk_radio_menu_item_get_group((GtkRadioMenuItem *)g_menu_item_mbr);
1022 gtk_radio_menu_item_set_group((GtkRadioMenuItem *)g_menu_item_gpt, pGroup);
1023
1024 gtk_widget_hide((GtkWidget *)g_image_secure_local);
1025 gtk_widget_hide((GtkWidget *)g_image_secure_device);
1026
1027 g_snprintf(version, sizeof(version), VTOY_VER_FMT, ventoy_get_local_version());
1028 gtk_label_set_markup(g_label_local_ver, version);
1029
1030 init_part_style_menu();
1031 gtk_check_menu_item_set_active(g_menu_item_show_all, ventoy_code_get_cur_show_all());
1032
1033 load_languages();
1034
1035 SIGNAL("combobox_devlist", "changed", on_devlist_changed);
1036
1037 SIGNAL("button_refresh", "clicked", on_button_refresh_clicked);
1038 SIGNAL("button_install", "clicked", on_button_install_clicked);
1039 SIGNAL("button_update", "clicked", on_button_update_clicked);
1040
1041 SIGNAL("menu_item_secure", "toggled", on_secure_boot_toggled);
1042 SIGNAL("menu_item_mbr", "toggled", on_part_style_toggled);
1043 SIGNAL("menu_item_show_all", "toggled", on_show_all_toggled);
1044
1045 SIGNAL("menu_item_part_cfg", "activate", on_part_config);
1046 SIGNAL("menu_item_clear", "activate", on_clear_ventoy);
1047
1048 fill_dev_list(NULL);
1049
1050 return;
1051 }
1052
1053 int on_exit_window(GtkWidget *widget, gpointer data)
1054 {
1055 vlog("on_exit_window ...\n");
1056
1057 if (g_thread_run || ventoy_code_is_busy())
1058 {
1059 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
1060 return TRUE;
1061 }
1062
1063 ventoy_code_save_cfg();
1064 return FALSE;
1065 }
1066