X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=src%2Fxo-file.c;h=bff53a1a206caf18dc2b979dd3923c8f914921dd;hb=fd008f43f9e0848369972b0bc0e52129380e229b;hp=23678eb09a6589c5fd285a31d37a38eb2dce4ec3;hpb=162ca4d7cb693f9348f0ea478e7cfdf3c79cad6d;p=xournal.git diff --git a/src/xo-file.c b/src/xo-file.c index 23678eb..bff53a1 100644 --- a/src/xo-file.c +++ b/src/xo-file.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "xournal.h" #include "xo-interface.h" @@ -21,7 +22,7 @@ #include "xo-misc.h" #include "xo-file.h" -const char *tool_names[NUM_STROKE_TOOLS] = {"pen", "eraser", "highlighter"}; +const char *tool_names[NUM_TOOLS] = {"pen", "eraser", "highlighter", "", "", "selectrect", "vertspace", "hand"}; const char *color_names[COLOR_MAX] = {"black", "blue", "red", "green", "gray", "lightblue", "lightgreen", "magenta", "orange", "yellow", "white"}; const char *bgtype_names[3] = {"solid", "pixmap", "pdf"}; @@ -29,6 +30,8 @@ const char *bgcolor_names[COLOR_MAX] = {"", "blue", "pink", "green", "", "", "", "", "orange", "yellow", "white"}; const char *bgstyle_names[4] = {"plain", "lined", "ruled", "graph"}; const char *file_domain_names[3] = {"absolute", "attach", "clone"}; +const char *unit_names[4] = {"cm", "in", "px", "pt"}; +int PDFTOPPM_PRINTING_DPI, GS_BITMAP_DPI; // creates a new empty journal @@ -81,6 +84,8 @@ gboolean save_journal(const char *filename) f = gzopen(filename, "w"); if (f==NULL) return FALSE; chk_attach_names(); + + setlocale(LC_NUMERIC, "C"); gzprintf(f, "\n" "Xournal document - see http://math.mit.edu/~auroux/software/xournal/\n" @@ -138,6 +143,7 @@ gboolean save_journal(const char *filename) tmpf = fopen(tmpfn, "w"); if (tmpf != NULL && fwrite(pdfbuf, 1, pdflen, tmpf) == pdflen) success = TRUE; + g_free(pdfbuf); fclose(tmpf); } if (!success) { @@ -178,6 +184,8 @@ gboolean save_journal(const char *filename) gzprintf(f, "\n"); } gzclose(f); + setlocale(LC_NUMERIC, ""); + return TRUE; } @@ -200,6 +208,13 @@ gboolean close_journal(void) use new_journal() to reinitialize them */ } +// sanitize a string containing floats, in case it may have , instead of . + +void cleanup_numeric(char *s) +{ + while (*s!=0) { if (*s==',') *s='.'; s++; } +} + // the XML parser functions for open_journal() struct Journal tmpJournal; @@ -252,13 +267,15 @@ void xoj_parser_start_element(GMarkupParseContext *context, while (*attribute_names!=NULL) { if (!strcmp(*attribute_names, "width")) { if (has_attr & 1) *error = xoj_invalid(); - tmpPage->width = strtod(*attribute_values, &ptr); + cleanup_numeric((gchar *)*attribute_values); + tmpPage->width = g_ascii_strtod(*attribute_values, &ptr); if (ptr == *attribute_values) *error = xoj_invalid(); has_attr |= 1; } else if (!strcmp(*attribute_names, "height")) { if (has_attr & 2) *error = xoj_invalid(); - tmpPage->height = strtod(*attribute_values, &ptr); + cleanup_numeric((gchar *)*attribute_values); + tmpPage->height = g_ascii_strtod(*attribute_values, &ptr); if (ptr == *attribute_values) *error = xoj_invalid(); has_attr |= 2; } @@ -373,7 +390,7 @@ void xoj_parser_start_element(GMarkupParseContext *context, else if (!strcmp(*attribute_names, "pageno")) { if (tmpPage->bg->type != BG_PDF || (has_attr & 32)) { *error = xoj_invalid(); return; } - tmpPage->bg->file_page_seq = strtod(*attribute_values, &ptr); + tmpPage->bg->file_page_seq = strtol(*attribute_values, &ptr, 10); if (ptr == *attribute_values) *error = xoj_invalid(); has_attr |= 32; } @@ -414,7 +431,8 @@ void xoj_parser_start_element(GMarkupParseContext *context, while (*attribute_names!=NULL) { if (!strcmp(*attribute_names, "width")) { if (has_attr & 1) *error = xoj_invalid(); - tmpItem->brush.thickness = strtod(*attribute_values, &ptr); + cleanup_numeric((gchar *)*attribute_values); + tmpItem->brush.thickness = g_ascii_strtod(*attribute_values, &ptr); if (ptr == *attribute_values) *error = xoj_invalid(); has_attr |= 1; } @@ -495,11 +513,12 @@ void xoj_parser_text(GMarkupParseContext *context, element_name = g_markup_parse_context_get_element(context); if (element_name == NULL) return; if (!strcmp(element_name, "stroke")) { + cleanup_numeric((gchar *)text); ptr = text; n = 0; while (text_len > 0) { realloc_cur_path(n/2 + 1); - ui.cur_path.coords[n] = strtod(text, (char **)(&ptr)); + ui.cur_path.coords[n] = g_ascii_strtod(text, (char **)(&ptr)); if (ptr == text) break; text_len -= (ptr - text); text = ptr; @@ -526,7 +545,7 @@ gboolean user_wants_second_chance(char **filename) if (response != GTK_RESPONSE_YES) return FALSE; dialog = gtk_file_chooser_dialog_new("Open PDF", GTK_WINDOW (winMain), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); filt_all = gtk_file_filter_new(); gtk_file_filter_set_name(filt_all, "All files"); @@ -537,7 +556,7 @@ gboolean user_wants_second_chance(char **filename) gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog), filt_pdf); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog), filt_all); - if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { + if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK) { gtk_widget_destroy(dialog); return FALSE; } @@ -560,6 +579,7 @@ gboolean open_journal(char *filename) char buffer[1000]; int len; gchar *tmpfn; + gboolean maybe_pdf; f = gzopen(filename, "r"); if (f==NULL) return FALSE; @@ -575,10 +595,14 @@ gboolean open_journal(char *filename) tmpFilename = filename; error = NULL; tmpBg_pdf = NULL; + maybe_pdf = TRUE; while (valid && !gzeof(f)) { len = gzread(f, buffer, 1000); if (len<0) valid = FALSE; + if (maybe_pdf && len>=4 && !strncmp(buffer, "%PDF", 4)) + { valid = FALSE; break; } // most likely pdf + else maybe_pdf = FALSE; if (len<=0) break; valid = g_markup_parse_context_parse(context, buffer, len, &error); } @@ -586,9 +610,19 @@ gboolean open_journal(char *filename) if (valid) valid = g_markup_parse_context_end_parse(context, &error); if (tmpJournal.npages == 0) valid = FALSE; g_markup_parse_context_free(context); + if (!valid) { delete_journal(&tmpJournal); - return FALSE; + if (!maybe_pdf) return FALSE; + // essentially same as on_fileNewBackground from here on + ui.saved = TRUE; + close_journal(); + while (bgpdf.status != STATUS_NOT_INIT) gtk_main_iteration(); + new_journal(); + ui.zoom = DEFAULT_ZOOM; + gnome_canvas_set_pixels_per_unit(canvas, ui.zoom); + update_page_stuff(); + return init_bgpdf(filename, TRUE, DOMAIN_ABSOLUTE); } ui.saved = TRUE; // force close_journal() to do its job @@ -631,7 +665,7 @@ gboolean open_journal(char *filename) ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data); ui.saved = TRUE; ui.zoom = DEFAULT_ZOOM; - update_file_name(filename); + update_file_name(g_strdup(filename)); gnome_canvas_set_pixels_per_unit(canvas, ui.zoom); make_canvas_items(); update_page_stuff(); @@ -781,8 +815,11 @@ struct Background *attempt_screenshot_bg(void) void end_bgpdf_shutdown(void) { if (bgpdf.tmpdir!=NULL) { - g_unlink(bgpdf.tmpfile_copy); - g_free(bgpdf.tmpfile_copy); + if (bgpdf.tmpfile_copy!=NULL) { + g_unlink(bgpdf.tmpfile_copy); + g_free(bgpdf.tmpfile_copy); + bgpdf.tmpfile_copy = NULL; + } g_rmdir(bgpdf.tmpdir); g_free(bgpdf.tmpdir); bgpdf.tmpdir = NULL; @@ -1006,16 +1043,18 @@ gboolean init_bgpdf(char *pdfname, gboolean create_pages, int file_domain) gsize filelen; if (bgpdf.status != STATUS_NOT_INIT) return FALSE; + bgpdf.tmpfile_copy = NULL; bgpdf.tmpdir = mkdtemp(g_strdup(TMPDIR_TEMPLATE)); if (!bgpdf.tmpdir) return FALSE; // make a local copy and check if it's a PDF - if (!g_file_get_contents(pdfname, &filebuf, &filelen, NULL)) return FALSE; + if (!g_file_get_contents(pdfname, &filebuf, &filelen, NULL)) + { end_bgpdf_shutdown(); return FALSE; } if (filelen < 4 || strncmp(filebuf, "%PDF", 4)) - { g_free(filebuf); return FALSE; } + { g_free(filebuf); end_bgpdf_shutdown(); return FALSE; } bgpdf.tmpfile_copy = g_strdup_printf("%s/bg.pdf", bgpdf.tmpdir); f = fopen(bgpdf.tmpfile_copy, "w"); if (f == NULL || fwrite(filebuf, 1, filelen, f) != filelen) - { g_free(filebuf); return FALSE; } + { g_free(filebuf); end_bgpdf_shutdown(); return FALSE; } fclose(f); g_free(filebuf); bgpdf.status = STATUS_IDLE; @@ -1087,3 +1126,525 @@ void bgpdf_update_bg(int pageno, struct BgPdfPage *bgpg) } } +// initialize the recent files list +void init_mru(void) +{ + int i; + gsize lfptr; + char s[5]; + GIOChannel *f; + gchar *str; + GIOStatus status; + + g_strlcpy(s, "mru0", 5); + for (s[3]='0', i=0; i0) { + str[lfptr] = 0; + ui.mru[i] = str; + i++; + } + } + if (f) { + g_io_channel_shutdown(f, FALSE, NULL); + g_io_channel_unref(f); + } + update_mru_menu(); +} + +void update_mru_menu(void) +{ + int i; + gboolean anyone = FALSE; + + for (i=0; i=1; j--) ui.mru[j] = ui.mru[j-1]; + ui.mru[0] = g_strdup(name); + update_mru_menu(); +} + +void delete_mru_entry(int which) +{ + int i; + + if (ui.mru[which]!=NULL) g_free(ui.mru[which]); + for (i=which+1;itype = BG_SOLID; + ui.default_page.bg->color_no = COLOR_WHITE; + ui.default_page.bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE]; + ui.default_page.bg->ruling = RULING_LINED; + ui.view_continuous = TRUE; + ui.allow_xinput = TRUE; + ui.bg_apply_all_pages = FALSE; + ui.use_erasertip = FALSE; + ui.window_default_width = 720; + ui.window_default_height = 480; + ui.maximize_at_start = FALSE; + ui.fullscreen = FALSE; + ui.scrollbar_step_increment = 30; + ui.zoom_step_increment = 1; + ui.zoom_step_factor = 1.5; + ui.antialias_bg = TRUE; + ui.progressive_bg = TRUE; + ui.print_ruling = TRUE; + ui.default_unit = UNIT_CM; + + ui.toolno[0] = ui.startuptool = TOOL_PEN; + ui.ruler[0] = ui.startupruler = FALSE; + for (i=1; i<=NUM_BUTTONS; i++) { + ui.toolno[i] = TOOL_ERASER; + ui.ruler[i] = FALSE; + } + for (i=0; i<=NUM_BUTTONS; i++) + ui.linked_brush[i] = BRUSH_LINKED; + ui.brushes[0][TOOL_PEN].color_no = COLOR_BLACK; + ui.brushes[0][TOOL_ERASER].color_no = COLOR_WHITE; + ui.brushes[0][TOOL_HIGHLIGHTER].color_no = COLOR_YELLOW; + for (i=0; i < NUM_STROKE_TOOLS; i++) { + ui.brushes[0][i].thickness_no = THICKNESS_MEDIUM; + ui.brushes[0][i].tool_options = 0; + } + for (i=0; i< NUM_STROKE_TOOLS; i++) + for (j=1; j<=NUM_BUTTONS; j++) + g_memmove(&(ui.brushes[j][i]), &(ui.brushes[0][i]), sizeof(struct Brush)); + + // predef_thickness is already initialized as a global variable + + GS_BITMAP_DPI = 144; + PDFTOPPM_PRINTING_DPI = 150; +} + +#if GLIB_CHECK_VERSION(2,6,0) + +void update_keyval(const gchar *group_name, const gchar *key, + const gchar *comment, gchar *value) +{ + gboolean has_it = g_key_file_has_key(ui.config_data, group_name, key, NULL); + cleanup_numeric(value); + g_key_file_set_value(ui.config_data, group_name, key, value); + g_free(value); + if (!has_it) g_key_file_set_comment(ui.config_data, group_name, key, comment, NULL); +} + +#endif + +void save_config_to_file(void) +{ + gchar *buf; + FILE *f; + +#if GLIB_CHECK_VERSION(2,6,0) + // no support for keyval files before Glib 2.6.0 + if (glib_minor_version<6) return; + + // save some data... + ui.maximize_at_start = (gdk_window_get_state(winMain->window) & GDK_WINDOW_STATE_MAXIMIZED); + if (!ui.maximize_at_start && !ui.fullscreen) + gdk_drawable_get_size(winMain->window, + &ui.window_default_width, &ui.window_default_height); + + update_keyval("general", "display_dpi", + " the display resolution, in pixels per inch", + g_strdup_printf("%.2f", DEFAULT_ZOOM*72)); + update_keyval("general", "initial_zoom", + " the initial zoom level, in percent", + g_strdup_printf("%.2f", 100*ui.zoom/DEFAULT_ZOOM)); + update_keyval("general", "window_maximize", + " maximize the window at startup (true/false)", + g_strdup(ui.maximize_at_start?"true":"false")); + update_keyval("general", "window_fullscreen", + " start in full screen mode (true/false)", + g_strdup(ui.fullscreen?"true":"false")); + update_keyval("general", "window_width", + " the window width in pixels (when not maximized)", + g_strdup_printf("%d", ui.window_default_width)); + update_keyval("general", "window_height", + " the window height in pixels", + g_strdup_printf("%d", ui.window_default_height)); + update_keyval("general", "scrollbar_speed", + " scrollbar step increment (in pixels)", + g_strdup_printf("%d", ui.scrollbar_step_increment)); + update_keyval("general", "zoom_dialog_increment", + " the step increment in the zoom dialog box", + g_strdup_printf("%d", ui.zoom_step_increment)); + update_keyval("general", "zoom_step_factor", + " the multiplicative factor for zoom in/out", + g_strdup_printf("%.3f", ui.zoom_step_factor)); + update_keyval("general", "view_continuous", + " document view (true = continuous, false = single page)", + g_strdup(ui.view_continuous?"true":"false")); + update_keyval("general", "use_xinput", + " use XInput extensions (true/false)", + g_strdup(ui.allow_xinput?"true":"false")); + update_keyval("general", "use_erasertip", + " always map eraser tip to eraser (true/false)", + g_strdup(ui.use_erasertip?"true":"false")); + + update_keyval("paper", "width", + " the default page width, in points (1/72 in)", + g_strdup_printf("%.2f", ui.default_page.width)); + update_keyval("paper", "height", + " the default page height, in points (1/72 in)", + g_strdup_printf("%.2f", ui.default_page.height)); + update_keyval("paper", "color", + " the default paper color", + g_strdup(bgcolor_names[ui.default_page.bg->color_no])); + update_keyval("paper", "style", + " the default paper style (plain, lined, ruled, or graph)", + g_strdup(bgstyle_names[ui.default_page.bg->ruling])); + update_keyval("paper", "apply_all", + " apply paper style changes to all pages (true/false)", + g_strdup(ui.bg_apply_all_pages?"true":"false")); + update_keyval("paper", "default_unit", + " preferred unit (cm, in, px, pt)", + g_strdup(unit_names[ui.default_unit])); + update_keyval("paper", "print_ruling", + " include paper ruling when printing or exporting to PDF (true/false)", + g_strdup(ui.print_ruling?"true":"false")); + update_keyval("paper", "antialias_bg", + " antialiased bitmap backgrounds (true/false)", + g_strdup(ui.antialias_bg?"true":"false")); + update_keyval("paper", "progressive_bg", + " progressive scaling of bitmap backgrounds (true/false)", + g_strdup(ui.progressive_bg?"true":"false")); + update_keyval("paper", "gs_bitmap_dpi", + " bitmap resolution of PS/PDF backgrounds rendered using ghostscript (dpi)", + g_strdup_printf("%d", GS_BITMAP_DPI)); + update_keyval("paper", "pdftoppm_printing_dpi", + " bitmap resolution of PDF backgrounds when printing with libgnomeprint (dpi)", + g_strdup_printf("%d", PDFTOPPM_PRINTING_DPI)); + + update_keyval("tools", "startup_tool", + " selected tool at startup (pen, eraser, highlighter, selectrect, vertspace, hand)", + g_strdup(tool_names[ui.startuptool])); + update_keyval("tools", "startup_ruler", + " ruler mode at startup (true/false) (for pen or highlighter only)", + g_strdup(ui.startupruler?"true":"false")); + update_keyval("tools", "pen_color", + " default pen color", + g_strdup(color_names[ui.default_brushes[TOOL_PEN].color_no])); + update_keyval("tools", "pen_thickness", + " default pen thickness (fine = 1, medium = 2, thick = 3)", + g_strdup_printf("%d", ui.default_brushes[TOOL_PEN].thickness_no)); + update_keyval("tools", "eraser_thickness", + " default eraser thickness (fine = 1, medium = 2, thick = 3)", + g_strdup_printf("%d", ui.default_brushes[TOOL_ERASER].thickness_no)); + update_keyval("tools", "eraser_mode", + " default eraser mode (standard = 0, whiteout = 1, strokes = 2)", + g_strdup_printf("%d", ui.default_brushes[TOOL_ERASER].tool_options)); + update_keyval("tools", "highlighter_color", + " default highlighter color", + g_strdup(color_names[ui.default_brushes[TOOL_HIGHLIGHTER].color_no])); + update_keyval("tools", "highlighter_thickness", + " default highlighter thickness (fine = 1, medium = 2, thick = 3)", + g_strdup_printf("%d", ui.default_brushes[TOOL_HIGHLIGHTER].thickness_no)); + update_keyval("tools", "btn2_tool", + " button 2 tool (pen, eraser, highlighter, selectrect, vertspace, hand)", + g_strdup(tool_names[ui.toolno[1]])); + update_keyval("tools", "btn2_linked", + " button 2 brush linked to primary brush (true/false) (overrides all other settings)", + g_strdup((ui.linked_brush[1]==BRUSH_LINKED)?"true":"false")); + update_keyval("tools", "btn2_ruler", + " button 2 ruler mode (true/false) (for pen or highlighter only)", + g_strdup(ui.ruler[1]?"true":"false")); + update_keyval("tools", "btn2_color", + " button 2 brush color (for pen or highlighter only)", + g_strdup((ui.toolno[1] sup) return FALSE; + *val = conv; + return TRUE; +} + +gboolean parse_keyval_floatlist(const gchar *group, const gchar *key, double *val, int n, double inf, double sup) +{ + gchar *ret, *end; + double conv[5]; + int i; + + if (n>5) return FALSE; + ret = g_key_file_get_value(ui.config_data, group, key, NULL); + if (ret==NULL) return FALSE; + end = ret; + for (i=0; i sup)) { g_free(ret); return FALSE; } + end++; + } + g_free(ret); + for (i=0; i sup) return FALSE; + *val = conv; + return TRUE; +} + +gboolean parse_keyval_enum(const gchar *group, const gchar *key, int *val, const char **names, int n) +{ + gchar *ret; + int i; + + ret = g_key_file_get_value(ui.config_data, group, key, NULL); + if (ret==NULL) return FALSE; + for (i=0; icolor_no), bgcolor_names, COLOR_MAX); + ui.default_page.bg->color_rgba = predef_bgcolors_rgba[ui.default_page.bg->color_no]; + parse_keyval_enum("paper", "style", &(ui.default_page.bg->ruling), bgstyle_names, 4); + parse_keyval_boolean("paper", "apply_all", &ui.bg_apply_all_pages); + parse_keyval_enum("paper", "default_unit", &ui.default_unit, unit_names, 4); + parse_keyval_boolean("paper", "antialias_bg", &ui.antialias_bg); + parse_keyval_boolean("paper", "progressive_bg", &ui.progressive_bg); + parse_keyval_boolean("paper", "print_ruling", &ui.print_ruling); + parse_keyval_int("paper", "gs_bitmap_dpi", &GS_BITMAP_DPI, 1, 1200); + parse_keyval_int("paper", "pdftoppm_printing_dpi", &PDFTOPPM_PRINTING_DPI, 1, 1200); + + parse_keyval_enum("tools", "startup_tool", &ui.startuptool, tool_names, NUM_TOOLS); + ui.toolno[0] = ui.startuptool; + if (ui.startuptool == TOOL_PEN || ui.startuptool == TOOL_HIGHLIGHTER) { + parse_keyval_boolean("tools", "startup_ruler", &ui.startupruler); + ui.ruler[0] = ui.startupruler; + } + parse_keyval_enum("tools", "pen_color", &(ui.brushes[0][TOOL_PEN].color_no), color_names, COLOR_MAX); + parse_keyval_int("tools", "pen_thickness", &(ui.brushes[0][TOOL_PEN].thickness_no), 0, 4); + parse_keyval_int("tools", "eraser_thickness", &(ui.brushes[0][TOOL_ERASER].thickness_no), 1, 3); + parse_keyval_int("tools", "eraser_mode", &(ui.brushes[0][TOOL_ERASER].tool_options), 0, 2); + parse_keyval_enum("tools", "highlighter_color", &(ui.brushes[0][TOOL_HIGHLIGHTER].color_no), color_names, COLOR_MAX); + parse_keyval_int("tools", "highlighter_thickness", &(ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no), 0, 4); + for (i=0; i< NUM_STROKE_TOOLS; i++) + for (j=1; j<=NUM_BUTTONS; j++) + g_memmove(&(ui.brushes[j][i]), &(ui.brushes[0][i]), sizeof(struct Brush)); + + parse_keyval_enum("tools", "btn2_tool", &(ui.toolno[1]), tool_names, NUM_TOOLS); + if (parse_keyval_boolean("tools", "btn2_linked", &b)) + ui.linked_brush[1] = b?BRUSH_LINKED:BRUSH_STATIC; + parse_keyval_enum("tools", "btn3_tool", &(ui.toolno[2]), tool_names, NUM_TOOLS); + if (parse_keyval_boolean("tools", "btn3_linked", &b)) + ui.linked_brush[2] = b?BRUSH_LINKED:BRUSH_STATIC; + for (i=1; i<=NUM_BUTTONS; i++) + if (ui.toolno[i]==TOOL_PEN || ui.toolno[i]==TOOL_HIGHLIGHTER) + ui.ruler[i] = ui.ruler[0]; + if (ui.linked_brush[1]!=BRUSH_LINKED) { + if (ui.toolno[1]==TOOL_PEN || ui.toolno[1]==TOOL_HIGHLIGHTER) { + parse_keyval_boolean("tools", "btn2_ruler", &(ui.ruler[1])); + parse_keyval_enum("tools", "btn2_color", &(ui.brushes[1][ui.toolno[1]].color_no), color_names, COLOR_MAX); + } + if (ui.toolno[1]