Beispiel #1
0
bool InsertSilence(const char* _undoTitle, double _pos, double _len)
{
	if (_pos>=0.0 && _len>0.0 && _pos<SNM_GetProjectLength())
	{
		if (_undoTitle)
			Undo_BeginBlock2(NULL);

		PreventUIRefresh(1);

		double timeSel1, timeSel2, d=_pos+_len;
		GetSet_LoopTimeRange2(NULL, false, false, &timeSel1, &timeSel2, false);
		GetSet_LoopTimeRange2(NULL, true, false, &_pos, &d, false);
		Main_OnCommand(40200, 0); // insert space at time sel

		// restore time sel, enlarge if needed (mimic native behavior)
		if (timeSel1>_pos) timeSel1+=_len;
		if (_pos<timeSel2) timeSel2+=_len;
		GetSet_LoopTimeRange2(NULL, true, false, &timeSel1, &timeSel2, false);

		PreventUIRefresh(-1);

		if (_undoTitle)
			Undo_EndBlock2(NULL, _undoTitle, UNDO_STATE_ALL);
		return true;
	}
	return false;
}
Beispiel #2
0
void SelItems::Restore(MediaTrack* tr)
{
	bool* bUsed = NULL;
	if (m_selItems.GetSize())
	{
		bUsed = new bool[m_selItems.GetSize()];
		memset(bUsed, 0, sizeof(bool) * m_selItems.GetSize());
	}

	if (tr == NULL)
	{
		PreventUIRefresh(1);
		for (int i = 1; i <= GetNumTracks(); i++)
			Match(CSurf_TrackFromID(i, false), bUsed);
		PreventUIRefresh(-1);
	}
	else
		Match(tr, bUsed);

	// Delete unused items
	for (int i = m_selItems.GetSize()-1; i >= 0 ; i--)
		if (!bUsed[i])
			m_selItems.Delete(i, true);

	delete[] bUsed;
}
Beispiel #3
0
// bUsed is an array of bools with size == m_selItems.GetSize(), used to "check off" when an item from that list is used
// caller initializes bUsed
void SelItems::Match(MediaTrack* tr, bool* bUsed)
{
	PreventUIRefresh(1);
	int nbitems=GetTrackNumMediaItems(tr);
	for (int i = 0; i < nbitems; i++)
	{
		MediaItem* mi = GetTrackMediaItem(tr, i);
		GetSetMediaItemInfo(mi, "B_UISEL", &g_bFalse);
		GUID* g = (GUID*)GetSetMediaItemInfo(mi, "GUID", NULL);
		for (int j = 0; j < m_selItems.GetSize(); j++)
			if (!bUsed[j] && GuidsEqual(m_selItems.Get(j), g))
			{
				bUsed[j] = true;
				GetSetMediaItemInfo(mi, "B_UISEL", &g_bTrue);
				break;
			}
	}
	PreventUIRefresh(-1);
}
Beispiel #4
0
bool GotoMarkerRegion(ReaProject* _proj, int _num, int _flags, bool _select = false)
{
	bool isrgn; double pos, end;
	int x=0, n; 
	while ((x = EnumProjectMarkers3(_proj, x, &isrgn, &pos, &end, NULL, &n, NULL)))
		if (n == _num && ((!isrgn && _flags&SNM_MARKER_MASK) || (isrgn && _flags&SNM_REGION_MASK)))
		{
			PreventUIRefresh(1);

			if (_select && isrgn && (_flags&SNM_REGION_MASK))
				GetSet_LoopTimeRange2(NULL, true, true, &pos, &end, false); // seek is managed below

			int* opt = (int*)GetConfigVar("smoothseek"); // obeys smooth seek
			SetEditCurPos2(_proj, pos, true, opt && *opt); // includes an undo point, if enabled in prefs

			PreventUIRefresh(-1);

			return true;
		}
	return false;
}
Beispiel #5
0
void RestoreLastSelItemTrack(COMMAND_T* ct)
{
	PreventUIRefresh(1);
	for (int i = 1; i <= GetNumTracks(); i++)
	{
		MediaTrack* tr = CSurf_TrackFromID(i, false);
		if (*(int*)GetSetMediaTrackInfo(tr, "I_SELECTED", NULL))
		{
			GUID* g = (GUID*)GetSetMediaTrackInfo(tr, "GUID", NULL);
			for (int j = 0; j < g_selItemsTrack.Get()->GetSize(); j++)
				if (GuidsEqual(g, &g_selItemsTrack.Get()->Get(j)->m_guid))
				{
					g_selItemsTrack.Get()->Get(j)->PreviousSelection(tr);
					break;
				}
		}
	}
	PreventUIRefresh(-1);
	Undo_OnStateChangeEx(SWS_CMD_SHORTNAME(ct), UNDO_STATE_ITEMS, -1);
	UpdateArrange();
}
Beispiel #6
0
void SetTrackHeight(MediaTrack* track, int height, bool useChunk)
{
	if (!useChunk)
	{
		GetSetMediaTrackInfo(track, "I_HEIGHTOVERRIDE", &height);

		PreventUIRefresh(1);
		Main_OnCommand(41327, 0);
		Main_OnCommand(41328, 0);
		PreventUIRefresh(-1);
	}
	else
	{
		SNM_ChunkParserPatcher p(track);
		char pTrackLine[BUFFER_SIZE] = "";

		if (p.Parse(SNM_GET_CHUNK_CHAR, 1, "TRACK", "TRACKHEIGHT", 0, 1, pTrackLine))
		{
			_snprintfSafe(pTrackLine, BUFFER_SIZE, "%d", height);
			p.ParsePatch(SNM_SET_CHUNK_CHAR, 1, "TRACK", "TRACKHEIGHT", 0, 1, pTrackLine);
		}
	}
}
Beispiel #7
0
void RestoreSelTrackSelItems(int iSlot)
{
	PreventUIRefresh(1);
	for (int i = 1; i <= GetNumTracks(); i++)
	{
		MediaTrack* tr = CSurf_TrackFromID(i, false);
		if (*(int*)GetSetMediaTrackInfo(tr, "I_SELECTED", NULL))
		{
			GUID* g = (GUID*)GetSetMediaTrackInfo(tr, "GUID", NULL);
			for (int j = 0; j < g_selItemsTrack.Get()->GetSize(); j++)
				if (GuidsEqual(g, &g_selItemsTrack.Get()->Get(j)->m_guid))
				{
					g_selItemsTrack.Get()->Get(j)->Restore(tr, iSlot);
					break;
				}
		}
	}
	PreventUIRefresh(-1);

	char cUndoText[256];
	sprintf(cUndoText, __LOCALIZE_VERFMT("Restore selected track(s) selected item(s), slot %d","sws_undo"), iSlot+1);
	Undo_OnStateChangeEx(cUndoText, UNDO_STATE_ITEMS, -1);
	UpdateArrange();
}
Beispiel #8
0
void MoveTempo (COMMAND_T* ct)
{
	BR_Envelope tempoMap(GetTempoEnv());
	if (!tempoMap.Count())
		return;
	double cursor = GetCursorPositionEx(NULL);
	double tDiff = 0;
	int targetId = -1;

	// Find tempo marker closest to the edit cursor
	if ((int)ct->user == 3)
	{
		targetId = tempoMap.FindClosest(cursor);
		if (targetId == 0) ++targetId;
		if (!tempoMap.ValidateId(targetId))
			return;

		double cTime; tempoMap.GetPoint(targetId, &cTime, NULL, NULL, NULL);
		tDiff = cursor - cTime;
	}

	// Just get time difference for selected points
	else
	{
		if ((int)ct->user == 2 || (int)ct->user == -2)
			tDiff = 1 / GetHZoomLevel() * (double)ct->user / 2;
		else
			tDiff = (double)ct->user/10000;
	}

	if (tDiff == 0)
		return;

	// Loop through selected points
	int skipped = 0;
	int count = (targetId != -1) ? (1) : (tempoMap.CountSelected());
	for (int i = 0; i < count; ++i)
	{
		if (int id = ((int)ct->user == 3) ? (targetId) : (tempoMap.GetSelected(i))) // skip first point
			skipped += (MoveTempo(tempoMap, id, tDiff)) ? (0) : (1);
	}

	// Commit changes
	PreventUIRefresh(1); // prevent jumpy cursor
	if (tempoMap.Commit())
	{
		if ((int)ct->user == 3)
			SetEditCurPos2(NULL, cursor, false, false); // always keep cursor position when moving to closest tempo marker
		Undo_OnStateChangeEx2(NULL, SWS_CMD_SHORTNAME(ct), UNDO_STATE_ALL, -1);
	}
	PreventUIRefresh(-1);

	// Warn user if some points weren't processed
	static bool s_warnUser = true;
	if (s_warnUser && skipped != 0)
	{
		char buffer[512];
		_snprintfSafe(buffer, sizeof(buffer), __LOCALIZE_VERFMT("%d of the selected points didn't get processed because some points would end up with illegal BPM or position. Would you like to be warned if it happens again?", "sws_mbox"), skipped);
		int userAnswer = ShowMessageBox(buffer, __LOCALIZE("SWS - Warning", "sws_mbox"), 4);
		if (userAnswer == 7)
			s_warnUser = false;
	}
}
Beispiel #9
0
void MoveGridToEditPlayCursor (COMMAND_T* ct)
{
	// Find cursor immediately (in case of playback we want the most accurate position)
	double cursor = ((int)ct->user == 1 || (int)ct->user == 3) ? (GetPlayPositionEx(NULL)) : (GetCursorPositionEx(NULL));

	// Make sure tempo map already has at least one point created (for some reason it won't work if creating it directly in chunk)
	InitTempoMap();
	BR_Envelope tempoMap(GetTempoEnv());
	if (!tempoMap.Count())
		return;

	// Set preferences to prevent play cursor from jumping
	int seekmodes = 0;
	if ((int)ct->user == 1 || (int)ct->user == 3)
	{
		GetConfig("seekmodes", seekmodes);
		SetConfig("seekmodes", ClearBit(seekmodes, 5));
	}

	// Find closest grid
	double grid = 0;
	if      ((int)ct->user == 0 || (int)ct->user == 1) grid = GetClosestGrid(cursor);
	else if ((int)ct->user == 2 || (int)ct->user == 3) grid = GetClosestMeasureGrid(cursor);
	else if ((int)ct->user == 4)                       grid = GetClosestLeftSideGrid(cursor);
	else                                               grid = GetClosestRightSideGrid(cursor);
	int targetId = tempoMap.Find(grid, MIN_TEMPO_DIST);

	// No tempo marker on grid, create it
	if (!tempoMap.ValidateId(targetId))
	{
		int prevId  = tempoMap.FindPrevious(grid);
		double value = tempoMap.ValueAtPosition(grid);
		int shape;
		tempoMap.GetPoint(prevId, NULL, NULL, &shape, NULL);
		tempoMap.CreatePoint(prevId+1, grid, value, shape, 0, false);
		targetId = prevId+1;
	}
	double tDiff = cursor - grid;

	// Commit changes and warn user if needed
	if (tDiff != 0)
	{
		if (MoveTempo(tempoMap, targetId, tDiff))
		{
			PreventUIRefresh(1); // prevent jumpy cursor
			if (tempoMap.Commit())
			{
				// Restore edit cursor only if moving to it
				if ((int)ct->user != 1 && (int)ct->user != 3)
					SetEditCurPos2(NULL, cursor, false, false);
				Undo_OnStateChangeEx2(NULL, SWS_CMD_SHORTNAME(ct), UNDO_STATE_ALL, -1);
			}
			PreventUIRefresh(-1);
		}
		else
		{
			static bool s_warnUser = true;
			if (s_warnUser)
			{
				int userAnswer = ShowMessageBox(__LOCALIZE("Moving grid failed because some tempo markers would end up with illegal BPM or position. Would you like to be warned if it happens again?", "sws_mbox"), __LOCALIZE("SWS - Warning", "sws_mbox"), 4);
				if (userAnswer == 7)
					s_warnUser = false;
			}
		}
	}

	// Restore preferences
	if ((int)ct->user == 1 || (int)ct->user == 3)
		SetConfig("seekmodes", seekmodes);
}
Beispiel #10
0
void DeleteTempoPreserveItems (COMMAND_T* ct)
{
	BR_Envelope tempoMap(GetTempoEnv());
	if (!tempoMap.CountSelected())
		return;

	// Get items' position info and set their timebase to time
	vector<BR_MidiItemTimePos> items;
	double firstMarker;
	tempoMap.GetPoint(tempoMap.GetSelected(0), &firstMarker, NULL, NULL, NULL);

	int itemCount = ((int)ct->user) ? CountSelectedMediaItems(NULL) : CountMediaItems(NULL);
	items.reserve(itemCount);
	for (int i = 0; i < itemCount; ++i)
	{
		MediaItem* item = ((int)ct->user) ? GetSelectedMediaItem(NULL, i) : GetMediaItem(NULL, i);
		double itemEnd = GetMediaItemInfo_Value(item, "D_POSITION") + GetMediaItemInfo_Value(item, "D_LENGTH");
		if (itemEnd >= firstMarker)
		{
			items.push_back(BR_MidiItemTimePos(item));
			SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", 0);
		}
	}

	// Readjust unselected tempo markers
	double offset = 0;
	for (int i = 0; i < tempoMap.CountConseq(); ++i)
	{
		int startId, endId;
		tempoMap.GetConseq (i, &startId, &endId);

		if (endId == tempoMap.Count()-1) continue;         // no points after selection, nothing to adjust
		if (startId == 0 && (++startId > endId)) continue; // skip first point

		// Get musical length of selection
		double musicalLen = 0;
		for (int i = startId - 1; i <= endId; ++i )
		{
			double t0, t1, b0, b1; int s0;
			tempoMap.GetPoint(i,   &t0, &b0, &s0, NULL);
			tempoMap.GetPoint(i+1, &t1, &b1, NULL, NULL);
			if (i == startId-1) t0 -= offset; // readjust position to original (earlier iterations moved it)

			if (s0 == SQUARE)
				musicalLen += (t1-t0) * b0 / 240;
			else
				musicalLen += (t1-t0) * (b0+b1) / 480;
		}

		// Readjust points after selection
		double t0, t1, b0, b1; int s0;
		tempoMap.GetPoint(startId - 1, &t0, &b0, &s0, NULL);
		tempoMap.GetPoint(endId   + 1, &t1, &b1, NULL, NULL);

		if (s0 == SQUARE)
			offset = (t0 + (240*musicalLen) / b0) - t1;
		else
			offset = (t0 + (480*musicalLen) / (b0 + b1)) - t1;

		while (!tempoMap.GetSelection(++endId) && endId < tempoMap.Count())
		{
			double t;
			tempoMap.GetPoint(endId, &t, NULL, NULL, NULL);
			t += offset;
			tempoMap.SetPoint (endId, &t, NULL, NULL, NULL);
		}
	}

	// Delete selected tempo markers
	int idOffset = 0;
	for (int i = 0; i < tempoMap.CountSelected(); ++i)
	{
		int id = tempoMap.GetSelected(i) - idOffset;
		if (id != 0) // skip first point
		{
			tempoMap.DeletePoint(id);
			++idOffset;
		}
	}

	// Commit tempo map and restore position info
	PreventUIRefresh(1);
	if (tempoMap.Commit(false))
	{
		for (size_t i = 0; i < items.size(); ++i)
			items[i].Restore();
		Undo_OnStateChangeEx2(NULL, SWS_CMD_SHORTNAME(ct), UNDO_STATE_ALL, -1);
	}
	PreventUIRefresh(-1);
}
Beispiel #11
0
static void MidiTakePreview (int mode, MediaItem_Take* take, MediaTrack* track, double volume, double startOffset, double measureSync, bool pauseDuringPrev)
{
	/* mode: 0 -> stop   *
	*        1 -> start  *
	*        2 -> toggle */

	// First stop any ongoing preview
	RegisterCsurfPlayState(false, MidiTakePreviewPlayState);
	if (g_itemPreviewPlaying)
	{
		if (g_ItemPreview.preview_track)
		{
			StopTrackPreview(&g_ItemPreview);
			SendAllNotesOff((MediaTrack*)g_ItemPreview.preview_track);
		}
		else
		{
			StopPreview(&g_ItemPreview);
		}

		g_itemPreviewPlaying = false;
		plugin_register("-timer",(void*)MidiTakePreviewTimer);
		delete g_ItemPreview.src;

		if (g_itemPreviewPaused && mode != 1) // requesting new preview while old one is still playing shouldn't unpause playback
		{
			if (IsPaused(NULL))
				OnPauseButtonEx(NULL);
			g_itemPreviewPaused = false;
		}

		// Toggled preview off...treat it as stop
		if (mode == 2)
			mode = 0;
	}

	// About IsRecording: REAPER won't preview anything during recording but extension will still think preview is in progress if we let it continue here
	if (mode == 0 || IsRecording(NULL))
		return;

	if (take)
	{
		PreventUIRefresh(1); // item may get resized temporarily so hide it from the user

		MediaItem* item         = GetMediaItemTake_Item(take);
		MediaItem_Take* oldTake = GetActiveTake(item);
		bool itemMuteState      = *(bool*)GetSetMediaItemInfo(item, "B_MUTE", NULL);

		GetSetMediaItemInfo(item, "B_MUTE", &g_bFalse); // needs to be set before getting the source
		SetActiveTake(take);                            // active item take and editor take may differ

		// If timebase in MIDI editor is set to Beats (source), make sure item is croped so full MIDI source is visible beforing getting the source
		double itemStart    = -1;
		double itemEnd      = -1;
		double takeOffset   = 0;
		double itemPositionOffset = 0;
		if (GetToggleCommandState2(SectionFromUniqueID(SECTION_MIDI_EDITOR), 40470) > 0) // Timebase: Beats(source)
		{
			itemStart = GetMediaItemInfo_Value(item, "D_POSITION");
			itemEnd   = itemStart + GetMediaItemInfo_Value(item, "D_LENGTH");
			takeOffset = GetMediaItemTakeInfo_Value(take, "D_STARTOFFS");
			double sourceLenPPQ = GetMidiSourceLengthPPQ(take, NULL);

			if (takeOffset != 0)
				SetMediaItemTakeInfo_Value(take, "D_STARTOFFS", 0);

			double itemSourceStart = MIDI_GetProjTimeFromPPQPos(take, 0);
			if (itemSourceStart < 0)
			{
				itemPositionOffset = abs(itemSourceStart);
				SetMediaItemInfo_Value(item, "D_POSITION", itemStart + itemPositionOffset);
				itemSourceStart = MIDI_GetProjTimeFromPPQPos(take, 0);
			}
			SetMediaItemInfo_Value(item, "D_POSITION", itemSourceStart);
			SetMediaItemInfo_Value(item, "D_LENGTH",   MIDI_GetProjTimeFromPPQPos(take, sourceLenPPQ) - itemSourceStart);
		}

		double effectiveTakeLen = EffectiveMidiTakeEnd(take, true, true, true) - GetMediaItemInfo_Value(item, "D_POSITION");
		PCM_source* src = DuplicateSource((PCM_source*)item); // must be item source otherwise item/take volume won't get accounted for
		if (src && effectiveTakeLen > 0)
		{
			GetSetMediaItemInfo((MediaItem*)src, "D_POSITION", &g_d0);
			GetSetMediaItemInfo((MediaItem*)src, "D_LENGTH", &effectiveTakeLen);

			if (!g_ItemPreview.src)
			{
				#ifdef _WIN32
					InitializeCriticalSection(&g_ItemPreview.cs);
				#else
					pthread_mutex_init(&g_ItemPreview.mutex, NULL);
				#endif
				g_ItemPreview.loop = false;
			}

			g_ItemPreview.src           = src;
			g_ItemPreview.m_out_chan    = (track) ? (-1) : (0);
			g_ItemPreview.curpos        = startOffset;
			g_ItemPreview.volume        = volume;
			g_ItemPreview.preview_track = track;

			// Pause before preview otherwise MidiTakePreviewPlayState will stop it
			g_itemPreviewPaused = pauseDuringPrev;
			if (g_itemPreviewPaused && IsPlaying(NULL) && !IsPaused(NULL))
				OnPauseButton();

			if (g_ItemPreview.preview_track)
				g_itemPreviewPlaying = !!PlayTrackPreview2Ex(NULL, &g_ItemPreview, 1, measureSync);
			else
				g_itemPreviewPlaying = !!PlayPreviewEx(&g_ItemPreview, 1, measureSync);

			if (g_itemPreviewPlaying)
			{
				plugin_register("timer",(void*)MidiTakePreviewTimer);
				RegisterCsurfPlayState(true, MidiTakePreviewPlayState);
			}
			else
				delete g_ItemPreview.src;
		}

		if (itemStart != -1)
		{
			SetMediaItemInfo_Value(item, "D_POSITION", itemStart + itemPositionOffset);
			SetMediaItemInfo_Value(item, "D_LENGTH",   itemEnd - itemStart);
			if (itemPositionOffset != 0) SetMediaItemInfo_Value(item, "D_POSITION", itemStart);
			if (takeOffset != 0)         SetMediaItemTakeInfo_Value(take, "D_STARTOFFS", takeOffset);
		}
		SetActiveTake(oldTake);
		GetSetMediaItemInfo(item, "B_MUTE", &itemMuteState);

		PreventUIRefresh(-1);
	}
}
Beispiel #12
0
// param _allTakes only makes sense if jobTake() is used
bool FindWnd::FindMediaItem(int _dir, bool _allTakes, bool (*jobTake)(MediaItem_Take*,const char*), bool (*jobItem)(MediaItem*,const char*))
{
	bool update = false, found = false, sel = true;
	if (g_searchStr && *g_searchStr)
	{
		PreventUIRefresh(1);

		MediaItem* startItem = NULL;
		bool clearCurrentSelection = false;
		if (_dir)
		{
			WDL_PtrList<MediaItem> items;
			SNM_GetSelectedItems(NULL, &items);
			if (items.GetSize())
			{
				startItem = FindPrevNextItem(_dir, items.Get(_dir > 0 ? 0 : items.GetSize()-1));
				clearCurrentSelection = (startItem != NULL); 
			}
			else
				startItem = FindPrevNextItem(_dir, NULL);
		}
		else
		{
			startItem = FindPrevNextItem(1, NULL);
			clearCurrentSelection = (startItem != NULL); 
		}

		if (clearCurrentSelection)
		{
			Undo_BeginBlock2(NULL);
			Main_OnCommand(40289,0); // unselect all items
			update = true;
		}

		MediaItem* item = NULL;
		MediaTrack* startTr = startItem ? GetMediaItem_Track(startItem) : NULL;
		int startTrIdx = startTr ? CSurf_TrackToID(startTr, false) : -1;
		if (startTr && startItem && startTrIdx>=0)
		{
			// find startItem idx
			int startItemIdx=-1;
			while (item != startItem) 
				item = GetTrackMediaItem(startTr,++startItemIdx);

			bool firstItem=true, breakSelection=false;
			for (int i=startTrIdx; !breakSelection && i <= CountTracks(NULL) && i>=1; i += (!_dir ? 1 : _dir))
			{
				MediaTrack* tr = CSurf_TrackFromID(i, false); 
				int nbItems = GetTrackNumMediaItems(tr);
				for (int j = (firstItem ? startItemIdx : (_dir >= 0 ? 0 : (nbItems-1))); 
					 tr && !breakSelection && j < nbItems && j >= 0; 
					 j += (!_dir ? 1 : _dir))
				{
					item = GetTrackMediaItem(tr,j);
					firstItem = false;

					// search at item level 
					if (jobItem)
					{
						if (jobItem(item, g_searchStr))
						{
							if (!update) Undo_BeginBlock2(NULL);
							update = found = true;
							GetSetMediaItemInfo(item, "B_UISEL", &sel);
							if (_dir) breakSelection = true;
						}
					}
					// search at take level 
					else if (jobTake)
					{
						int nbTakes = GetMediaItemNumTakes(item);
						for (int k=0; item && k < nbTakes; k++)
						{
							MediaItem_Take* tk = GetMediaItemTake(item, k);
							if (tk && (_allTakes || (!_allTakes && tk == GetActiveTake(item))))
							{
								if (jobTake(tk, g_searchStr))
								{
									if (!update) Undo_BeginBlock2(NULL);
									update = found = true;
									GetSetMediaItemInfo(item, "B_UISEL", &sel);
									if (_dir) {
										breakSelection = true;
										break;
									}
								}
							}
						}
					}
				}
			}
		}
		UpdateNotFoundMsg(found);
		if (found && m_zoomSrollItems) {
			if (!_dir) ZoomToSelItems();
			else if (item) ScrollToSelItem(item);
		}

		PreventUIRefresh(-1);
	}

	if (update)
	{
		UpdateTimeline();
		Undo_EndBlock2(NULL, __LOCALIZE("Find: change media item selection","sws_undo"), UNDO_STATE_ALL);
	}
	return update;
}