MCFontnode::MCFontnode(MCNameRef fname, uint2 &size, uint2 style) { reqname = fname; reqsize = size; reqstyle = style; #if defined(TARGET_SUBPLATFORM_IPHONE) font = new MCFontStruct; font -> size = size; uindex_t t_comma; MCAutoStringRef reqname_str; reqname_str = MCNameGetString(*reqname); Boolean t_success; t_success = MCStringFirstIndexOfChar(*reqname_str, ',', 0, kMCCompareExact, t_comma); MCAutoStringRef t_before_comma; /* UNCHECKED */ MCStringCopySubstring(*reqname_str, MCRangeMake(0, t_comma - 1), &t_before_comma); font -> fid = (MCSysFontHandle)iphone_font_create(*t_before_comma, reqsize, (reqstyle & FA_WEIGHT) > 0x05, (reqstyle & FA_ITALIC) != 0); font -> ascent = size - 1; font -> descent = size * 2 / 14 + 1; float ascent, descent; iphone_font_get_metrics(font -> fid, ascent, descent); if (ceilf(ascent) + ceilf(descent) > size) font -> ascent++; #elif defined(TARGET_SUBPLATFORM_ANDROID) font = new MCFontStruct; font -> size = size; uindex_t t_comma; MCAutoStringRef reqname_str; reqname_str = MCNameGetString(*reqname); Boolean t_success; t_success = MCStringFirstIndexOfChar(*reqname_str, ',', 0, kMCCompareExact, t_comma); MCAutoStringRef t_before_comma; /* UNCHECKED */ MCStringCopySubstring(*reqname_str, MCRangeMake(0, t_comma - 1), &t_before_comma); font -> fid = (MCSysFontHandle)android_font_create(*t_before_comma, reqsize, (reqstyle & FA_WEIGHT) > 0x05, (reqstyle & FA_ITALIC) != 0); font -> ascent = size - 1; font -> descent = size * 2 / 14 + 1; float ascent, descent; android_font_get_metrics(font -> fid, ascent, descent); if (ceilf(ascent) + ceilf(descent) > size) font -> ascent++; #endif }
Boolean MCEPS::import(MCStringRef fname, IO_handle stream) { size = (uint4)MCS_fsize(stream); delete postscript; postscript = new char[size + 1]; if (IO_read(postscript, size, stream) != IO_NORMAL) return False; postscript[size] = '\0'; uindex_t t_sep; MCStringRef t_fname; if (MCStringLastIndexOfChar(fname, PATH_SEPARATOR, UINDEX_MAX, kMCCompareExact, t_sep)) /* UNCHECKED */ MCStringCopySubstring(fname, MCRangeMake(t_sep + 1, MCStringGetLength(fname) - (t_sep + 1)), t_fname); else t_fname = MCValueRetain(fname); MCNewAutoNameRef t_name; if (!MCNameCreateAndRelease(t_fname, &t_name)) return False; setname(*t_name); setextents(); rect.width = (uint2)(ex * xscale / xf); rect.height = (uint2)(ey * yscale / yf); if (flags & F_SHOW_BORDER) { rect.width += borderwidth << 1; rect.height += borderwidth << 1; } return True; }
void MCPlatformHandleTextInputQueryTextRect(MCPlatformWindowRef p_window, MCRange p_range, MCRectangle& r_first_line_rect, MCRange& r_actual_range) { if (!MCactivefield) { r_first_line_rect = MCRectangleMake(0, 0, 0, 0); r_actual_range = MCRangeMake(UINDEX_MAX, 0); return; } int32_t t_si, t_ei; t_si = 0; t_ei = INT32_MAX; MCactivefield -> resolvechars(0, t_si, t_ei, p_range . offset, p_range . length); MCRectangle t_rect; t_rect = MCactivefield -> firstRectForCharacterRange(t_si, t_ei); MCactivefield -> unresolvechars(0, t_si, t_ei); MCPoint t_top_left, t_bottom_right; t_top_left = MCactivefield -> getstack() -> stacktowindowloc(MCPointMake(t_rect . x, t_rect . y)); t_bottom_right = MCactivefield -> getstack() -> stacktowindowloc(MCPointMake(t_rect . x + t_rect . width, t_rect . y + t_rect . height)); r_first_line_rect = MCRectangleMake(t_top_left . x, t_top_left . y, t_bottom_right . x - t_top_left . x, t_bottom_right . y - t_top_left . y); r_actual_range = MCRangeMakeMinMax(t_si, t_ei); }
void MCPlatformHandleTextInputQueryTextRanges(MCPlatformWindowRef p_window, MCRange& r_marked_range, MCRange& r_selected_range) { if (!MCactivefield) { r_marked_range = MCRangeMake(UINDEX_MAX, 0); r_selected_range = MCRangeMake(UINDEX_MAX, 0); return; } int4 si, ei; MCactivefield -> selectedmark(False, si, ei, False); MCactivefield -> unresolvechars(0, si, ei); r_selected_range = MCRangeMakeMinMax(si, ei); if (MCactivefield -> getcompositionrange(si, ei)) { MCactivefield -> unresolvechars(0, si, ei); r_marked_range = MCRangeMakeMinMax(si, ei); } else r_marked_range = MCRangeMake(UINDEX_MAX, 0); }
bool MCArraysSplitIndexes(MCNameRef p_key, integer_t*& r_indexes, uindex_t& r_count, bool& r_all_integers) { r_indexes = nil; r_count = 0; MCStringRef t_string = MCNameGetString(p_key); uindex_t t_string_len = MCStringGetLength(t_string); if (t_string_len == 0) return true; r_all_integers = true; uindex_t t_start, t_finish; t_start = 0; t_finish = 0; for(;;) { if (!MCStringFirstIndexOfChar(t_string, ',', t_start, kMCCompareExact, t_finish)) t_finish = t_string_len; if (!MCMemoryResizeArray(r_count + 1, r_indexes, r_count)) return false; MCAutoStringRef t_substring; MCAutoNumberRef t_number; MCStringCopySubstring(t_string, MCRangeMake(t_start, t_finish - t_start), &t_substring); if (!MCNumberParse(*t_substring, &t_number)) { r_indexes[r_count - 1] = 0; r_all_integers = false; break; } else r_indexes[r_count - 1] = MCNumberFetchAsInteger(*t_number); if (t_finish >= t_string_len) break; t_start = t_finish + 1; } return true; }
void MCPlatformHandleTextInputQueryText(MCPlatformWindowRef p_window, MCRange p_range, unichar_t*& r_chars, uindex_t& r_char_count, MCRange& r_actual_range) { if (!MCactivefield) { r_chars = nil; r_char_count = 0; r_actual_range = MCRangeMake(p_range . offset, 0); return; } int32_t t_si, t_ei; MCAutoStringRef t_text; t_si = 0; t_ei = INT32_MAX; MCactivefield -> resolvechars(0, t_si, t_ei, p_range . offset, p_range . length); MCactivefield -> exportastext(0, t_si, t_ei, &t_text); MCactivefield -> unresolvechars(0, t_si, t_ei); /* UNCHECKED */ MCStringConvertToUnicode(*t_text, r_chars, r_char_count); r_actual_range = MCRangeMakeMinMax(t_si, t_ei); }
void MCArraysExecSplitByColumn(MCExecContext& ctxt, MCStringRef p_string, MCArrayRef& r_array) { MCStringRef t_row_delim, t_column_delim; t_row_delim = ctxt . GetRowDelimiter(); t_column_delim = ctxt . GetColumnDelimiter(); // Output array MCAutoArrayRef t_array; if (!MCArrayCreateMutable(&t_array)) { ctxt . Throw(); return; } // Temporary array for storing columns MCAutoArray<MCStringRef> t_temp_array; // Iterate over the rows of the input string uindex_t t_offset, t_length; t_offset = 0; t_length = MCStringGetLength(p_string); bool t_success; t_success = true; uindex_t t_row_index; t_row_index = 0; while (t_success && t_offset < t_length) { // Find the end of this row MCRange t_row_found; if (!MCStringFind(p_string, MCRangeMake(t_offset, UINDEX_MAX), t_row_delim, ctxt . GetStringComparisonType(), &t_row_found)) { t_row_found . offset = t_length; t_row_found . length = 0; } // Iterate over the cells of this row uindex_t t_cell_offset, t_column_index; t_cell_offset = t_offset; t_column_index = 0; while (t_success && t_cell_offset <= t_row_found . offset) { // Find the end of this cell MCRange t_cell_found; if (!MCStringFind(p_string, MCRangeMake(t_cell_offset, UINDEX_MAX), t_column_delim, ctxt . GetStringComparisonType(), &t_cell_found) || t_cell_found . offset > t_row_found . offset) { t_cell_found . offset = t_row_found . offset; // AL-2014-08-04: [[ Bug 13090 ]] Make sure cell offset is incremented eventually when the delimiter is not found t_cell_found . length = 1; } // Check that the output array has a slot for this column if (t_temp_array.Size() <= t_column_index) t_temp_array.Extend(t_column_index + 1); // Check that a string has been created to store this column MCRange t_range; t_range = MCRangeMake(t_cell_offset, t_cell_found . offset - t_cell_offset); if (t_temp_array[t_column_index] == nil) { t_success = MCStringCreateMutable(0, t_temp_array[t_column_index]); // AL-2014-08-04: [[ Bug 13090 ]] If we are creating a new column, make sure we pad with empty cells 'above' this one uindex_t t_rows = t_row_index; while (t_success && t_rows--) t_success = MCStringAppend(t_temp_array[t_column_index], t_row_delim); if (t_success) t_success = MCStringAppendFormat(t_temp_array[t_column_index], "%*@", &t_range, p_string); } else { // AL-2014-06-12: [[ Bug 12610 ]] Range parameter to MCStringFormat must be a pointer to an MCRange t_success = MCStringAppendFormat(t_temp_array[t_column_index], "%@%*@", t_row_delim, &t_range, p_string); } // Next cell t_column_index++; t_cell_offset = t_cell_found . offset + t_cell_found . length; } // AL-2014-08-04: [[ Bug 13090 ]] Pad the rest of this row with empty cells index_t t_pad_number; t_pad_number = t_temp_array . Size() - t_column_index; if (t_success && t_pad_number > 0) { while (t_success && t_pad_number--) t_success = MCStringAppend(t_temp_array[t_column_index++], t_row_delim); } // Next row t_row_index++; t_offset = t_row_found . offset + t_row_found . length; } // Convert the temporary array into a "proper" array for (uindex_t i = 0; i < t_temp_array.Size() && t_success; i++) { t_success = MCArrayStoreValueAtIndex(*t_array, i + 1, t_temp_array[i]); MCValueRelease(t_temp_array[i]); } if (t_success) t_success = MCArrayCopy(*t_array, r_array); if (!t_success) ctxt . Throw(); }
// SN-2014-09-01: [[ Bug 13297 ]] Combining by column deserves its own function as it is too // different from combining by row void MCArraysExecCombineByColumn(MCExecContext& ctxt, MCArrayRef p_array, MCStringRef &r_string) { MCStringRef t_row_delimiter, t_col_delimiter; t_row_delimiter = ctxt . GetRowDelimiter(); t_col_delimiter = ctxt . GetColumnDelimiter(); MCAutoListRef t_list; MCListCreateMutable(t_row_delimiter, &t_list); uindex_t t_count = MCArrayGetCount(p_array); combine_int_indexed_array_t t_lisctxt; bool t_success; t_lisctxt . elements = nil; t_lisctxt . index = 0; t_lisctxt . converter = &ctxt; t_success = MCMemoryNewArray(t_count, t_lisctxt . elements); if (t_success) { if (MCArrayApply(p_array, list_int_indexed_array_elements, &t_lisctxt)) { bool t_valid_keys; qsort(t_lisctxt . elements, t_count, sizeof(array_element_t), compare_int_indexed_elements); // Combine by row/column is only valid if all the indices are consecutive numbers // Otherwise, an empty string is returned - no error index_t t_last_index; t_valid_keys = true; t_last_index = 0; for (int i = 0; i < t_count && t_valid_keys; ++i) { if (!t_last_index) t_last_index = t_lisctxt . elements[i] . key; else t_valid_keys = ++t_last_index == t_lisctxt . elements[i] . key; } if (t_valid_keys) { // SN-2014-09-01: [[ Bug 13297 ]] // We need to store the converted strings in a array, to be able to iterate through the elements by one row-delimitated // at a time MCStringRef* t_strings; uindex_t *t_next_row_indices; t_strings = NULL; t_next_row_indices = NULL; /* UNCHECKED */ MCMemoryNewArray(t_count, t_strings); // MCMemoryNewArray initialises all t_next_row_indices elements to 0. /* UNCHECKED */ MCMemoryNewArray(t_count, t_next_row_indices); for (int i = 0; i < t_count && t_success; ++i) { if (t_lisctxt . elements[i] . key == 0) // The index 0 is ignored continue; t_success = ctxt . ConvertToString(t_lisctxt . elements[i] . value, t_strings[i]); } // SN-2014-09-01: [[ Bug 13297 ]] Added a missed part in column-combining: // only combining row-by-row the array elements. if (t_success) { uindex_t t_elements_over; t_elements_over = 0; // We iterate as long as one element still has uncombined rows while (t_success && t_elements_over != t_count) { MCAutoListRef t_row; t_success = MCListCreateMutable(t_col_delimiter, &t_row); t_elements_over = 0; // Iterate through all the elements of the array for (int i = 0; i < t_count && t_success; ++i) { // Only consider this element if it has any uncombined rows remaining if (t_next_row_indices[i] < MCStringGetLength(t_strings[i])) { MCRange t_cell_range; if (MCStringFind(t_strings[i], MCRangeMake(t_next_row_indices[i], UINDEX_MAX), t_row_delimiter, ctxt.GetStringComparisonType(), &t_cell_range)) { // We found a row delimiter, so we stop the copy range before it and update the next index from which to look t_success = MCListAppendSubstring(*t_row, t_strings[i], MCRangeMake(t_next_row_indices[i], t_cell_range . offset - t_next_row_indices[i])); t_next_row_indices[i] = t_cell_range . offset + t_cell_range . length; } else { // No row delimiter: we copy the remaining part of the string and mark the element // as wholly combined by setting the next index to the length of the element t_success = MCListAppendSubstring(*t_row, t_strings[i], MCRangeMake(t_next_row_indices[i], UINDEX_MAX)); t_next_row_indices[i] = MCStringGetLength(t_strings[i]); } } else { // Everything has been combined in this element t_elements_over++; MCListAppend(*t_row, kMCEmptyString); } } // One more row has been combined - doing it anyway mimics the previous behaviour of having an empty row // added in the end when combining by columns if (t_elements_over != t_count) MCListAppend(*t_list, *t_row); } } MCMemoryDeleteArray(t_next_row_indices); MCMemoryDeleteArray(t_strings); } } MCMemoryDeleteArray(t_lisctxt . elements); } if (t_success && MCListCopyAsString(*t_list, r_string)) return; ctxt . Throw(); }
Exec_stat MCF_parsetextatts(Properties which, MCStringRef data, uint4 &flags, MCStringRef &fname, uint2 &height, uint2 &size, uint2 &style) { int2 i1; switch (which) { case P_TEXT_ALIGN: flags &= ~F_ALIGNMENT; if (MCStringIsEqualToCString(data, MCleftstring, kMCCompareCaseless) || MCStringIsEmpty(data)) flags |= F_ALIGN_LEFT; else if (MCStringIsEqualToCString(data, MCcenterstring, kMCCompareCaseless)) flags |= F_ALIGN_CENTER; else if (MCStringIsEqualToCString(data, MCrightstring, kMCCompareCaseless)) flags |= F_ALIGN_RIGHT; else if (MCStringIsEqualToCString(data, MCjustifystring, kMCCompareCaseless)) flags |= F_ALIGN_JUSTIFY; else { MCeerror->add(EE_OBJECT_BADALIGN, 0, 0, data); return ES_ERROR; } break; case P_TEXT_FONT: {// MW-2012-02-17: [[ IntrinsicUnicode ]] Strip any lang tag from the // fontname. uindex_t t_offset; if (MCStringFirstIndexOfChar(data, ',', 0, kMCCompareExact, t_offset)) /* UNCHECKED */ MCStringCopySubstring(data, MCRangeMake(t_offset + 1, MCStringGetLength(data) - (t_offset + 1)), fname); else fname = MCValueRetain(data); } break; case P_TEXT_HEIGHT: if (!MCU_stoi2(data, i1)) { MCeerror->add (EE_OBJECT_TEXTHEIGHTNAN, 0, 0, data); return ES_ERROR; } height = i1; break; case P_TEXT_SIZE: if (MCStringIsEmpty(data)) i1 = 0; else if (!MCU_stoi2(data, i1)) { MCeerror->add (EE_OBJECT_TEXTSIZENAN, 0, 0, data); return ES_ERROR; } size = i1; break; case P_TEXT_STYLE: { // MW-2012-02-17: [[ SplitTextAttrs ]] If the string is empty, then // return 0 for the style - indicating to unset the property. if (MCStringIsEmpty(data)) style = 0; else { style = FA_DEFAULT_STYLE; uindex_t t_start_pos, t_end_pos; t_end_pos = 0; while (t_end_pos < MCStringGetLength(data)) { t_start_pos = t_end_pos; // skip spaces at the beginning or after a comma (if any) MCU_skip_spaces(data, t_start_pos); uindex_t t_comma; if (!MCStringFirstIndexOfChar(data, ',', t_start_pos, kMCCompareExact, t_comma)) t_end_pos = MCStringGetLength(data); else t_end_pos = t_comma; MCAutoStringRef tdata; /* UNCHECKED */ MCStringCopySubstring(data, MCRangeMake(t_start_pos, t_end_pos - t_start_pos), &tdata); t_end_pos++; if (MCF_setweightstring(style, *tdata)) continue; if (MCF_setexpandstring(style, *tdata)) continue; if (MCF_setslantlongstring(style, *tdata)) continue; if (MCStringIsEqualToCString(*tdata, MCplainstring, kMCCompareCaseless)) { style = FA_DEFAULT_STYLE; continue; } if (MCStringIsEqualToCString(*tdata, MCmixedstring, kMCCompareCaseless)) { style = FA_DEFAULT_STYLE; continue; } if (MCStringIsEqualToCString(*tdata, MCboxstring, kMCCompareCaseless)) { style &= ~FA_3D_BOX; style |= FA_BOX; continue; } if (MCStringIsEqualToCString(*tdata, MCthreedboxstring, kMCCompareCaseless)) { style &= ~FA_BOX; style |= FA_3D_BOX; continue; } if (MCStringIsEqualToCString(*tdata, MCunderlinestring, kMCCompareCaseless)) { style |= FA_UNDERLINE; continue; } if (MCStringIsEqualToCString(*tdata, MCstrikeoutstring, kMCCompareCaseless)) { style |= FA_STRIKEOUT; continue; } if (MCStringIsEqualToCString(*tdata, MCgroupstring, kMCCompareCaseless) || MCStringIsEqualToCString(*tdata, MClinkstring, kMCCompareCaseless)) { style |= FA_LINK; continue; } MCeerror->add(EE_OBJECT_BADSTYLE, 0, 0, data); return ES_ERROR; } } } break; default: break; } return ES_NORMAL; }
void MCFontDrawText(MCGContextRef p_gcontext, int32_t x, int32_t y, MCStringRef p_text, MCFontRef font, bool p_rtl, bool p_can_break) { MCRange t_range = MCRangeMake(0, MCStringGetLength(p_text)); return MCFontDrawTextSubstring(p_gcontext, x, y, p_text, t_range, font, p_rtl, p_can_break); }
int32_t MCFontMeasureText(MCFontRef p_font, MCStringRef p_text, const MCGAffineTransform &p_transform) { MCRange t_range = MCRangeMake(0, MCStringGetLength(p_text)); return MCFontMeasureTextSubstring(p_font, p_text, t_range, p_transform); }
void MCFontBreakText(MCFontRef p_font, MCStringRef p_text, MCRange p_range, MCFontBreakTextCallback p_callback, void *p_callback_data, bool p_rtl) { // MW-2013-12-19: [[ Bug 11559 ]] If the font has a nil font, do nothing. if (p_font -> fontstruct == nil) return; // If the text is small enough, don't bother trying to break it /*if (p_length <= (kMCFontBreakTextCharLimit * (p_is_unicode ? 2 : 1))) { p_callback(p_font, p_text, p_length, p_is_unicode, p_callback_data); return; }*/ //p_callback(p_font, p_text, p_length, p_is_unicode, p_callback_data); //return; // Scan forward in the string for possible break locations. Breaks are // assigned a quality value as some locations are better for breaking than // others. The qualities are: // // 0. no break found // 1. grapheme break found // 2. URL break found ('/' char) // 3. word break found // // This isn't a particularly good algorithm but should suffice until full // Unicode support is added and a proper breaking algorithm implemented. uint32_t t_stride; t_stride = kMCFontBreakTextCharLimit; uindex_t t_end = p_range.offset + p_range.length; uindex_t t_length = p_range.length; uindex_t t_offset = (p_rtl) ? 0 : p_range.offset; while (t_length > 0) { int t_break_quality; uindex_t t_break_point, t_index; t_break_quality = 0; t_break_point = 0; t_index = 0; // Find the best break within the next stride characters. If there are // no breaking points, extend beyond the stride until one is found. while ((t_index < t_stride || t_break_quality == 0) && t_index < t_length) { codepoint_t t_char; uindex_t t_advance; if (p_rtl) t_char = MCStringGetCharAtIndex(p_text, t_end - t_index - t_offset); else t_char = MCStringGetCharAtIndex(p_text, t_offset + t_index); if (MCUnicodeCodepointIsHighSurrogate(t_char)) { // Surrogate pair if (p_rtl) t_char = MCUnicodeSurrogatesToCodepoint(t_char, MCStringGetCharAtIndex(p_text, t_end - t_index - t_offset - 1)); else t_char = MCUnicodeSurrogatesToCodepoint(t_char, MCStringGetCharAtIndex(p_text, t_offset + t_index + 1)); t_advance = 2; } else { t_advance = 1; } // Prohibit breaks at the beginning of the string if (t_index == 0) { t_index += t_advance; continue; } if (t_char == ' ') { t_break_point = t_index; t_break_quality = 3; } else if (t_break_quality < 3 && t_char == '/') { t_break_point = t_index; t_break_quality = 2; } else if (t_break_quality < 2 && MCUnicodeCodepointIsBase(t_char)) { t_break_point = t_index; t_break_quality = 1; } else if (t_break_quality < 2 && t_char > 0xFFFF) { // Character outside BMP, assume can break here t_break_point = t_index; t_break_quality = 1; } // If the break point is a word boundary, don't look for a later // breaking point. Words are cached as-is where possible. if (t_break_quality == 3) break; // Advance to the next character t_index += t_advance; } // If no word break was found and the whole of the remaining text was // searched and the remaining text is smaller than the break size then // don't attempt a break just for the sake of it. if (t_break_quality < 3 && t_length < kMCFontBreakTextCharLimit) t_break_point = t_length; // If no break point was found, just process the whole line if (t_break_quality == 0) t_break_point = t_length; // Process this chunk of text MCRange t_range; if (p_rtl) t_range = MCRangeMake(t_end - t_offset - t_break_point, t_break_point); else t_range = MCRangeMake(t_offset, t_break_point); #if !defined(_WIN32) && !defined(_ANDROID_MOBILE) // This is a really ugly hack to get LTR/RTL overrides working correctly - // ATSUI and Pango think they know better than us and won't let us suppress // the BiDi algorithm they uses for text layout. So instead, we need to add // an LRO or RLO character in front of every single bit of text :-( MCAutoStringRef t_temp; unichar_t t_override; if (p_rtl) t_override = 0x202E; else t_override = 0x202D; /* UNCHECKED */ MCStringCreateMutable(0, &t_temp); /* UNCHECKED */ MCStringAppendChar(*t_temp, t_override); /* UNCHECKED */ MCStringAppendSubstring(*t_temp, p_text, t_range); /* UNCHECKED */ MCStringAppendChar(*t_temp, 0x202C); p_callback(p_font, *t_temp, MCRangeMake(0, MCStringGetLength(*t_temp)), p_callback_data); #else // Another ugly hack - this time, to avoid incoming strings being coerced // into Unicode strings needlessly (because the drawing code uses unichars). // Do a mutable copy (to ensure an actual copy) before drawing. MCAutoStringRef t_temp; /* UNCHECKED */ MCStringMutableCopySubstring(p_text, t_range, &t_temp); p_callback(p_font, *t_temp, MCRangeMake(0, t_range.length), p_callback_data); #endif // Explicitly show breaking points //p_callback(p_font, MCSTR("|"), MCRangeMake(0, 1), p_callback_data); // Move on to the next chunk t_offset += t_break_point; // SN-2014-07-23: [[ Bug 12910 ]] Script editor crashes // Make sure we get 0 as a minimum, not a negative value since t_length is a uindex_t. if (t_length < t_break_point) t_length = 0; else t_length -= t_break_point; } }
void MCKeywordsExecRepeatFor(MCExecContext& ctxt, MCStatement *statements, MCExpression *endcond, MCVarref *loopvar, File_unit each, uint2 line, uint2 pos) { MCAutoArrayRef t_array; MCAutoStringRef t_string; MCAutoDataRef t_data; MCRange t_chunk_range; t_chunk_range = MCRangeMake(0,0); uindex_t t_length = 0; MCAutoValueRef t_condition; MCNameRef t_key; MCValueRef t_value; uintptr_t t_iterator; // SN2015-06-15: [[ Bug 15457 ]] The index can be a negative index. index_t t_sequenced_iterator; const byte_t *t_data_ptr; MCAutoPointer<MCTextChunkIterator> tci; if (!ctxt . TryToEvaluateExpression(endcond, line, pos, EE_REPEAT_BADFORCOND, &t_condition)) return; bool t_sequence_array; t_sequence_array = false; if (each == FU_ELEMENT || each == FU_KEY) { if (!ctxt . ConvertToArray(*t_condition, &t_array)) return; // SN-2015-06-15: [[ Bug 15457 ]] If this is a numerical array, do // it in order - even if it does not start at 1 if (each == FU_ELEMENT && MCArrayIsNumericSequence(*t_array, t_sequenced_iterator)) { t_sequence_array = true; if (!MCArrayFetchValueAtIndex(*t_array, t_sequenced_iterator, t_value)) return; } else { t_iterator = 0; if (!MCArrayIterate(*t_array, t_iterator, t_key, t_value)) return; } } else if (each == FU_BYTE) { if (!ctxt . ConvertToData(*t_condition, &t_data)) return; t_length = MCDataGetLength(*t_data); t_data_ptr = MCDataGetBytePtr(*t_data); } else { if (!ctxt . ConvertToString(*t_condition, &t_string)) return; switch (each) { case FU_LINE: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_LINE); break; case FU_PARAGRAPH: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_PARAGRAPH); break; case FU_SENTENCE: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_SENTENCE); break; case FU_ITEM: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_ITEM); break; case FU_WORD: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_WORD); break; case FU_TRUEWORD: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_TRUEWORD); break; case FU_TOKEN: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_TOKEN); break; case FU_CODEPOINT: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_CODEPOINT); break; case FU_CODEUNIT: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_CODEUNIT); break; case FU_CHARACTER: default: tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_CHARACTER); break; } } bool done; done = false; bool endnext; endnext = false; bool t_found; t_found = false; while (!done) { MCAutoStringRef t_unit; MCAutoDataRef t_byte; switch (each) { case FU_KEY: { loopvar -> set(ctxt, t_key); if (!MCArrayIterate(*t_array, t_iterator, t_key, t_value)) endnext = true; } break; case FU_ELEMENT: { loopvar -> set(ctxt, t_value); // SN-2015-06-15: [[ Bug 15457 ]] Sequenced, numeric arrays // have their own iterator if (t_sequence_array) { if (!MCArrayFetchValueAtIndex(*t_array, ++t_sequenced_iterator, t_value)) endnext = true; } else { if (!MCArrayIterate(*t_array, t_iterator, t_key, t_value)) endnext = true; } } break; case FU_BYTE: { // SN-2014-04-14 [[ Bug 12184 ]] If we have no data at all, we don't want to start the loop if (t_length) { MCDataCreateWithBytes(t_data_ptr++, 1, &t_byte); endnext = (--t_length) == 0; } else done = true; } break; default: { t_found = MCStringsTextChunkIteratorNext(ctxt, *tci); endnext = tci -> IsExhausted(); if (!t_found) { t_unit = kMCEmptyString; done = true; } else tci -> CopyString(&t_unit); } break; } // MW-2010-12-15: [[ Bug 9218 ]] Added KEY to the type of repeat that already // copies the value. // MW-2011-02-08: [[ Bug ]] Make sure we don't use 't_unit' if the repeat type is 'key' or // 'element'. // Set the loop variable to whatever the value was in the last iteration. if (each == FU_BYTE) { // SN-2014-04-14 [[ Bug 12184 ]] We don't need to set anything since we are not going in the loop if (!done) loopvar -> set(ctxt, *t_byte); } else if (each != FU_ELEMENT && each != FU_KEY) loopvar -> set(ctxt, *t_unit); if (!done) MCKeywordsExecuteRepeatStatements(ctxt, statements, line, pos, done); if (endnext) { // Reset the loop variable to whatever the value was in the last iteration. if (loopvar != nil) { if (each == FU_BYTE) loopvar -> set(ctxt, *t_byte); else if (each != FU_ELEMENT && each != FU_KEY) loopvar -> set(ctxt, *t_unit); } } done = done || endnext; } }
static int MCA_do_file_dialog(MCStringRef p_title, MCStringRef p_prompt, MCStringRef p_filter, MCStringRef p_initial, unsigned int p_options, MCStringRef &r_value, MCStringRef &r_result) { int t_result = 0; MCAutoStringRef t_initial_file; MCAutoStringRef t_initial_folder; MCAutoStringRef t_initial_native_folder; if (p_initial != nil && !MCStringIsEmpty(p_initial)) { MCAutoStringRef t_fixed_path; /* UNCHECKED */ MCU_fix_path(p_initial, &t_fixed_path); if (MCS_exists(*t_fixed_path, False)) t_initial_folder = *t_fixed_path; else if ((p_options & MCA_OPTION_SAVE_DIALOG) != 0) { uindex_t t_last_slash; if (!MCStringLastIndexOfChar(*t_fixed_path, '/', UINDEX_MAX, kMCStringOptionCompareExact, t_last_slash)) { if (MCStringGetLength(*t_fixed_path) != 0) t_initial_file = *t_fixed_path; } else { if (t_last_slash < MCStringGetLength(*t_fixed_path) - 1) /* UNCHECKED */ MCStringCopySubstring(*t_fixed_path, MCRangeMake(t_last_slash + 1, MCStringGetLength(*t_fixed_path) - (t_last_slash + 1)), &t_initial_file); MCAutoStringRef t_folder_split; /* UNCHECKED */ MCStringCopySubstring(*t_fixed_path, MCRangeMake(0, t_last_slash - 1), &t_folder_split); if (MCS_exists(*t_folder_split, False)) t_initial_folder = *t_folder_split; } } else { uindex_t t_last_slash; if (MCStringLastIndexOfChar(*t_fixed_path, '/', UINDEX_MAX, kMCStringOptionCompareExact, t_last_slash)) { MCAutoStringRef t_folder_split; /* UNCHECKED */ MCStringCopySubstring(*t_fixed_path, MCRangeMake(0, t_last_slash - 1), &t_folder_split); if (MCS_exists(*t_folder_split, False)) t_initial_folder = *t_folder_split; } } MCAutoStringRef t_resolved_folder; /* UNCHECKED */ MCS_resolvepath(*t_initial_folder != nil ? *t_initial_folder : kMCEmptyString, &t_resolved_folder); /* UNCHECKED */ MCS_pathtonative(*t_resolved_folder, &t_initial_native_folder); } if (!MCModeMakeLocalWindows()) { MCAutoStringRefArray t_filters; if (p_filter != NULL) { /* UNCHECKED */ MCStringsSplit(p_filter, '\0', t_filters.PtrRef(), t_filters.CountRef()); } MCRemoteFileDialog(p_title, p_prompt, *t_filters, t_filters.Count(), *t_initial_native_folder, *t_initial_file, (p_options & MCA_OPTION_SAVE_DIALOG) != 0, (p_options & MCA_OPTION_PLURAL) != 0, r_value); return 0; } Window t_window; t_window = MCModeGetParentWindow(); MCAutoStringRef t_value; bool t_succeeded; int t_filter_index; if (MCmajorosversion >= 0x0600) { static SHCreateItemFromParsingNamePtr s_shcreateitemfromparsingname = NULL; if (s_shcreateitemfromparsingname == NULL) { static HMODULE s_shell32_module = NULL; s_shell32_module = LoadLibraryW(L"shell32.dll"); s_shcreateitemfromparsingname = (SHCreateItemFromParsingNamePtr)GetProcAddress(s_shell32_module, "SHCreateItemFromParsingName"); } IFileSaveDialog *t_file_save_dialog; IFileOpenDialog *t_file_open_dialog; IFileDialog *t_file_dialog; t_file_dialog = NULL; HRESULT t_hresult; if ((p_options & MCA_OPTION_SAVE_DIALOG) == 0) { t_hresult = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, __uuidof(IFileOpenDialog), (LPVOID *)&t_file_open_dialog); t_succeeded = SUCCEEDED(t_hresult); t_file_dialog = t_file_open_dialog; } else { t_hresult = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_INPROC_SERVER, __uuidof(IFileSaveDialog), (LPVOID *)&t_file_save_dialog); t_succeeded = SUCCEEDED(t_hresult); t_file_dialog = t_file_save_dialog; } if (t_succeeded) { DWORD t_options; t_options = FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR | FOS_PATHMUSTEXIST; if (p_options & MCA_OPTION_PLURAL) t_options |= FOS_ALLOWMULTISELECT; if (p_options & MCA_OPTION_SAVE_DIALOG) t_options |= FOS_OVERWRITEPROMPT; if (p_options & MCA_OPTION_FOLDER_DIALOG) t_options |= FOS_PICKFOLDERS; else t_options |= FOS_FILEMUSTEXIST; t_hresult = t_file_dialog -> SetOptions(t_options); t_succeeded = SUCCEEDED(t_hresult); } if (t_succeeded && *t_initial_native_folder != NULL) { IShellItem *t_initial_folder_shellitem; t_initial_folder_shellitem = NULL; MCAutoStringRefAsWString t_initial_folder_wstr; /* UNCHECKED */ t_initial_folder_wstr.Lock(*t_initial_native_folder); t_hresult = s_shcreateitemfromparsingname(*t_initial_folder_wstr, NULL, __uuidof(IShellItem), (LPVOID *)&t_initial_folder_shellitem); if (SUCCEEDED(t_hresult)) t_file_dialog -> SetFolder(t_initial_folder_shellitem); if (t_initial_folder_shellitem != NULL) t_initial_folder_shellitem -> Release(); t_succeeded = SUCCEEDED(t_hresult); } if (t_succeeded && *t_initial_file != NULL) { MCAutoStringRefAsWString t_initial_file_wstr; /* UNCHECKED */ t_initial_file_wstr.Lock(*t_initial_file); t_hresult = t_file_dialog -> SetFileName(*t_initial_file_wstr); t_succeeded = SUCCEEDED(t_hresult); } if (t_succeeded && p_filter != NULL && (p_options & MCA_OPTION_FOLDER_DIALOG) == 0) { uint4 t_filter_length, t_filter_count; measure_filter(p_filter, t_filter_length, t_filter_count); MCAutoStringRefAsWString t_filter_wstr; /* UNCHECKED */ t_filter_wstr.Lock(p_filter); COMDLG_FILTERSPEC *t_filter_spec; filter_to_spec(*t_filter_wstr, t_filter_count, t_filter_spec); t_hresult = t_file_dialog -> SetFileTypes(t_filter_count, t_filter_spec); t_succeeded = SUCCEEDED(t_hresult); delete t_filter_spec; } if (t_succeeded && p_filter != NULL && (p_options & MCA_OPTION_FOLDER_DIALOG) == 0) { t_hresult = t_file_dialog -> SetFileTypeIndex(1); t_succeeded = SUCCEEDED(t_hresult); } if (t_succeeded) { MCAutoStringRefAsWString t_prompt_wstr; /* UNCHECKED */ t_prompt_wstr.Lock(p_prompt); t_hresult = t_file_dialog -> SetTitle(*t_prompt_wstr); } if (t_succeeded) { t_hresult = t_file_dialog -> Show(t_window != NULL ? (HWND)t_window -> handle . window : NULL); t_succeeded = SUCCEEDED(t_hresult); } if ((p_options & MCA_OPTION_SAVE_DIALOG) == 0) { IShellItemArray *t_file_items; t_file_items = NULL; if (t_succeeded) { t_hresult = t_file_open_dialog -> GetResults(&t_file_items); t_succeeded = SUCCEEDED(t_hresult); } DWORD t_file_item_count; if (t_succeeded) { t_hresult = t_file_items -> GetCount(&t_file_item_count); t_succeeded = SUCCEEDED(t_hresult); } if (t_succeeded) { for(uint4 t_index = 0; t_index < t_file_item_count && t_succeeded; ++t_index) { IShellItem *t_file_item; t_file_item = NULL; if (t_succeeded) { t_hresult = t_file_items -> GetItemAt(t_index, &t_file_item); t_succeeded = SUCCEEDED(t_hresult); } if (t_succeeded) { t_hresult = append_shellitem_path_and_release(t_file_item, t_index == 0, &t_value); t_succeeded = SUCCEEDED(t_hresult); } } } if (t_file_items != NULL) t_file_items -> Release(); } else { IShellItem *t_file_item; t_file_item = NULL; if (t_succeeded) { t_hresult = t_file_dialog -> GetResult(&t_file_item); t_succeeded = SUCCEEDED(t_hresult); } if (t_succeeded) { t_hresult = append_shellitem_path_and_release(t_file_item, true, &t_value); t_succeeded = SUCCEEDED(t_hresult); } } t_filter_index = 0; if (t_succeeded && (p_options & MCA_OPTION_FOLDER_DIALOG) == 0) { UINT t_index; t_hresult = t_file_dialog -> GetFileTypeIndex(&t_index); t_succeeded = SUCCEEDED(t_hresult); if (t_succeeded) t_filter_index = (int)t_index; } if (t_file_dialog != NULL) t_file_dialog -> Release(); if (!t_succeeded) t_result = t_hresult; else t_result = 0; } else { OPENFILENAMEW t_open_dialog; memset(&t_open_dialog, 0, sizeof(OPENFILENAMEW)); t_open_dialog . lStructSize = sizeof(OPENFILENAMEW); MCAutoStringRefAsWString t_initial_folder_wstr; MCAutoStringRefAsWString t_prompt_wstr; MCAutoStringRefAsWString t_filter_wstr; /* UNCHECKED */ t_filter_wstr.Lock(p_filter); /* UNCHECKED */ t_initial_folder_wstr.Lock(*t_initial_native_folder); /* UNCHECKED */ t_prompt_wstr.Lock(p_prompt); MCAutoArray<unichar_t> t_buffer; /* UNCHECKED */ t_buffer.New(MAX_PATH); if (!MCStringIsEmpty(*t_initial_file)) /* UNCHECKED */ MCStringGetChars(*t_initial_file, MCRangeMake(0, t_buffer.Size()), t_buffer.Ptr()); else t_buffer[0] = '\0'; t_open_dialog . lpstrFilter = *t_filter_wstr; t_open_dialog . nFilterIndex = 1; t_open_dialog . lpstrFile = t_buffer.Ptr(); t_open_dialog . nMaxFile = t_buffer.Size(); t_open_dialog . lpstrInitialDir = *t_initial_folder_wstr; t_open_dialog . lpstrTitle = *t_prompt_wstr; t_open_dialog . Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_LONGNAMES | OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLESIZING; if (p_options & MCA_OPTION_PLURAL) t_open_dialog . Flags |= OFN_ALLOWMULTISELECT; if (p_options & MCA_OPTION_SAVE_DIALOG) t_open_dialog . Flags |= OFN_OVERWRITEPROMPT; t_open_dialog . lpstrFilter = *t_filter_wstr; t_open_dialog . lpfnHook = open_dialog_hook; t_open_dialog . hwndOwner = t_window != NULL ? (HWND)t_window -> handle . window : NULL; if (p_options & MCA_OPTION_SAVE_DIALOG) t_succeeded = GetSaveFileNameW(&t_open_dialog) == TRUE; else { *t_open_dialog . lpstrFile = '\0'; t_succeeded = GetOpenFileNameW(&t_open_dialog) == TRUE; } if (!t_succeeded) t_result = CommDlgExtendedError(); // MW-2005-07-26: Try again without the specified filename if it was invalid if (t_result == FNERR_INVALIDFILENAME) { *t_open_dialog . lpstrFile = '\0'; if (p_options & MCA_OPTION_SAVE_DIALOG) t_succeeded = GetSaveFileNameW(&t_open_dialog) == TRUE; else t_succeeded = GetOpenFileNameW(&t_open_dialog) == TRUE; if (!t_succeeded) t_result = CommDlgExtendedError(); } if (t_result == FNERR_BUFFERTOOSMALL) t_succeeded = true; if (t_succeeded) { build_paths(&t_value); t_filter_index = t_open_dialog . nFilterIndex; } } if (t_succeeded) { if (p_options & MCA_OPTION_RETURN_FILTER) { // The filter string has the following format: // "<description0>\0<extensions0>\0<description1>\0...\0<extensionsN>\0" // so the n'th filter comes after the 2(n - 1)'th null character uindex_t t_index = 2 * (t_filter_index - 1); uindex_t t_offset = 0; while (t_index--) { /* UNCHECKED */ MCStringFirstIndexOfChar(p_filter, '\0', t_offset, kMCStringOptionCompareExact, t_offset); t_offset++; } uindex_t t_end; t_end = UINDEX_MAX; /* UNCHECKED */ MCStringFirstIndexOfChar(p_filter, '\0', t_offset, kMCStringOptionCompareExact, t_end); /* UNCHECKED */ MCStringCopySubstring(p_filter, MCRangeMake(t_offset, t_end-t_offset), r_result); } t_result = 0; r_value = MCValueRetain(*t_value); } else r_result = MCValueRetain(MCNameGetString(MCN_cancel)); waitonbutton(); return t_result; }